github.com/kyma-project/kyma/components/asset-store-controller-manager@v0.0.0-20191203152857-3792b5df17c5/internal/handler/bucket/bucket.go (about) 1 package bucket 2 3 import ( 4 "context" 5 "fmt" 6 "github.com/pkg/errors" 7 "time" 8 9 "github.com/go-logr/logr" 10 "github.com/kyma-project/kyma/components/asset-store-controller-manager/internal/store" 11 "github.com/kyma-project/kyma/components/asset-store-controller-manager/pkg/apis/assetstore/v1alpha2" 12 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 "k8s.io/apimachinery/pkg/runtime" 14 "k8s.io/apimachinery/pkg/runtime/schema" 15 "k8s.io/client-go/tools/record" 16 ) 17 18 type Handler interface { 19 Do(ctx context.Context, now time.Time, instance MetaAccessor, spec v1alpha2.CommonBucketSpec, status v1alpha2.CommonBucketStatus) (*v1alpha2.CommonBucketStatus, error) 20 } 21 22 type MetaAccessor interface { 23 GetNamespace() string 24 GetName() string 25 GetGeneration() int64 26 GetDeletionTimestamp() *v1.Time 27 GetFinalizers() []string 28 SetFinalizers(finalizers []string) 29 GetObjectKind() schema.ObjectKind 30 DeepCopyObject() runtime.Object 31 } 32 33 var _ Handler = &bucketHandler{} 34 35 type bucketHandler struct { 36 recorder record.EventRecorder 37 store store.Store 38 externalEndpoint string 39 log logr.Logger 40 relistInterval time.Duration 41 } 42 43 func New(log logr.Logger, recorder record.EventRecorder, store store.Store, externalEndpoint string, relistInterval time.Duration) Handler { 44 return &bucketHandler{ 45 recorder: recorder, 46 store: store, 47 externalEndpoint: externalEndpoint, 48 log: log, 49 relistInterval: relistInterval, 50 } 51 } 52 53 func (h *bucketHandler) Do(ctx context.Context, now time.Time, instance MetaAccessor, spec v1alpha2.CommonBucketSpec, status v1alpha2.CommonBucketStatus) (*v1alpha2.CommonBucketStatus, error) { 54 h.logInfof("Start common Bucket handling") 55 defer h.logInfof("Finish common Bucket handling") 56 57 switch { 58 case h.isOnDelete(instance): 59 return h.onDelete(ctx, instance, status) 60 case h.isOnAddOrUpdate(instance, status): 61 return h.onAddOrUpdate(instance, spec, status) 62 case h.isOnReady(status, now): 63 return h.onReady(instance, spec, status) 64 case h.isOnFailed(status): 65 return h.onFailed(instance, spec, status) 66 default: 67 h.logInfof("Action not taken") 68 return nil, nil 69 } 70 } 71 72 func (h *bucketHandler) isOnReady(status v1alpha2.CommonBucketStatus, now time.Time) bool { 73 return status.Phase == v1alpha2.BucketReady && now.After(status.LastHeartbeatTime.Add(h.relistInterval)) 74 } 75 76 func (*bucketHandler) isOnAddOrUpdate(object MetaAccessor, status v1alpha2.CommonBucketStatus) bool { 77 return status.ObservedGeneration != object.GetGeneration() 78 } 79 80 func (*bucketHandler) isOnFailed(status v1alpha2.CommonBucketStatus) bool { 81 return status.Phase == v1alpha2.BucketFailed 82 } 83 84 func (*bucketHandler) isOnDelete(object MetaAccessor) bool { 85 return !object.GetDeletionTimestamp().IsZero() 86 } 87 88 func (h *bucketHandler) onFailed(object MetaAccessor, spec v1alpha2.CommonBucketSpec, status v1alpha2.CommonBucketStatus) (*v1alpha2.CommonBucketStatus, error) { 89 switch status.Reason { 90 case v1alpha2.BucketNotFound: 91 return h.onAddOrUpdate(object, spec, status) 92 case v1alpha2.BucketCreationFailure: 93 return h.onAddOrUpdate(object, spec, status) 94 case v1alpha2.BucketVerificationFailure: 95 return h.onReady(object, spec, status) 96 case v1alpha2.BucketPolicyUpdateFailed: 97 return h.onReady(object, spec, status) 98 } 99 100 return nil, nil 101 } 102 103 func (h *bucketHandler) onReady(object MetaAccessor, spec v1alpha2.CommonBucketSpec, status v1alpha2.CommonBucketStatus) (*v1alpha2.CommonBucketStatus, error) { 104 h.logInfof("Checking if bucket exists") 105 exists, err := h.store.BucketExists(status.RemoteName) 106 if err != nil { 107 h.recordWarningEventf(object, v1alpha2.BucketVerificationFailure, err.Error()) 108 return h.getStatus(object, status.RemoteName, status.URL, v1alpha2.BucketFailed, v1alpha2.BucketVerificationFailure, err.Error()), err 109 } 110 if !exists { 111 h.recordWarningEventf(object, v1alpha2.BucketNotFound, status.RemoteName) 112 return h.getStatus(object, "", "", v1alpha2.BucketFailed, v1alpha2.BucketNotFound, status.RemoteName), errors.Errorf(v1alpha2.BucketNotFound.String(), status.RemoteName) 113 } 114 h.logInfof("Bucket exists") 115 116 h.logInfof("Comparing bucket policy") 117 equal, err := h.store.CompareBucketPolicy(status.RemoteName, spec.Policy) 118 if err != nil { 119 h.recordWarningEventf(object, v1alpha2.BucketPolicyVerificationFailed, err.Error()) 120 return h.getStatus(object, status.RemoteName, status.URL, v1alpha2.BucketFailed, v1alpha2.BucketPolicyVerificationFailed, status.RemoteName), err 121 } 122 if equal { 123 h.logInfof("Bucket is up-to-date") 124 return h.getStatus(object, status.RemoteName, status.URL, v1alpha2.BucketReady, v1alpha2.BucketPolicyUpdated), nil 125 } 126 127 h.logInfof("Updating bucket policy") 128 h.recordWarningEventf(object, v1alpha2.BucketPolicyHasBeenChanged) 129 if err := h.store.SetBucketPolicy(status.RemoteName, spec.Policy); err != nil { 130 h.recordWarningEventf(object, v1alpha2.BucketPolicyUpdateFailed, err.Error()) 131 return h.getStatus(object, status.RemoteName, status.URL, v1alpha2.BucketFailed, v1alpha2.BucketPolicyUpdateFailed, err.Error()), err 132 } 133 h.recordNormalEventf(object, v1alpha2.BucketPolicyUpdated) 134 h.logInfof("Bucket policy updated") 135 136 return h.getStatus(object, status.RemoteName, status.URL, v1alpha2.BucketReady, v1alpha2.BucketPolicyUpdated), nil 137 } 138 139 func (h *bucketHandler) onAddOrUpdate(object MetaAccessor, spec v1alpha2.CommonBucketSpec, status v1alpha2.CommonBucketStatus) (*v1alpha2.CommonBucketStatus, error) { 140 h.logInfof("Checking if bucket was previously created") 141 if status.RemoteName != "" { 142 h.logInfof("Bucket was created") 143 return h.onReady(object, spec, status) 144 } 145 146 h.logInfof("Creating bucket") 147 remoteName, err := h.store.CreateBucket(object.GetNamespace(), object.GetName(), string(spec.Region)) 148 if err != nil { 149 h.recordWarningEventf(object, v1alpha2.BucketCreationFailure, err.Error()) 150 return h.getStatus(object, "", "", v1alpha2.BucketFailed, v1alpha2.BucketCreationFailure, err.Error()), err 151 } 152 h.recordNormalEventf(object, v1alpha2.BucketCreated) 153 h.logInfof("Bucket created") 154 155 externalUrl := h.getBucketUrl(remoteName) 156 157 h.logInfof("Updating bucket policy") 158 if err := h.store.SetBucketPolicy(remoteName, spec.Policy); err != nil { 159 h.recordWarningEventf(object, v1alpha2.BucketPolicyUpdateFailed, err.Error()) 160 return h.getStatus(object, remoteName, externalUrl, v1alpha2.BucketFailed, v1alpha2.BucketPolicyUpdateFailed, err.Error()), err 161 } 162 h.recordNormalEventf(object, v1alpha2.BucketPolicyUpdated) 163 h.logInfof("Bucket policy updated") 164 165 return h.getStatus(object, remoteName, externalUrl, v1alpha2.BucketReady, v1alpha2.BucketPolicyUpdated), nil 166 } 167 168 func (h *bucketHandler) onDelete(ctx context.Context, object MetaAccessor, status v1alpha2.CommonBucketStatus) (*v1alpha2.CommonBucketStatus, error) { 169 h.logInfof("Deleting Bucket") 170 if status.RemoteName == "" || status.Reason == v1alpha2.BucketNotFound { 171 h.logInfof("Nothing to delete, there is no remote bucket") 172 return nil, nil 173 } 174 175 if err := h.store.DeleteBucket(ctx, status.RemoteName); err != nil { 176 return nil, errors.Wrap(err, "while deleting remote bucket") 177 } 178 h.logInfof("Remote bucket %s deleted", status.RemoteName) 179 180 return nil, nil 181 } 182 183 func (h *bucketHandler) getBucketUrl(name string) string { 184 return fmt.Sprintf("%s/%s", h.externalEndpoint, name) 185 } 186 187 func (h *bucketHandler) recordNormalEventf(object MetaAccessor, reason v1alpha2.BucketReason, args ...interface{}) { 188 h.recordEventf(object, "Normal", reason, args...) 189 } 190 191 func (h *bucketHandler) recordWarningEventf(object MetaAccessor, reason v1alpha2.BucketReason, args ...interface{}) { 192 h.recordEventf(object, "Warning", reason, args...) 193 } 194 195 func (h *bucketHandler) logInfof(message string, args ...interface{}) { 196 h.log.Info(fmt.Sprintf(message, args...)) 197 } 198 199 func (h *bucketHandler) recordEventf(object MetaAccessor, eventType string, reason v1alpha2.BucketReason, args ...interface{}) { 200 h.recorder.Eventf(object, eventType, reason.String(), reason.Message(), args...) 201 } 202 203 func (*bucketHandler) getStatus(object MetaAccessor, remoteName, url string, phase v1alpha2.BucketPhase, reason v1alpha2.BucketReason, args ...interface{}) *v1alpha2.CommonBucketStatus { 204 return &v1alpha2.CommonBucketStatus{ 205 LastHeartbeatTime: v1.Now(), 206 ObservedGeneration: object.GetGeneration(), 207 Phase: phase, 208 RemoteName: remoteName, 209 URL: url, 210 Reason: reason, 211 Message: fmt.Sprintf(reason.Message(), args...), 212 } 213 }