github.com/AliyunContainerService/cli@v0.0.0-20181009023821-814ced4b30d0/cli/command/stack/kubernetes/stackclient.go (about) 1 package kubernetes 2 3 import ( 4 "fmt" 5 6 composev1beta1 "github.com/docker/cli/kubernetes/client/clientset/typed/compose/v1beta1" 7 composev1beta2 "github.com/docker/cli/kubernetes/client/clientset/typed/compose/v1beta2" 8 "github.com/docker/cli/kubernetes/labels" 9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 corev1 "k8s.io/client-go/kubernetes/typed/core/v1" 11 "k8s.io/client-go/rest" 12 ) 13 14 // StackClient talks to a kubernetes compose component. 15 type StackClient interface { 16 StackConverter 17 CreateOrUpdate(s Stack) error 18 Delete(name string) error 19 Get(name string) (Stack, error) 20 List(opts metav1.ListOptions) ([]Stack, error) 21 IsColliding(servicesClient corev1.ServiceInterface, s Stack) error 22 } 23 24 // stackV1Beta1 implements stackClient interface and talks to compose component v1beta1. 25 type stackV1Beta1 struct { 26 stackV1Beta1Converter 27 stacks composev1beta1.StackInterface 28 } 29 30 func newStackV1Beta1(config *rest.Config, namespace string) (*stackV1Beta1, error) { 31 client, err := composev1beta1.NewForConfig(config) 32 if err != nil { 33 return nil, err 34 } 35 return &stackV1Beta1{stacks: client.Stacks(namespace)}, nil 36 } 37 38 func (s *stackV1Beta1) CreateOrUpdate(internalStack Stack) error { 39 // If it already exists, update the stack 40 if stackBeta1, err := s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil { 41 stackBeta1.Spec.ComposeFile = internalStack.ComposeFile 42 _, err := s.stacks.Update(stackBeta1) 43 return err 44 } 45 // Or create it 46 _, err := s.stacks.Create(stackToV1beta1(internalStack)) 47 return err 48 } 49 50 func (s *stackV1Beta1) Delete(name string) error { 51 return s.stacks.Delete(name, &metav1.DeleteOptions{}) 52 } 53 54 func (s *stackV1Beta1) Get(name string) (Stack, error) { 55 stackBeta1, err := s.stacks.Get(name, metav1.GetOptions{}) 56 if err != nil { 57 return Stack{}, err 58 } 59 return stackFromV1beta1(stackBeta1) 60 } 61 62 func (s *stackV1Beta1) List(opts metav1.ListOptions) ([]Stack, error) { 63 list, err := s.stacks.List(opts) 64 if err != nil { 65 return nil, err 66 } 67 stacks := make([]Stack, len(list.Items)) 68 for i := range list.Items { 69 stack, err := stackFromV1beta1(&list.Items[i]) 70 if err != nil { 71 return nil, err 72 } 73 stacks[i] = stack 74 } 75 return stacks, nil 76 } 77 78 // IsColliding verifies that services defined in the stack collides with already deployed services 79 func (s *stackV1Beta1) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error { 80 for _, srv := range st.getServices() { 81 if err := verify(servicesClient, st.Name, srv); err != nil { 82 return err 83 } 84 } 85 return nil 86 } 87 88 // verify checks whether the service is already present in kubernetes. 89 // If we find the service by name but it doesn't have our label or it has a different value 90 // than the stack name for the label, we fail (i.e. it will collide) 91 func verify(services corev1.ServiceInterface, stackName string, service string) error { 92 svc, err := services.Get(service, metav1.GetOptions{}) 93 if err == nil { 94 if key, ok := svc.ObjectMeta.Labels[labels.ForStackName]; ok { 95 if key != stackName { 96 return fmt.Errorf("service %s already present in stack named %s", service, key) 97 } 98 return nil 99 } 100 return fmt.Errorf("service %s already present in the cluster", service) 101 } 102 return nil 103 } 104 105 // stackV1Beta2 implements stackClient interface and talks to compose component v1beta2. 106 type stackV1Beta2 struct { 107 stackV1Beta2Converter 108 stacks composev1beta2.StackInterface 109 } 110 111 func newStackV1Beta2(config *rest.Config, namespace string) (*stackV1Beta2, error) { 112 client, err := composev1beta2.NewForConfig(config) 113 if err != nil { 114 return nil, err 115 } 116 return &stackV1Beta2{stacks: client.Stacks(namespace)}, nil 117 } 118 119 func (s *stackV1Beta2) CreateOrUpdate(internalStack Stack) error { 120 // If it already exists, update the stack 121 if stackBeta2, err := s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil { 122 stackBeta2.Spec = internalStack.Spec 123 _, err := s.stacks.Update(stackBeta2) 124 return err 125 } 126 // Or create it 127 _, err := s.stacks.Create(stackToV1beta2(internalStack)) 128 return err 129 } 130 131 func (s *stackV1Beta2) Delete(name string) error { 132 return s.stacks.Delete(name, &metav1.DeleteOptions{}) 133 } 134 135 func (s *stackV1Beta2) Get(name string) (Stack, error) { 136 stackBeta2, err := s.stacks.Get(name, metav1.GetOptions{}) 137 if err != nil { 138 return Stack{}, err 139 } 140 return stackFromV1beta2(stackBeta2), nil 141 } 142 143 func (s *stackV1Beta2) List(opts metav1.ListOptions) ([]Stack, error) { 144 list, err := s.stacks.List(opts) 145 if err != nil { 146 return nil, err 147 } 148 stacks := make([]Stack, len(list.Items)) 149 for i := range list.Items { 150 stacks[i] = stackFromV1beta2(&list.Items[i]) 151 } 152 return stacks, nil 153 } 154 155 // IsColliding is handle server side with the compose api v1beta2, so nothing to do here 156 func (s *stackV1Beta2) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error { 157 return nil 158 }