github.com/docker/compose-on-kubernetes@v0.5.0/internal/registry/strategy.go (about) 1 package registry 2 3 import ( 4 "context" 5 6 iv "github.com/docker/compose-on-kubernetes/internal/internalversion" 7 "github.com/docker/compose-on-kubernetes/internal/requestaddons" 8 log "github.com/sirupsen/logrus" 9 apiequality "k8s.io/apimachinery/pkg/api/equality" 10 "k8s.io/apimachinery/pkg/runtime" 11 "k8s.io/apimachinery/pkg/util/validation/field" 12 "k8s.io/apiserver/pkg/storage/names" 13 appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1" 14 corev1 "k8s.io/client-go/kubernetes/typed/core/v1" 15 ) 16 17 // APIVersion describes an API level (has impact on canonicalization logic) 18 type APIVersion string 19 20 const ( 21 // APIV1beta1 represents v1beta1 API level 22 APIV1beta1 = APIVersion("v1beta1") 23 // APIV1beta2 represents v1beta2 API level or later 24 APIV1beta2 = APIVersion("v1beta2") 25 ) 26 27 type stackStrategy struct { 28 runtime.ObjectTyper 29 names.NameGenerator 30 coreClient corev1.CoreV1Interface 31 appsClient appsv1.AppsV1Interface 32 version APIVersion 33 } 34 35 func newStackStrategy(apiVersion APIVersion, typer runtime.ObjectTyper, coreClient corev1.CoreV1Interface, appsClient appsv1.AppsV1Interface) *stackStrategy { 36 return &stackStrategy{ObjectTyper: typer, NameGenerator: names.SimpleNameGenerator, coreClient: coreClient, appsClient: appsClient, version: apiVersion} 37 } 38 39 func (s *stackStrategy) NamespaceScoped() bool { 40 return true 41 } 42 43 type prepareStep func(ctx context.Context, oldStack *iv.Stack, newStack *iv.Stack) error 44 type validateStep func(ctx context.Context, stack *iv.Stack) field.ErrorList 45 46 func (s *stackStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { 47 stack, ok := obj.(*iv.Stack) 48 if !ok { 49 log.Error("Unable to cast object to Stack") 50 return 51 } 52 53 skipValidation := requestaddons.SkipValidationFrom(ctx) 54 55 steps := []prepareStep{ 56 prepareStackOwnership(), 57 prepareStackFromComposefile(skipValidation), 58 } 59 60 for _, step := range steps { 61 if err := step(ctx, nil, stack); err != nil { 62 stack.Status = &iv.StackStatus{ 63 Phase: iv.StackFailure, 64 Message: err.Error(), 65 } 66 log.Errorf("PrepareForCreate error (stack: %s/%s): %s", stack.Namespace, stack.Name, err) 67 return 68 } 69 } 70 stack.Status = &iv.StackStatus{ 71 Phase: iv.StackReconciliationPending, 72 Message: "Stack is waiting for reconciliation", 73 } 74 } 75 76 func (s *stackStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { 77 stack := obj.(*iv.Stack) 78 79 skipValidation := requestaddons.SkipValidationFrom(ctx) 80 if skipValidation { 81 return nil 82 } 83 84 steps := []validateStep{ 85 validateCreationStatus(), 86 validateStackNotNil(), 87 validateObjectNames(), 88 validateDryRun(), 89 validateCollisions(s.coreClient, s.appsClient), 90 } 91 92 for _, step := range steps { 93 if lst := step(ctx, stack); len(lst) > 0 { 94 log.Errorf("Validate error (stack: %s/%s): %s", stack.Namespace, stack.Name, lst.ToAggregate()) 95 return lst 96 } 97 } 98 return nil 99 } 100 101 func (s *stackStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { 102 newStack, ok := obj.(*iv.Stack) 103 if !ok { 104 log.Error("Unable to cast object to Stack") 105 return 106 } 107 108 oldStack, ok := old.(*iv.Stack) 109 if !ok { 110 log.Error("Unable to cast object to Stack") 111 return 112 } 113 skipValidation := requestaddons.SkipValidationFrom(ctx) 114 115 steps := []prepareStep{ 116 prepareStackOwnership(), 117 prepareFieldsForUpdate(s.version), 118 prepareStackFromComposefile(skipValidation), 119 } 120 121 for _, step := range steps { 122 if err := step(ctx, oldStack, newStack); err != nil { 123 newStack.Status = &iv.StackStatus{ 124 Phase: iv.StackFailure, 125 Message: err.Error(), 126 } 127 log.Errorf("PrepareForUpdate error (stack: %s/%s): %s", newStack.Namespace, newStack.Name, err) 128 return 129 } 130 } 131 if !apiequality.Semantic.DeepEqual(oldStack.Spec, newStack.Spec) { 132 log.Infof("stack %s/%s spec has changed, marking as waiting for reconciliation", newStack.Namespace, newStack.Name) 133 newStack.Status = &iv.StackStatus{ 134 Phase: iv.StackReconciliationPending, 135 Message: "Stack is waiting for reconciliation", 136 } 137 } 138 } 139 140 func (s *stackStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { 141 stack := obj.(*iv.Stack) 142 143 skipValidation := requestaddons.SkipValidationFrom(ctx) 144 if skipValidation { 145 return nil 146 } 147 148 steps := []validateStep{ 149 validateStackNotNil(), 150 validateObjectNames(), 151 validateDryRun(), 152 validateCollisions(s.coreClient, s.appsClient), 153 } 154 155 for _, step := range steps { 156 if lst := step(ctx, stack); len(lst) > 0 { 157 log.Errorf("ValidateUpdate error (stack: %s/%s): %s", stack.Namespace, stack.Name, lst.ToAggregate()) 158 return lst 159 } 160 } 161 return nil 162 } 163 164 func (s *stackStrategy) AllowCreateOnUpdate() bool { 165 return false 166 } 167 168 func (s *stackStrategy) AllowUnconditionalUpdate() bool { 169 return true 170 } 171 172 func (s *stackStrategy) Canonicalize(obj runtime.Object) { 173 }