diff --git a/Dockerfile b/Dockerfile index aa74435..6de4d83 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,16 +12,16 @@ COPY go.sum go.sum RUN go mod download # Copy the go source -COPY cmd/main.go cmd/main.go +COPY main.go main.go COPY api/ api/ -COPY internal/controller/ internal/controller/ +COPY controllers/ controllers/ # Build # the GOARCH has not a default value to allow the binary be built according to the host where the command # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. -RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details diff --git a/Makefile b/Makefile index 582c65c..0808b32 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,10 @@ ifeq ($(USE_IMAGE_DIGESTS), true) BUNDLE_GEN_FLAGS += --use-image-digests endif +# Set the Operator SDK version to use. By default, what is installed on the system is used. +# This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit. +OPERATOR_SDK_VERSION ?= v1.32.0 + # Image URL to use all building/pushing image targets IMG ?= controller:latest # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. @@ -58,12 +62,6 @@ else GOBIN=$(shell go env GOBIN) endif -# CONTAINER_TOOL defines the container tool to be used for building images. -# Be aware that the target commands are only tested with Docker which is -# scaffolded by default. However, you might want to replace it to use other -# tools. (i.e. podman) -CONTAINER_TOOL ?= docker - # Setting SHELL to bash allows bash commands to be executed by recipes. # Options are set to exit when a recipe line exits non-zero or a piped command fails. SHELL = /usr/bin/env bash -o pipefail @@ -115,38 +113,38 @@ test: manifests generate fmt vet envtest ## Run tests. .PHONY: build build: manifests generate fmt vet ## Build manager binary. - go build -o bin/manager cmd/main.go + go build -o bin/manager main.go .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. - go run ./cmd/main.go + go run ./main.go # If you wish built the manager image targeting other platforms you can use the --platform flag. # (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it. # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ .PHONY: docker-build docker-build: test ## Build docker image with the manager. - $(CONTAINER_TOOL) build -t ${IMG} . + docker build -t ${IMG} . .PHONY: docker-push docker-push: ## Push docker image with the manager. - $(CONTAINER_TOOL) push ${IMG} + docker push ${IMG} # PLATFORMS defines the target platforms for the manager image be build to provide support to multiple # architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: # - able to use docker buildx . More info: https://docs.docker.com/build/buildx/ # - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/ -# - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=> than the export will fail) +# - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=> then the export will fail) # To properly provided solutions that supports more than one platform you should use this option. PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le .PHONY: docker-buildx docker-buildx: test ## Build and push docker image for the manager for cross-platform support # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross - - $(CONTAINER_TOOL) buildx create --name project-v3-builder - $(CONTAINER_TOOL) buildx use project-v3-builder - - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . - - $(CONTAINER_TOOL) buildx rm project-v3-builder + - docker buildx create --name project-v3-builder + docker buildx use project-v3-builder + - docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - docker buildx rm project-v3-builder rm Dockerfile.cross ##@ Deployment @@ -157,20 +155,20 @@ endif .PHONY: install install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f - + $(KUSTOMIZE) build config/crd | kubectl apply -f - .PHONY: uninstall uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - $(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + $(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f - .PHONY: deploy deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - + $(KUSTOMIZE) build config/default | kubectl apply -f - .PHONY: undeploy undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - $(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - ##@ Build Dependencies @@ -180,37 +178,58 @@ $(LOCALBIN): mkdir -p $(LOCALBIN) ## Tool Binaries -KUBECTL ?= kubectl KUSTOMIZE ?= $(LOCALBIN)/kustomize CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions -KUSTOMIZE_VERSION ?= v5.0.1 -CONTROLLER_TOOLS_VERSION ?= v0.12.0 +KUSTOMIZE_VERSION ?= v3.8.7 +CONTROLLER_TOOLS_VERSION ?= v0.11.1 -#KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" +KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize -kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. $(KUSTOMIZE): $(LOCALBIN) - test -s $(LOCALBIN)/kustomize || GOBIN=$(LOCALBIN) GO111MODULE=on go install sigs.k8s.io/kustomize/kustomize/v5@$(KUSTOMIZE_VERSION) + @if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \ + echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \ + rm -rf $(LOCALBIN)/kustomize; \ + fi + test -s $(LOCALBIN)/kustomize || { curl -Ss $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN); } .PHONY: controller-gen -controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten. $(CONTROLLER_GEN): $(LOCALBIN) - test -s $(LOCALBIN)/controller-gen || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) + test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \ + GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) .PHONY: envtest envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. $(ENVTEST): $(LOCALBIN) test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest +.PHONY: operator-sdk +OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk +operator-sdk: ## Download operator-sdk locally if necessary. +ifeq (,$(wildcard $(OPERATOR_SDK))) +ifeq (, $(shell which operator-sdk 2>/dev/null)) + @{ \ + set -e ;\ + mkdir -p $(dir $(OPERATOR_SDK)) ;\ + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ + curl -sSLo $(OPERATOR_SDK) https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$${OS}_$${ARCH} ;\ + chmod +x $(OPERATOR_SDK) ;\ + } +else +OPERATOR_SDK = $(shell which operator-sdk) +endif +endif + .PHONY: bundle -bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files. - operator-sdk generate kustomize manifests -q +bundle: manifests kustomize operator-sdk ## Generate bundle manifests and metadata, then validate generated files. + $(OPERATOR_SDK) generate kustomize manifests -q cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) - $(KUSTOMIZE) build config/manifests | operator-sdk generate bundle $(BUNDLE_GEN_FLAGS) - operator-sdk bundle validate ./bundle + $(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle $(BUNDLE_GEN_FLAGS) + $(OPERATOR_SDK) bundle validate ./bundle .PHONY: bundle-build bundle-build: ## Build the bundle image. diff --git a/PROJECT b/PROJECT index 9e67313..8e210a6 100644 --- a/PROJECT +++ b/PROJECT @@ -1,28 +1,41 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html domain: onyxia.sh layout: -- go.kubebuilder.io/v4 +- go.kubebuilder.io/v3 plugins: manifests.sdk.operatorframework.io/v2: {} scorecard.sdk.operatorframework.io/v2: {} projectName: s3-operator -repo: github.com/inseefrlab/s3-operator +repo: github.com/phlg/s3-operator-downgrade resources: - api: crdVersion: v1 namespaced: true controller: true domain: onyxia.sh - group: s3 + group: s3.onyxia.sh kind: Bucket - path: github.com/inseefrlab/s3-operator/api/v1alpha1 + path: github.com/phlg/s3-operator-downgrade/api/v1alpha1 version: v1alpha1 - api: crdVersion: v1 namespaced: true controller: true domain: onyxia.sh - group: s3 + group: s3.onyxia.sh kind: Policy - path: github.com/inseefrlab/s3-operator/api/v1alpha1 + path: github.com/phlg/s3-operator-downgrade/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: onyxia.sh + group: s3.onyxia.sh + kind: Path + path: github.com/phlg/s3-operator-downgrade/api/v1alpha1 version: v1alpha1 version: "3" diff --git a/README.md b/README.md index ce24661..af11b18 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ This Operator SDK based tool aims at managing S3 related resources (buckets, pol This operator has been successfully tested with : -- Kubernetes : 1.25, 1.26 -- MinIO : 2023-05-27T05:56:19Z +- Kubernetes : 1.25, 1.26, 1.27 +- MinIO : 2023-05-27T05:56:19Z (up to v0.3.0 included), 2023-11-20T22-40-07Z (from v0.4.0 onwards) ## Description @@ -20,17 +20,19 @@ At its heart, the operator revolves around CRDs that match S3 resources : - `buckets.s3.onyxia.sh` - `policies.s3.onyxia.sh` +- `paths.s3.onyxia.sh` The custom resources based on these CRDs are a somewhat simplified projection of the real S3 resources. From the operator's point of view : - A `Bucket` CR matches a S3 bucket, and only has a name, a quota (actually two, [see Bucket example in *Usage* section below](#bucket)), and optionally, a set of paths - A `Policy` CR matches a "canned" policy (not a bucket policy, but a global one, that can be attached to a user), and has a name, and its actual content (IAM JSON) +- A `Path` CR matches a set of paths inside of a policy. This is akin to the `paths` property of the `Bucket` CRD, except `Path` is not responsible for Bucket creation. Each custom resource based on these CRDs on Kubernetes is to be matched with a resource on the S3 instance. If the CR and the corresponding S3 resource diverge, the operator will create or update the S3 resource to bring it back to . Two important caveats : -- It is one-way - if something happens on the S3 side directly (instead of going through the CRs), the operator ha s no way of reacting. At best, the next trigger will overwrite the S3 state with the declared state in the k8s custom resource. +- It is one-way - if something happens on the S3 side directly (instead of going through the CRs), the operator has no way of reacting. At best, the next trigger will overwrite the S3 state with the declared state in the k8s custom resource. - For now, the operator won't delete any resource on S3 - if a CR is removed, its matching resource on S3 will still be present. This behavior was primarily picked to avoid data loss for bucket, but also applied to policies. ## Installation @@ -48,7 +50,7 @@ helm install s3-operator --values # see below for the pa ### Running from source -Alternatively, if you just wish to try out the operator without actually, it is also possible to just clone this repository, and run the operator locally - outside of the Kubernetes cluster. This requires Go 1.19+ : +Alternatively, if you just wish to try out the operator without actually installing it, it is also possible to just clone this repository, and run the operator locally - outside of the Kubernetes cluster. This requires Go 1.19+, and prior installation of the CRDs located in `config/crd/bases`, typically with `kubectl`. After which, you can simply run : ```shell git clone https://github.com/InseeFrLab/s3-operator.git # or use a tag/release @@ -56,7 +58,7 @@ cd s3-operator go run main.go --s3-endpoint-url *** --s3-access-key *** --s3-secret-key *** # see below for the parameters ``` -To quote the Operator SDK README (also visible below), running the operator this way *will automatically use the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows).* RBAC-wise, you need to be able to freely manipulate the custom resources associated to the operator (`Bucket` and `Policy`) in every namespace - [see also the generated ClusterRole manifest](https://github.com/InseeFrLab/s3-operator/blob/main/config/rbac/role.yaml). +To quote the Operator SDK README (also visible below), running the operator this way *will automatically use the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows).* RBAC-wise, you need to be able to freely manipulate the custom resources associated to the operator (`Bucket`, `Policy` and `Path`) in every namespace - [see also the generated ClusterRole manifest](https://github.com/InseeFrLab/s3-operator/blob/main/config/rbac/role.yaml). ### Kustomize @@ -165,7 +167,30 @@ spec: } ] } +``` + +### Path example + +```yaml +apiVersion: s3.onyxia.sh/v1alpha1 +kind: Path +metadata: + labels: + app.kubernetes.io/name: path + app.kubernetes.io/instance: path-sample + app.kubernetes.io/part-of: s3-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: s3-operator + name: path-sample +spec: + # Bucket name (on S3 server, not a Bucket CR's metadata.name) + bucketName: shared-bucket + # Paths to create on the bucket + paths: + - /home/alice + - /home/bob + ``` diff --git a/api/v1alpha1/bucket_types.go b/api/v1alpha1/bucket_types.go index 3ec4f51..092aac6 100644 --- a/api/v1alpha1/bucket_types.go +++ b/api/v1alpha1/bucket_types.go @@ -43,8 +43,8 @@ type BucketSpec struct { // BucketStatus defines the observed state of Bucket type BucketStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + // Status management using Conditions. + // See also : https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` } diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go index 2f1f24b..cff37ca 100644 --- a/api/v1alpha1/groupversion_info.go +++ b/api/v1alpha1/groupversion_info.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package v1alpha1 contains API Schema definitions for the s3 v1alpha1 API group +// Package v1alpha1 contains API Schema definitions for the s3.onyxia.sh v1alpha1 API group // +kubebuilder:object:generate=true // +groupName=s3.onyxia.sh package v1alpha1 diff --git a/api/v1alpha1/path_types.go b/api/v1alpha1/path_types.go new file mode 100644 index 0000000..68ae026 --- /dev/null +++ b/api/v1alpha1/path_types.go @@ -0,0 +1,70 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// PathSpec defines the desired state of Path +type PathSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Name of the bucket + // +kubebuilder:validation:Required + BucketName string `json:"bucketName"` + + // Paths (folders) to create inside the bucket + // +kubebuilder:validation:Optional + Paths []string `json:"paths,omitempty"` +} + +// PathStatus defines the observed state of Path +type PathStatus struct { + // Status management using Conditions. + // See also : https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// Path is the Schema for the paths API +type Path struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PathSpec `json:"spec,omitempty"` + Status PathStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// PathList contains a list of Path +type PathList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Path `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Path{}, &PathList{}) +} diff --git a/api/v1alpha1/policy_types.go b/api/v1alpha1/policy_types.go index a48b688..6862f5e 100644 --- a/api/v1alpha1/policy_types.go +++ b/api/v1alpha1/policy_types.go @@ -39,8 +39,8 @@ type PolicySpec struct { // PolicyStatus defines the observed state of Policy type PolicyStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + // Status management using Conditions. + // See also : https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 4a32713..938126c 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -128,6 +128,107 @@ func (in *BucketStatus) DeepCopy() *BucketStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Path) DeepCopyInto(out *Path) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Path. +func (in *Path) DeepCopy() *Path { + if in == nil { + return nil + } + out := new(Path) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Path) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PathList) DeepCopyInto(out *PathList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Path, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PathList. +func (in *PathList) DeepCopy() *PathList { + if in == nil { + return nil + } + out := new(PathList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PathList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PathSpec) DeepCopyInto(out *PathSpec) { + *out = *in + if in.Paths != nil { + in, out := &in.Paths, &out.Paths + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PathSpec. +func (in *PathSpec) DeepCopy() *PathSpec { + if in == nil { + return nil + } + out := new(PathSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PathStatus) DeepCopyInto(out *PathStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PathStatus. +func (in *PathStatus) DeepCopy() *PathStatus { + if in == nil { + return nil + } + out := new(PathStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Policy) DeepCopyInto(out *Policy) { *out = *in diff --git a/config/crd/bases/s3.onyxia.sh_buckets.yaml b/config/crd/bases/s3.onyxia.sh_buckets.yaml index 6edd66b..6b2cbcd 100644 --- a/config/crd/bases/s3.onyxia.sh_buckets.yaml +++ b/config/crd/bases/s3.onyxia.sh_buckets.yaml @@ -3,7 +3,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.11.1 + creationTimestamp: null name: buckets.s3.onyxia.sh spec: group: s3.onyxia.sh @@ -64,9 +65,7 @@ spec: description: BucketStatus defines the observed state of Bucket properties: conditions: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' + description: 'Status management using Conditions. See also : https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' items: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct diff --git a/config/crd/bases/s3.onyxia.sh_paths.yaml b/config/crd/bases/s3.onyxia.sh_paths.yaml new file mode 100644 index 0000000..bc55aa3 --- /dev/null +++ b/config/crd/bases/s3.onyxia.sh_paths.yaml @@ -0,0 +1,126 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.1 + creationTimestamp: null + name: paths.s3.onyxia.sh +spec: + group: s3.onyxia.sh + names: + kind: Path + listKind: PathList + plural: paths + singular: path + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Path is the Schema for the paths API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PathSpec defines the desired state of Path + properties: + bucketName: + description: Name of the bucket + type: string + paths: + description: Paths (folders) to create inside the bucket + items: + type: string + type: array + required: + - bucketName + type: object + status: + description: PathStatus defines the observed state of Path + properties: + conditions: + description: 'Status management using Conditions. See also : https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/s3.onyxia.sh_policies.yaml b/config/crd/bases/s3.onyxia.sh_policies.yaml index 5918072..aa78618 100644 --- a/config/crd/bases/s3.onyxia.sh_policies.yaml +++ b/config/crd/bases/s3.onyxia.sh_policies.yaml @@ -3,7 +3,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.11.1 + creationTimestamp: null name: policies.s3.onyxia.sh spec: group: s3.onyxia.sh @@ -48,9 +49,7 @@ spec: description: PolicyStatus defines the observed state of Policy properties: conditions: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' + description: 'Status management using Conditions. See also : https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' items: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 4841954..a93ebae 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -4,19 +4,22 @@ resources: - bases/s3.onyxia.sh_buckets.yaml - bases/s3.onyxia.sh_policies.yaml +- bases/s3.onyxia.sh_paths.yaml #+kubebuilder:scaffold:crdkustomizeresource -patches: +patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD -#- path: patches/webhook_in_buckets.yaml -#- path: patches/webhook_in_policies.yaml +#- patches/webhook_in_buckets.yaml +#- patches/webhook_in_policies.yaml +#- patches/webhook_in_paths.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD -#- path: patches/cainjection_in_buckets.yaml -#- path: patches/cainjection_in_policies.yaml +#- patches/cainjection_in_buckets.yaml +#- patches/cainjection_in_policies.yaml +#- patches/cainjection_in_paths.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_buckets.yaml b/config/crd/patches/cainjection_in_buckets.yaml index 8d51be1..18870fa 100644 --- a/config/crd/patches/cainjection_in_buckets.yaml +++ b/config/crd/patches/cainjection_in_buckets.yaml @@ -3,5 +3,5 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) name: buckets.s3.onyxia.sh diff --git a/config/crd/patches/cainjection_in_paths.yaml b/config/crd/patches/cainjection_in_paths.yaml new file mode 100644 index 0000000..6c5369c --- /dev/null +++ b/config/crd/patches/cainjection_in_paths.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: paths.s3.onyxia.sh diff --git a/config/crd/patches/cainjection_in_policies.yaml b/config/crd/patches/cainjection_in_policies.yaml index 64fe8d1..b138a6a 100644 --- a/config/crd/patches/cainjection_in_policies.yaml +++ b/config/crd/patches/cainjection_in_policies.yaml @@ -3,5 +3,5 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) name: policies.s3.onyxia.sh diff --git a/config/crd/patches/webhook_in_paths.yaml b/config/crd/patches/webhook_in_paths.yaml new file mode 100644 index 0000000..e9d6275 --- /dev/null +++ b/config/crd/patches/webhook_in_paths.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: paths.s3.onyxia.sh +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index acce350..d2a7cce 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -9,12 +9,10 @@ namespace: s3-operator-system namePrefix: s3-operator- # Labels to add to all resources and selectors. -#labels: -#- includeSelectors: true -# pairs: -# someName: someValue +#commonLabels: +# someName: someValue -resources: +bases: - ../crd - ../rbac - ../manager @@ -46,101 +44,29 @@ patchesStrategicMerge: # the following config is for teaching kustomize how to do var substitution vars: # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -# Uncomment the following replacements to add the cert-manager CA injection annotations -#replacements: -# - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldPath: .metadata.namespace # namespace of the certificate CR -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldPath: .metadata.name -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - source: # Add cert-manager annotation to the webhook Service -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.name # namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 0 -# create: true -# - source: -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.namespace # namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 1 -# create: true +#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldref: +# fieldpath: metadata.namespace +#- name: CERTIFICATE_NAME +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # this name should match the one in certificate.yaml +#- name: SERVICE_NAMESPACE # namespace of the service +# objref: +# kind: Service +# version: v1 +# name: webhook-service +# fieldref: +# fieldpath: metadata.namespace +#- name: SERVICE_NAME +# objref: +# kind: Service +# version: v1 +# name: webhook-service diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 2957c27..4c237c1 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -37,7 +37,7 @@ spec: control-plane: controller-manager spec: # TODO(user): Uncomment the following code to configure the nodeAffinity expression - # according to the platforms which are supported by your solution. + # according to the platforms which are supported by your solution. # It is considered best practice to support multiple architectures. You can # build your manager image using the makefile target docker-buildx. # affinity: diff --git a/config/rbac/path_editor_role.yaml b/config/rbac/path_editor_role.yaml new file mode 100644 index 0000000..ff3e7ac --- /dev/null +++ b/config/rbac/path_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit paths. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: path-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: s3-operator + app.kubernetes.io/part-of: s3-operator + app.kubernetes.io/managed-by: kustomize + name: path-editor-role +rules: +- apiGroups: + - s3.onyxia.sh + resources: + - paths + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - s3.onyxia.sh + resources: + - paths/status + verbs: + - get diff --git a/config/rbac/path_viewer_role.yaml b/config/rbac/path_viewer_role.yaml new file mode 100644 index 0000000..6afede8 --- /dev/null +++ b/config/rbac/path_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view paths. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: path-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: s3-operator + app.kubernetes.io/part-of: s3-operator + app.kubernetes.io/managed-by: kustomize + name: path-viewer-role +rules: +- apiGroups: + - s3.onyxia.sh + resources: + - paths + verbs: + - get + - list + - watch +- apiGroups: + - s3.onyxia.sh + resources: + - paths/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 2c998a3..41cabe7 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -2,6 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + creationTimestamp: null name: manager-role rules: - apiGroups: @@ -30,6 +31,32 @@ rules: - get - patch - update +- apiGroups: + - s3.onyxia.sh + resources: + - paths + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - s3.onyxia.sh + resources: + - paths/finalizers + verbs: + - update +- apiGroups: + - s3.onyxia.sh + resources: + - paths/status + verbs: + - get + - patch + - update - apiGroups: - s3.onyxia.sh resources: diff --git a/config/rbac/service_account.yaml b/config/rbac/service_account.yaml index 104c4f3..9cf3648 100644 --- a/config/rbac/service_account.yaml +++ b/config/rbac/service_account.yaml @@ -3,7 +3,7 @@ kind: ServiceAccount metadata: labels: app.kubernetes.io/name: serviceaccount - app.kubernetes.io/instance: controller-manager-sa + app.kubernetes.io/instance: controller-manager app.kubernetes.io/component: rbac app.kubernetes.io/created-by: s3-operator app.kubernetes.io/part-of: s3-operator diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 80c8802..e886e4e 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,5 +1,6 @@ ## Append samples you want in your CSV to this file as resources ## resources: -- s3_v1alpha1_bucket.yaml -- s3_v1alpha1_policy.yaml +- s3.onyxia.sh_v1alpha1_bucket.yaml +- s3.onyxia.sh_v1alpha1_policy.yaml +- s3.onyxia.sh_v1alpha1_path.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/s3_v1alpha1_bucket.yaml b/config/samples/s3.onyxia.sh_v1alpha1_bucket.yaml similarity index 100% rename from config/samples/s3_v1alpha1_bucket.yaml rename to config/samples/s3.onyxia.sh_v1alpha1_bucket.yaml diff --git a/config/samples/s3.onyxia.sh_v1alpha1_path.yaml b/config/samples/s3.onyxia.sh_v1alpha1_path.yaml new file mode 100644 index 0000000..86cf9af --- /dev/null +++ b/config/samples/s3.onyxia.sh_v1alpha1_path.yaml @@ -0,0 +1,18 @@ +apiVersion: s3.onyxia.sh/v1alpha1 +kind: Path +metadata: + labels: + app.kubernetes.io/name: path + app.kubernetes.io/instance: path-sample + app.kubernetes.io/part-of: s3-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: s3-operator + name: path-sample +spec: + # Bucket name (on S3 server, as opposed to the name of the CR) + bucketName: dummy-bucket + + # Paths to create on the bucket + paths: + - path/to/create # a .keep file will be created at s3://dummy-bucket/path/to/create/.keep + diff --git a/config/samples/s3_v1alpha1_policy.yaml b/config/samples/s3.onyxia.sh_v1alpha1_policy.yaml similarity index 100% rename from config/samples/s3_v1alpha1_policy.yaml rename to config/samples/s3.onyxia.sh_v1alpha1_policy.yaml diff --git a/config/scorecard/patches/basic.config.yaml b/config/scorecard/patches/basic.config.yaml index fb74264..472a988 100644 --- a/config/scorecard/patches/basic.config.yaml +++ b/config/scorecard/patches/basic.config.yaml @@ -4,7 +4,7 @@ entrypoint: - scorecard-test - basic-check-spec - image: quay.io/operator-framework/scorecard-test:v1.27.0 + image: quay.io/operator-framework/scorecard-test:v1.32.0 labels: suite: basic test: basic-check-spec-test diff --git a/config/scorecard/patches/olm.config.yaml b/config/scorecard/patches/olm.config.yaml index 5d5aa63..343c6d8 100644 --- a/config/scorecard/patches/olm.config.yaml +++ b/config/scorecard/patches/olm.config.yaml @@ -4,7 +4,7 @@ entrypoint: - scorecard-test - olm-bundle-validation - image: quay.io/operator-framework/scorecard-test:v1.27.0 + image: quay.io/operator-framework/scorecard-test:v1.32.0 labels: suite: olm test: olm-bundle-validation-test @@ -14,7 +14,7 @@ entrypoint: - scorecard-test - olm-crds-have-validation - image: quay.io/operator-framework/scorecard-test:v1.27.0 + image: quay.io/operator-framework/scorecard-test:v1.32.0 labels: suite: olm test: olm-crds-have-validation-test @@ -24,7 +24,7 @@ entrypoint: - scorecard-test - olm-crds-have-resources - image: quay.io/operator-framework/scorecard-test:v1.27.0 + image: quay.io/operator-framework/scorecard-test:v1.32.0 labels: suite: olm test: olm-crds-have-resources-test @@ -34,7 +34,7 @@ entrypoint: - scorecard-test - olm-spec-descriptors - image: quay.io/operator-framework/scorecard-test:v1.27.0 + image: quay.io/operator-framework/scorecard-test:v1.32.0 labels: suite: olm test: olm-spec-descriptors-test @@ -44,7 +44,7 @@ entrypoint: - scorecard-test - olm-status-descriptors - image: quay.io/operator-framework/scorecard-test:v1.27.0 + image: quay.io/operator-framework/scorecard-test:v1.32.0 labels: suite: olm test: olm-status-descriptors-test diff --git a/internal/controller/bucket_controller.go b/controllers/bucket_controller.go similarity index 98% rename from internal/controller/bucket_controller.go rename to controllers/bucket_controller.go index e386ff6..c0a0130 100644 --- a/internal/controller/bucket_controller.go +++ b/controllers/bucket_controller.go @@ -33,8 +33,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" - s3v1alpha1 "github.com/inseefrlab/s3-operator/api/v1alpha1" - "github.com/inseefrlab/s3-operator/internal/controller/s3/factory" + s3v1alpha1 "github.com/phlg/s3-operator-downgrade/api/v1alpha1" + "github.com/phlg/s3-operator-downgrade/controllers/s3/factory" ) // BucketReconciler reconciles a Bucket object @@ -142,7 +142,7 @@ func (r *BucketReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr // TODO ? : the way this is naively implemented, it's probably costly. Maybe // we can get the "effectiveBucket" (with its quota and paths) once at the beginning, // and iterate on this instead of interrogating the S3 server twice for every path. - // But then again, some buckets will likely be filled with many objets outside the + // But then again, some buckets will likely be filled with many objects outside the // scope of the CR, so getting all of them might be even more costly. for _, pathInCr := range bucketResource.Spec.Paths { pathExists, err := r.S3Client.PathExists(bucketResource.Spec.Name, pathInCr) diff --git a/controllers/path_controller.go b/controllers/path_controller.go new file mode 100644 index 0000000..ad19d5b --- /dev/null +++ b/controllers/path_controller.go @@ -0,0 +1,163 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + "fmt" + "time" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + s3v1alpha1 "github.com/phlg/s3-operator-downgrade/api/v1alpha1" + "github.com/phlg/s3-operator-downgrade/controllers/s3/factory" +) + +// PathReconciler reconciles a Path object +type PathReconciler struct { + client.Client + Scheme *runtime.Scheme + S3Client factory.S3Client +} + +//+kubebuilder:rbac:groups=s3.onyxia.sh,resources=paths,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=s3.onyxia.sh,resources=paths/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=s3.onyxia.sh,resources=paths/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Path object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.1/pkg/reconcile +func (r *PathReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + pathResource := &s3v1alpha1.Path{} + err := r.Get(ctx, req.NamespacedName, pathResource) + if err != nil { + if errors.IsNotFound(err) { + logger.Info(fmt.Sprintf("Path CRD %s has been removed. NOOP", req.Name)) + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + // Check bucket existence on the S3 server + bucketFound, err := r.S3Client.BucketExists(pathResource.Spec.BucketName) + if err != nil { + logger.Error(err, "an error occurred while checking the existence of a bucket", "bucket", pathResource.Spec.BucketName) + return r.SetPathStatusConditionAndUpdate(ctx, pathResource, "OperatorFailed", metav1.ConditionFalse, "BucketExistenceCheckFailed", + fmt.Sprintf("Checking existence of bucket [%s] from S3 instance has failed", pathResource.Spec.BucketName), err) + } + + // If bucket does not exist, the Path CR should be in a failing state + if !bucketFound { + logger.Error(err, "the path CR references a non-existing bucket", "pathCr", pathResource.Name, "bucket", pathResource.Spec.BucketName) + return r.SetPathStatusConditionAndUpdate(ctx, pathResource, "OperatorFailed", metav1.ConditionFalse, "ReferencingNonExistingBucket", + fmt.Sprintf("The Path CR [%s] references a non-existing bucket [%s]", pathResource.Name, pathResource.Spec.BucketName), err) + } + + // If the bucket exists, proceed to create or recreate the referenced paths + // For every path on the custom resource's spec, we check the path actually + // exists on the bucket on the S3 server, and create it if it doesn't + // TODO ? : the way this is naively implemented, it's probably costly. Maybe + // we can get the "effectiveBucket" (with its quota and paths) once at the beginning, + // and iterate on this instead of interrogating the S3 server twice for every path. + // But then again, some buckets will likely be filled with many objects outside the + // scope of the CR, so getting all of them might be even more costly. + for _, pathInCr := range pathResource.Spec.Paths { + pathExists, err := r.S3Client.PathExists(pathResource.Spec.BucketName, pathInCr) + if err != nil { + logger.Error(err, "an error occurred while checking a path's existence on a bucket", "bucket", pathResource.Spec.BucketName, "path", pathInCr) + return r.SetPathStatusConditionAndUpdate(ctx, pathResource, "OperatorFailed", metav1.ConditionFalse, "PathCheckFailed", + fmt.Sprintf("The check for path [%s] on bucket [%s] has failed", pathInCr, pathResource.Spec.BucketName), err) + } + + if !pathExists { + err = r.S3Client.CreatePath(pathResource.Spec.BucketName, pathInCr) + if err != nil { + logger.Error(err, "an error occurred while creating a path on a bucket", "bucket", pathResource.Spec.BucketName, "path", pathInCr) + return r.SetPathStatusConditionAndUpdate(ctx, pathResource, "OperatorFailed", metav1.ConditionFalse, "PathCreationFailed", + fmt.Sprintf("The creation of path [%s] on bucket [%s] has failed", pathInCr, pathResource.Spec.BucketName), err) + } + } + } + + // The bucket reconciliation with its CR was succesful (or NOOP) + return r.SetPathStatusConditionAndUpdate(ctx, pathResource, "OperatorSucceeded", metav1.ConditionTrue, "PathsCreated", + fmt.Sprintf("The paths were created according to the specs of the [%s] CR", pathResource.Name), nil) + +} + +// SetupWithManager sets up the controller with the Manager. +func (r *PathReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&s3v1alpha1.Path{}). + // TODO : implement a real strategy for event filtering ; for now just using the example from OpSDK doc + // (https://sdk.operatorframework.io/docs/building-operators/golang/references/event-filtering/) + WithEventFilter(predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + // Ignore updates to CR status in which case metadata.Generation does not change + return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() + }, + DeleteFunc: func(e event.DeleteEvent) bool { + // Evaluates to false if the object has been confirmed deleted. + return !e.DeleteStateUnknown + }, + }). + WithOptions(controller.Options{MaxConcurrentReconciles: 10}). + Complete(r) + + // return ctrl.NewControllerManagedBy(mgr). + // For(&s3v1alpha1.Path{}). + // Complete(r) +} + +func (r *PathReconciler) SetPathStatusConditionAndUpdate(ctx context.Context, pathResource *s3v1alpha1.Path, conditionType string, status metav1.ConditionStatus, reason string, message string, srcError error) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + meta.SetStatusCondition(&pathResource.Status.Conditions, + metav1.Condition{ + Type: conditionType, + Status: status, + Reason: reason, + LastTransitionTime: metav1.NewTime(time.Now()), + Message: message, + ObservedGeneration: pathResource.GetGeneration(), + }) + + err := r.Status().Update(ctx, pathResource) + if err != nil { + logger.Error(err, "an error occurred while updating the status of the bucket resource") + return ctrl.Result{}, utilerrors.NewAggregate([]error{err, srcError}) + } + return ctrl.Result{}, srcError +} diff --git a/internal/controller/policy_controller.go b/controllers/policy_controller.go similarity index 98% rename from internal/controller/policy_controller.go rename to controllers/policy_controller.go index 1883acd..a6e060e 100644 --- a/internal/controller/policy_controller.go +++ b/controllers/policy_controller.go @@ -36,8 +36,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" - s3v1alpha1 "github.com/inseefrlab/s3-operator/api/v1alpha1" - "github.com/inseefrlab/s3-operator/internal/controller/s3/factory" + s3v1alpha1 "github.com/phlg/s3-operator-downgrade/api/v1alpha1" + "github.com/phlg/s3-operator-downgrade/controllers/s3/factory" ) // PolicyReconciler reconciles a Policy object diff --git a/internal/controller/s3/factory/interface.go b/controllers/s3/factory/interface.go similarity index 100% rename from internal/controller/s3/factory/interface.go rename to controllers/s3/factory/interface.go diff --git a/internal/controller/s3/factory/minioS3Client.go b/controllers/s3/factory/minioS3Client.go similarity index 100% rename from internal/controller/s3/factory/minioS3Client.go rename to controllers/s3/factory/minioS3Client.go diff --git a/internal/controller/s3/factory/mockedS3Client.go b/controllers/s3/factory/mockedS3Client.go similarity index 100% rename from internal/controller/s3/factory/mockedS3Client.go rename to controllers/s3/factory/mockedS3Client.go diff --git a/internal/controller/suite_test.go b/controllers/suite_test.go similarity index 90% rename from internal/controller/suite_test.go rename to controllers/suite_test.go index 5e5caca..81bd752 100644 --- a/internal/controller/suite_test.go +++ b/controllers/suite_test.go @@ -30,7 +30,7 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - s3v1alpha1 "github.com/inseefrlab/s3-operator/api/v1alpha1" + s3onyxiashv1alpha1 "github.com/phlg/s3-operator-downgrade/api/v1alpha1" //+kubebuilder:scaffold:imports ) @@ -52,7 +52,7 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: true, } @@ -62,7 +62,7 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - err = s3v1alpha1.AddToScheme(scheme.Scheme) + err = s3onyxiashv1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) //+kubebuilder:scaffold:scheme diff --git a/go.mod b/go.mod index bf64f24..3908e19 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/inseefrlab/s3-operator +module github.com/phlg/s3-operator-downgrade go 1.21 @@ -8,9 +8,9 @@ require ( github.com/onsi/ginkgo/v2 v2.11.0 github.com/onsi/gomega v1.27.10 go.uber.org/zap v1.25.0 - k8s.io/apimachinery v0.28.2 - k8s.io/client-go v0.28.2 - sigs.k8s.io/controller-runtime v0.16.2 + k8s.io/apimachinery v0.28.3 + k8s.io/client-go v0.28.3 + sigs.k8s.io/controller-runtime v0.16.3 ) require ( @@ -18,7 +18,7 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-logr/logr v1.2.4 // indirect @@ -37,7 +37,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect + github.com/imdario/mergo v0.3.6 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.16.7 // indirect @@ -86,9 +86,9 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.28.2 // indirect - k8s.io/apiextensions-apiserver v0.28.0 // indirect - k8s.io/component-base v0.28.1 // indirect + k8s.io/api v0.28.3 // indirect + k8s.io/apiextensions-apiserver v0.28.3 // indirect + k8s.io/component-base v0.28.3 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect diff --git a/go.sum b/go.sum index 2d319b2..9790a39 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= @@ -62,8 +62,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -285,30 +285,29 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw= -k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg= -k8s.io/apiextensions-apiserver v0.28.0 h1:CszgmBL8CizEnj4sj7/PtLGey6Na3YgWyGCPONv7E9E= -k8s.io/apiextensions-apiserver v0.28.0/go.mod h1:uRdYiwIuu0SyqJKriKmqEN2jThIJPhVmOWETm8ud1VE= -k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= -k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= -k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY= -k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY= -k8s.io/component-base v0.28.1 h1:LA4AujMlK2mr0tZbQDZkjWbdhTV5bRyEyAFe0TJxlWg= -k8s.io/component-base v0.28.1/go.mod h1:jI11OyhbX21Qtbav7JkhehyBsIRfnO8oEgoAR12ArIU= +k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= +k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= +k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= +k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= +k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= +k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= +k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= +k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= +k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= +k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.16.2 h1:mwXAVuEk3EQf478PQwQ48zGOXvW27UJc8NHktQVuIPU= -sigs.k8s.io/controller-runtime v0.16.2/go.mod h1:vpMu3LpI5sYWtujJOa2uPK61nB5rbwlN7BAB8aSLvGU= +sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= +sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= diff --git a/cmd/main.go b/main.go similarity index 93% rename from cmd/main.go rename to main.go index aab6654..3e96c09 100644 --- a/cmd/main.go +++ b/main.go @@ -25,9 +25,9 @@ import ( // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" - s3v1alpha1 "github.com/inseefrlab/s3-operator/api/v1alpha1" - controllers "github.com/inseefrlab/s3-operator/internal/controller" - "github.com/inseefrlab/s3-operator/internal/controller/s3/factory" + s3v1alpha1 "github.com/phlg/s3-operator-downgrade/api/v1alpha1" + controllers "github.com/phlg/s3-operator-downgrade/controllers" + "github.com/phlg/s3-operator-downgrade/controllers/s3/factory" "go.uber.org/zap/zapcore" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -163,6 +163,14 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "Bucket") os.Exit(1) } + if err = (&controllers.PathReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + S3Client: s3Client, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Path") + os.Exit(1) + } if err = (&controllers.PolicyReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(),