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 }