2018-01-31 09:37:14 -05:00
|
|
|
package kubernetes
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2019-01-16 06:56:37 -05:00
|
|
|
composev1alpha3 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3"
|
2018-12-20 02:45:35 -05:00
|
|
|
composev1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1"
|
|
|
|
composev1beta2 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2"
|
2019-01-16 06:56:37 -05:00
|
|
|
"github.com/docker/compose-on-kubernetes/api/compose/v1alpha3"
|
2019-01-11 11:23:44 -05:00
|
|
|
"github.com/docker/compose-on-kubernetes/api/compose/v1beta1"
|
|
|
|
"github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
|
2018-12-20 02:45:35 -05:00
|
|
|
"github.com/docker/compose-on-kubernetes/api/labels"
|
2018-01-31 09:37:14 -05:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
|
|
|
"k8s.io/client-go/rest"
|
|
|
|
)
|
|
|
|
|
2018-04-09 09:13:16 -04:00
|
|
|
// StackClient talks to a kubernetes compose component.
|
|
|
|
type StackClient interface {
|
2018-06-27 10:41:00 -04:00
|
|
|
StackConverter
|
2019-01-11 11:23:44 -05:00
|
|
|
CreateOrUpdate(s Stack, childResources []childResource) error
|
2018-01-31 09:37:14 -05:00
|
|
|
Delete(name string) error
|
2018-06-27 10:41:00 -04:00
|
|
|
Get(name string) (Stack, error)
|
|
|
|
List(opts metav1.ListOptions) ([]Stack, error)
|
|
|
|
IsColliding(servicesClient corev1.ServiceInterface, s Stack) error
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// stackV1Beta1 implements stackClient interface and talks to compose component v1beta1.
|
|
|
|
type stackV1Beta1 struct {
|
2018-06-27 10:41:00 -04:00
|
|
|
stackV1Beta1Converter
|
2018-01-31 09:37:14 -05:00
|
|
|
stacks composev1beta1.StackInterface
|
|
|
|
}
|
|
|
|
|
2018-05-14 09:44:55 -04:00
|
|
|
func newStackV1Beta1(config *rest.Config, namespace string) (*stackV1Beta1, error) {
|
2018-01-31 09:37:14 -05:00
|
|
|
client, err := composev1beta1.NewForConfig(config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &stackV1Beta1{stacks: client.Stacks(namespace)}, nil
|
|
|
|
}
|
|
|
|
|
2019-01-11 11:23:44 -05:00
|
|
|
func (s *stackV1Beta1) CreateOrUpdate(internalStack Stack, childResources []childResource) error {
|
2018-01-31 09:37:14 -05:00
|
|
|
// If it already exists, update the stack
|
2019-01-11 11:23:44 -05:00
|
|
|
var (
|
|
|
|
stack *v1beta1.Stack
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil {
|
|
|
|
stack.Spec.ComposeFile = internalStack.ComposeFile
|
|
|
|
stack, err = s.stacks.Update(stack)
|
|
|
|
} else {
|
|
|
|
// Or create it
|
|
|
|
stack, err = s.stacks.Create(stackToV1beta1(internalStack))
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
deleteChildResources(childResources)
|
2018-01-31 09:37:14 -05:00
|
|
|
return err
|
|
|
|
}
|
2019-01-11 11:23:44 -05:00
|
|
|
blockOwnerDeletion := true
|
|
|
|
isController := true
|
|
|
|
return setChildResourcesOwner(childResources, metav1.OwnerReference{
|
|
|
|
APIVersion: v1beta1.SchemeGroupVersion.String(),
|
|
|
|
Kind: "Stack",
|
|
|
|
Name: stack.Name,
|
|
|
|
UID: stack.UID,
|
|
|
|
BlockOwnerDeletion: &blockOwnerDeletion,
|
|
|
|
Controller: &isController,
|
|
|
|
})
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stackV1Beta1) Delete(name string) error {
|
|
|
|
return s.stacks.Delete(name, &metav1.DeleteOptions{})
|
|
|
|
}
|
|
|
|
|
2018-06-27 10:41:00 -04:00
|
|
|
func (s *stackV1Beta1) Get(name string) (Stack, error) {
|
2018-01-31 09:37:14 -05:00
|
|
|
stackBeta1, err := s.stacks.Get(name, metav1.GetOptions{})
|
|
|
|
if err != nil {
|
2018-06-27 10:41:00 -04:00
|
|
|
return Stack{}, err
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
|
|
|
return stackFromV1beta1(stackBeta1)
|
|
|
|
}
|
|
|
|
|
2018-06-27 10:41:00 -04:00
|
|
|
func (s *stackV1Beta1) List(opts metav1.ListOptions) ([]Stack, error) {
|
2018-01-31 09:37:14 -05:00
|
|
|
list, err := s.stacks.List(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-06-27 10:41:00 -04:00
|
|
|
stacks := make([]Stack, len(list.Items))
|
2018-01-31 09:37:14 -05:00
|
|
|
for i := range list.Items {
|
|
|
|
stack, err := stackFromV1beta1(&list.Items[i])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
stacks[i] = stack
|
|
|
|
}
|
|
|
|
return stacks, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsColliding verifies that services defined in the stack collides with already deployed services
|
2018-06-27 10:41:00 -04:00
|
|
|
func (s *stackV1Beta1) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error {
|
2018-01-31 09:37:14 -05:00
|
|
|
for _, srv := range st.getServices() {
|
2018-06-27 10:41:00 -04:00
|
|
|
if err := verify(servicesClient, st.Name, srv); err != nil {
|
2018-01-31 09:37:14 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-09-07 05:18:00 -04:00
|
|
|
// verify checks whether the service is already present in kubernetes.
|
2018-01-31 09:37:14 -05:00
|
|
|
// If we find the service by name but it doesn't have our label or it has a different value
|
|
|
|
// than the stack name for the label, we fail (i.e. it will collide)
|
|
|
|
func verify(services corev1.ServiceInterface, stackName string, service string) error {
|
|
|
|
svc, err := services.Get(service, metav1.GetOptions{})
|
|
|
|
if err == nil {
|
|
|
|
if key, ok := svc.ObjectMeta.Labels[labels.ForStackName]; ok {
|
|
|
|
if key != stackName {
|
|
|
|
return fmt.Errorf("service %s already present in stack named %s", service, key)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return fmt.Errorf("service %s already present in the cluster", service)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// stackV1Beta2 implements stackClient interface and talks to compose component v1beta2.
|
|
|
|
type stackV1Beta2 struct {
|
2019-01-28 10:54:38 -05:00
|
|
|
stackV1Beta2Converter
|
2018-01-31 09:37:14 -05:00
|
|
|
stacks composev1beta2.StackInterface
|
|
|
|
}
|
|
|
|
|
2018-05-14 09:44:55 -04:00
|
|
|
func newStackV1Beta2(config *rest.Config, namespace string) (*stackV1Beta2, error) {
|
2018-01-31 09:37:14 -05:00
|
|
|
client, err := composev1beta2.NewForConfig(config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &stackV1Beta2{stacks: client.Stacks(namespace)}, nil
|
|
|
|
}
|
|
|
|
|
2019-01-11 11:23:44 -05:00
|
|
|
func (s *stackV1Beta2) CreateOrUpdate(internalStack Stack, childResources []childResource) error {
|
|
|
|
var (
|
|
|
|
stack *v1beta2.Stack
|
|
|
|
err error
|
|
|
|
)
|
2019-01-16 06:56:37 -05:00
|
|
|
resolved, err := stackToV1beta2(internalStack)
|
|
|
|
if err != nil {
|
|
|
|
deleteChildResources(childResources)
|
|
|
|
return err
|
|
|
|
}
|
2019-01-11 11:23:44 -05:00
|
|
|
if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil {
|
2019-01-16 06:56:37 -05:00
|
|
|
stack.Spec = resolved.Spec
|
2019-01-11 11:23:44 -05:00
|
|
|
stack, err = s.stacks.Update(stack)
|
|
|
|
} else {
|
|
|
|
// Or create it
|
2019-01-16 06:56:37 -05:00
|
|
|
stack, err = s.stacks.Create(resolved)
|
2019-01-11 11:23:44 -05:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
deleteChildResources(childResources)
|
2018-01-31 09:37:14 -05:00
|
|
|
return err
|
|
|
|
}
|
2019-01-11 11:23:44 -05:00
|
|
|
blockOwnerDeletion := true
|
|
|
|
isController := true
|
|
|
|
return setChildResourcesOwner(childResources, metav1.OwnerReference{
|
|
|
|
APIVersion: v1beta2.SchemeGroupVersion.String(),
|
|
|
|
Kind: "Stack",
|
|
|
|
Name: stack.Name,
|
|
|
|
UID: stack.UID,
|
|
|
|
BlockOwnerDeletion: &blockOwnerDeletion,
|
|
|
|
Controller: &isController,
|
|
|
|
})
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stackV1Beta2) Delete(name string) error {
|
|
|
|
return s.stacks.Delete(name, &metav1.DeleteOptions{})
|
|
|
|
}
|
|
|
|
|
2018-06-27 10:41:00 -04:00
|
|
|
func (s *stackV1Beta2) Get(name string) (Stack, error) {
|
2018-01-31 09:37:14 -05:00
|
|
|
stackBeta2, err := s.stacks.Get(name, metav1.GetOptions{})
|
|
|
|
if err != nil {
|
2018-06-27 10:41:00 -04:00
|
|
|
return Stack{}, err
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
2019-01-16 06:56:37 -05:00
|
|
|
return stackFromV1beta2(stackBeta2)
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
|
|
|
|
2018-06-27 10:41:00 -04:00
|
|
|
func (s *stackV1Beta2) List(opts metav1.ListOptions) ([]Stack, error) {
|
2018-01-31 09:37:14 -05:00
|
|
|
list, err := s.stacks.List(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-06-27 10:41:00 -04:00
|
|
|
stacks := make([]Stack, len(list.Items))
|
2018-01-31 09:37:14 -05:00
|
|
|
for i := range list.Items {
|
2019-01-16 06:56:37 -05:00
|
|
|
if stacks[i], err = stackFromV1beta2(&list.Items[i]); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
|
|
|
return stacks, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsColliding is handle server side with the compose api v1beta2, so nothing to do here
|
2018-06-27 10:41:00 -04:00
|
|
|
func (s *stackV1Beta2) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error {
|
2018-01-31 09:37:14 -05:00
|
|
|
return nil
|
|
|
|
}
|
2019-01-16 06:56:37 -05:00
|
|
|
|
|
|
|
// stackV1Beta2 implements stackClient interface and talks to compose component v1beta2.
|
|
|
|
type stackV1Alpha3 struct {
|
2019-01-28 10:54:38 -05:00
|
|
|
stackV1Alpha3Converter
|
2019-01-16 06:56:37 -05:00
|
|
|
stacks composev1alpha3.StackInterface
|
|
|
|
}
|
|
|
|
|
|
|
|
func newStackV1Alpha3(config *rest.Config, namespace string) (*stackV1Alpha3, error) {
|
|
|
|
client, err := composev1alpha3.NewForConfig(config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &stackV1Alpha3{stacks: client.Stacks(namespace)}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stackV1Alpha3) CreateOrUpdate(internalStack Stack, childResources []childResource) error {
|
|
|
|
var (
|
|
|
|
stack *v1alpha3.Stack
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
resolved := stackToV1alpha3(internalStack)
|
|
|
|
if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil {
|
|
|
|
stack.Spec = resolved.Spec
|
|
|
|
stack, err = s.stacks.Update(stack)
|
|
|
|
} else {
|
|
|
|
// Or create it
|
|
|
|
stack, err = s.stacks.Create(resolved)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
deleteChildResources(childResources)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
blockOwnerDeletion := true
|
|
|
|
isController := true
|
|
|
|
return setChildResourcesOwner(childResources, metav1.OwnerReference{
|
|
|
|
APIVersion: v1alpha3.SchemeGroupVersion.String(),
|
|
|
|
Kind: "Stack",
|
|
|
|
Name: stack.Name,
|
|
|
|
UID: stack.UID,
|
|
|
|
BlockOwnerDeletion: &blockOwnerDeletion,
|
|
|
|
Controller: &isController,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stackV1Alpha3) Delete(name string) error {
|
|
|
|
return s.stacks.Delete(name, &metav1.DeleteOptions{})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stackV1Alpha3) Get(name string) (Stack, error) {
|
|
|
|
stackAlpha3, err := s.stacks.Get(name, metav1.GetOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return Stack{}, err
|
|
|
|
}
|
|
|
|
return stackFromV1alpha3(stackAlpha3), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stackV1Alpha3) List(opts metav1.ListOptions) ([]Stack, error) {
|
|
|
|
list, err := s.stacks.List(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
stacks := make([]Stack, len(list.Items))
|
|
|
|
for i := range list.Items {
|
|
|
|
stacks[i] = stackFromV1alpha3(&list.Items[i])
|
|
|
|
}
|
|
|
|
return stacks, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsColliding is handle server side with the compose api v1beta2, so nothing to do here
|
|
|
|
func (s *stackV1Alpha3) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error {
|
|
|
|
return nil
|
|
|
|
}
|