github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/command/stack/kubernetes/stackclient.go (about)

     1  package kubernetes
     2  
     3  import (
     4  	"fmt"
     5  
     6  	composev1alpha3 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3"
     7  	composev1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1"
     8  	composev1beta2 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2"
     9  	"github.com/docker/compose-on-kubernetes/api/compose/v1alpha3"
    10  	"github.com/docker/compose-on-kubernetes/api/compose/v1beta1"
    11  	"github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
    12  	"github.com/docker/compose-on-kubernetes/api/labels"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
    15  	"k8s.io/client-go/rest"
    16  )
    17  
    18  // StackClient talks to a kubernetes compose component.
    19  type StackClient interface {
    20  	StackConverter
    21  	CreateOrUpdate(s Stack, childResources []childResource) error
    22  	Delete(name string) error
    23  	Get(name string) (Stack, error)
    24  	List(opts metav1.ListOptions) ([]Stack, error)
    25  	IsColliding(servicesClient corev1.ServiceInterface, s Stack) error
    26  }
    27  
    28  // stackV1Beta1 implements stackClient interface and talks to compose component v1beta1.
    29  type stackV1Beta1 struct {
    30  	stackV1Beta1Converter
    31  	stacks composev1beta1.StackInterface
    32  }
    33  
    34  func newStackV1Beta1(config *rest.Config, namespace string) (*stackV1Beta1, error) {
    35  	client, err := composev1beta1.NewForConfig(config)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	return &stackV1Beta1{stacks: client.Stacks(namespace)}, nil
    40  }
    41  
    42  func (s *stackV1Beta1) CreateOrUpdate(internalStack Stack, childResources []childResource) error {
    43  	// If it already exists, update the stack
    44  	var (
    45  		stack *v1beta1.Stack
    46  		err   error
    47  	)
    48  	if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil {
    49  		stack.Spec.ComposeFile = internalStack.ComposeFile
    50  		stack, err = s.stacks.Update(stack)
    51  	} else {
    52  		// Or create it
    53  		stack, err = s.stacks.Create(stackToV1beta1(internalStack))
    54  	}
    55  	if err != nil {
    56  		deleteChildResources(childResources)
    57  		return err
    58  	}
    59  	blockOwnerDeletion := true
    60  	isController := true
    61  	return setChildResourcesOwner(childResources, metav1.OwnerReference{
    62  		APIVersion:         v1beta1.SchemeGroupVersion.String(),
    63  		Kind:               "Stack",
    64  		Name:               stack.Name,
    65  		UID:                stack.UID,
    66  		BlockOwnerDeletion: &blockOwnerDeletion,
    67  		Controller:         &isController,
    68  	})
    69  }
    70  
    71  func (s *stackV1Beta1) Delete(name string) error {
    72  	return s.stacks.Delete(name, &metav1.DeleteOptions{})
    73  }
    74  
    75  func (s *stackV1Beta1) Get(name string) (Stack, error) {
    76  	stackBeta1, err := s.stacks.Get(name, metav1.GetOptions{})
    77  	if err != nil {
    78  		return Stack{}, err
    79  	}
    80  	return stackFromV1beta1(stackBeta1)
    81  }
    82  
    83  func (s *stackV1Beta1) List(opts metav1.ListOptions) ([]Stack, error) {
    84  	list, err := s.stacks.List(opts)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	stacks := make([]Stack, len(list.Items))
    89  	for i := range list.Items {
    90  		stack, err := stackFromV1beta1(&list.Items[i])
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  		stacks[i] = stack
    95  	}
    96  	return stacks, nil
    97  }
    98  
    99  // IsColliding verifies that services defined in the stack collides with already deployed services
   100  func (s *stackV1Beta1) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error {
   101  	for _, srv := range st.getServices() {
   102  		if err := verify(servicesClient, st.Name, srv); err != nil {
   103  			return err
   104  		}
   105  	}
   106  	return nil
   107  }
   108  
   109  // verify checks whether the service is already present in kubernetes.
   110  // If we find the service by name but it doesn't have our label or it has a different value
   111  // than the stack name for the label, we fail (i.e. it will collide)
   112  func verify(services corev1.ServiceInterface, stackName string, service string) error {
   113  	svc, err := services.Get(service, metav1.GetOptions{})
   114  	if err == nil {
   115  		if key, ok := svc.ObjectMeta.Labels[labels.ForStackName]; ok {
   116  			if key != stackName {
   117  				return fmt.Errorf("service %s already present in stack named %s", service, key)
   118  			}
   119  			return nil
   120  		}
   121  		return fmt.Errorf("service %s already present in the cluster", service)
   122  	}
   123  	return nil
   124  }
   125  
   126  // stackV1Beta2 implements stackClient interface and talks to compose component v1beta2.
   127  type stackV1Beta2 struct {
   128  	stackV1Beta2Converter
   129  	stacks composev1beta2.StackInterface
   130  }
   131  
   132  func newStackV1Beta2(config *rest.Config, namespace string) (*stackV1Beta2, error) {
   133  	client, err := composev1beta2.NewForConfig(config)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	return &stackV1Beta2{stacks: client.Stacks(namespace)}, nil
   138  }
   139  
   140  func (s *stackV1Beta2) CreateOrUpdate(internalStack Stack, childResources []childResource) error {
   141  	var (
   142  		stack *v1beta2.Stack
   143  		err   error
   144  	)
   145  	resolved, err := stackToV1beta2(internalStack)
   146  	if err != nil {
   147  		deleteChildResources(childResources)
   148  		return err
   149  	}
   150  	if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil {
   151  		stack.Spec = resolved.Spec
   152  		stack, err = s.stacks.Update(stack)
   153  	} else {
   154  		// Or create it
   155  		stack, err = s.stacks.Create(resolved)
   156  	}
   157  	if err != nil {
   158  		deleteChildResources(childResources)
   159  		return err
   160  	}
   161  	blockOwnerDeletion := true
   162  	isController := true
   163  	return setChildResourcesOwner(childResources, metav1.OwnerReference{
   164  		APIVersion:         v1beta2.SchemeGroupVersion.String(),
   165  		Kind:               "Stack",
   166  		Name:               stack.Name,
   167  		UID:                stack.UID,
   168  		BlockOwnerDeletion: &blockOwnerDeletion,
   169  		Controller:         &isController,
   170  	})
   171  }
   172  
   173  func (s *stackV1Beta2) Delete(name string) error {
   174  	return s.stacks.Delete(name, &metav1.DeleteOptions{})
   175  }
   176  
   177  func (s *stackV1Beta2) Get(name string) (Stack, error) {
   178  	stackBeta2, err := s.stacks.Get(name, metav1.GetOptions{})
   179  	if err != nil {
   180  		return Stack{}, err
   181  	}
   182  	return stackFromV1beta2(stackBeta2)
   183  }
   184  
   185  func (s *stackV1Beta2) List(opts metav1.ListOptions) ([]Stack, error) {
   186  	list, err := s.stacks.List(opts)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  	stacks := make([]Stack, len(list.Items))
   191  	for i := range list.Items {
   192  		if stacks[i], err = stackFromV1beta2(&list.Items[i]); err != nil {
   193  			return nil, err
   194  		}
   195  	}
   196  	return stacks, nil
   197  }
   198  
   199  // IsColliding is handle server side with the compose api v1beta2, so nothing to do here
   200  func (s *stackV1Beta2) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error {
   201  	return nil
   202  }
   203  
   204  // stackV1Beta2 implements stackClient interface and talks to compose component v1beta2.
   205  type stackV1Alpha3 struct {
   206  	stackV1Alpha3Converter
   207  	stacks composev1alpha3.StackInterface
   208  }
   209  
   210  func newStackV1Alpha3(config *rest.Config, namespace string) (*stackV1Alpha3, error) {
   211  	client, err := composev1alpha3.NewForConfig(config)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	return &stackV1Alpha3{stacks: client.Stacks(namespace)}, nil
   216  }
   217  
   218  func (s *stackV1Alpha3) CreateOrUpdate(internalStack Stack, childResources []childResource) error {
   219  	var (
   220  		stack *v1alpha3.Stack
   221  		err   error
   222  	)
   223  	resolved := stackToV1alpha3(internalStack)
   224  	if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil {
   225  		stack.Spec = resolved.Spec
   226  		stack, err = s.stacks.Update(stack)
   227  	} else {
   228  		// Or create it
   229  		stack, err = s.stacks.Create(resolved)
   230  	}
   231  	if err != nil {
   232  		deleteChildResources(childResources)
   233  		return err
   234  	}
   235  	blockOwnerDeletion := true
   236  	isController := true
   237  	return setChildResourcesOwner(childResources, metav1.OwnerReference{
   238  		APIVersion:         v1alpha3.SchemeGroupVersion.String(),
   239  		Kind:               "Stack",
   240  		Name:               stack.Name,
   241  		UID:                stack.UID,
   242  		BlockOwnerDeletion: &blockOwnerDeletion,
   243  		Controller:         &isController,
   244  	})
   245  }
   246  
   247  func (s *stackV1Alpha3) Delete(name string) error {
   248  	return s.stacks.Delete(name, &metav1.DeleteOptions{})
   249  }
   250  
   251  func (s *stackV1Alpha3) Get(name string) (Stack, error) {
   252  	stackAlpha3, err := s.stacks.Get(name, metav1.GetOptions{})
   253  	if err != nil {
   254  		return Stack{}, err
   255  	}
   256  	return stackFromV1alpha3(stackAlpha3), nil
   257  }
   258  
   259  func (s *stackV1Alpha3) List(opts metav1.ListOptions) ([]Stack, error) {
   260  	list, err := s.stacks.List(opts)
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  	stacks := make([]Stack, len(list.Items))
   265  	for i := range list.Items {
   266  		stacks[i] = stackFromV1alpha3(&list.Items[i])
   267  	}
   268  	return stacks, nil
   269  }
   270  
   271  // IsColliding is handle server side with the compose api v1beta2, so nothing to do here
   272  func (s *stackV1Alpha3) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error {
   273  	return nil
   274  }