-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add addons, apps and tenant API #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
23ef402
8c05dc8
d3f4ebc
6d785cd
4c22268
2f6d15e
eae801a
677b388
437d413
737d7aa
06d1ff9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| name: Lint | ||
|
|
||
| on: | ||
| push: | ||
| pull_request: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| lint: | ||
| permissions: | ||
| contents: read # for actions/checkout to fetch code | ||
| pull-requests: read # for golangci/golangci-lint-action to fetch pull requests | ||
| name: Run on Ubuntu | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Clone the code | ||
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | ||
|
|
||
| - name: Setup Go | ||
| uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 | ||
| with: | ||
| go-version-file: go.mod | ||
|
|
||
| - name: Run linter | ||
| uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 | ||
| with: | ||
| version: v2.10.1 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| *.dll | ||
| *.so | ||
| *.dylib | ||
| bin/* | ||
|
|
||
| # Test binary, built with `go test -c` | ||
| *.test | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,28 +1,118 @@ | ||
| # Version is set from the latest git tag or commit hash | ||
| VERSION=$(shell git describe --tags --always) | ||
|
|
||
| .PHONY: generate | ||
| # generate code | ||
| generate: | ||
| buf generate | ||
| # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) | ||
| ifeq (,$(shell go env GOBIN)) | ||
| GOBIN=$(shell go env GOPATH)/bin | ||
| else | ||
| GOBIN=$(shell go env GOBIN) | ||
| endif | ||
|
|
||
| ##@ General | ||
|
|
||
| # The help target prints out all targets with their descriptions organized | ||
| # beneath their categories. The categories are represented by '##@' and the | ||
| # target descriptions by '##'. The awk command is responsible for reading the | ||
| # entire set of makefiles included in this invocation, looking for lines of the | ||
| # file as xyz: ## something, and then pretty-format the target and help. Then, | ||
| # if there's a line with ##@ something, that gets pretty-printed as a category. | ||
| # More info on the usage of ANSI control characters for terminal formatting: | ||
| # https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters | ||
| # More info on the awk command: | ||
| # http://linuxcommand.org/lc3_adv_awk.php | ||
|
|
||
| .PHONY: help | ||
| help: ## Display this help. | ||
| @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) | ||
|
|
||
| ##@ Development | ||
|
|
||
| .PHONY: proto | ||
| proto: buf ## Generate code from proto files | ||
| "$(BUF)" generate | ||
| rm openapi.pb.go ts/src/openapi_pb.* # workaround for buf exclude | ||
|
|
||
| .PHONY: manifests | ||
| manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. | ||
| "$(CONTROLLER_GEN)" rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases | ||
|
|
||
| .PHONY: generate | ||
| generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. | ||
| "$(CONTROLLER_GEN)" object:headerFile="hack/boilerplate.go.txt" paths="./..." | ||
|
|
||
| .PHONY: fmt | ||
| fmt: ## Run go fmt against code. | ||
| go fmt ./... | ||
|
|
||
| .PHONY: vet | ||
| vet: ## Run go vet against code. | ||
| go vet ./... | ||
|
|
||
| .PHONY: tidy | ||
| tidy: ## Run go mod tidy | ||
| go mod tidy | ||
|
|
||
| .PHONY: help | ||
| # show help | ||
| help: | ||
| @echo '' | ||
| @echo 'Usage:' | ||
| @echo ' make [target]' | ||
| @echo '' | ||
| @echo 'Targets:' | ||
| @awk '/^[a-zA-Z\-0-9]+:/ { \ | ||
| helpMessage = match(lastLine, /^# (.*)/); \ | ||
| if (helpMessage) { \ | ||
| helpCommand = substr($$1, 0, index($$1, ":")-1); \ | ||
| helpMessage = substr(lastLine, RSTART + 2, RLENGTH); \ | ||
| printf "\033[36m%-22s\033[0m %s\n", helpCommand,helpMessage; \ | ||
| } \ | ||
| } \ | ||
| { lastLine = $$0 }' $(MAKEFILE_LIST) | ||
|
|
||
| .DEFAULT_GOAL := help | ||
| .PHONY: lint | ||
| lint: golangci-lint ## Run golangci-lint linter | ||
| "$(GOLANGCI_LINT)" run | ||
|
|
||
| .PHONY: lint-fix | ||
| lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes | ||
| "$(GOLANGCI_LINT)" run --fix | ||
|
|
||
| .PHONY: lint-config | ||
| lint-config: golangci-lint ## Verify golangci-lint linter configuration | ||
| "$(GOLANGCI_LINT)" config verify | ||
|
|
||
| .PHONY: all | ||
| all: proto manifests generate fmt vet tidy lint ## Generate all code from proto files, manifests, and go code. | ||
|
|
||
| .DEFAULT_GOAL := all | ||
|
|
||
| ##@ Dependencies | ||
|
|
||
| ## Location to install dependencies to | ||
| LOCALBIN ?= $(shell pwd)/bin | ||
| $(LOCALBIN): | ||
| mkdir -p "$(LOCALBIN)" | ||
|
|
||
| ## Tool Binaries | ||
| BUF ?= $(LOCALBIN)/buf | ||
| CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen | ||
| GOLANGCI_LINT = $(LOCALBIN)/golangci-lint | ||
|
|
||
| ## Tool Versions | ||
| BUF_VERSION ?= v1.66.0 | ||
| CONTROLLER_TOOLS_VERSION ?= v0.20.1 | ||
| GOLANGCI_LINT_VERSION ?= v2.10.1 | ||
|
|
||
| .PHONY: buf | ||
| buf: $(BUF) ## Download buf locally if necessary. | ||
| $(BUF): $(LOCALBIN) | ||
| $(call go-install-tool,$(BUF),github.com/bufbuild/buf/cmd/buf,$(BUF_VERSION)) | ||
|
|
||
| .PHONY: controller-gen | ||
| controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. | ||
| $(CONTROLLER_GEN): $(LOCALBIN) | ||
| $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) | ||
|
|
||
| .PHONY: golangci-lint | ||
| golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. | ||
| $(GOLANGCI_LINT): $(LOCALBIN) | ||
| $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) | ||
|
|
||
| # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist | ||
| # $1 - target path with name of binary | ||
| # $2 - package url which can be installed | ||
| # $3 - specific version of package | ||
| define go-install-tool | ||
| @[ -f "$(1)-$(3)" ] && [ "$$(readlink -- "$(1)" 2>/dev/null)" = "$(1)-$(3)" ] || { \ | ||
| set -e; \ | ||
| package=$(2)@$(3) ;\ | ||
| echo "Downloading $${package}" ;\ | ||
| rm -f "$(1)" ;\ | ||
| GOBIN="$(LOCALBIN)" go install $${package} ;\ | ||
| mv "$(LOCALBIN)/$$(basename "$(1)")" "$(1)-$(3)" ;\ | ||
| } ;\ | ||
| ln -sf "$$(realpath "$(1)-$(3)")" "$(1)" | ||
| endef | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| /* | ||
| Copyright 2026 The OtterScale Authors. | ||
|
|
||
| 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 contains API Schema definitions for the addons v1alpha1 API group. | ||
| // +kubebuilder:object:generate=true | ||
| // +groupName=addons.otterscale.io | ||
| package v1alpha1 | ||
|
|
||
| import ( | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| "k8s.io/apimachinery/pkg/runtime" | ||
| "k8s.io/apimachinery/pkg/runtime/schema" | ||
| ) | ||
|
|
||
| var ( | ||
| // GroupVersion is group version used to register these objects. | ||
| GroupVersion = schema.GroupVersion{Group: "addons.otterscale.io", Version: "v1alpha1"} | ||
|
|
||
| // SchemeBuilder is used to add go types to the GroupVersionKind scheme. | ||
| SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) | ||
|
|
||
| // AddToScheme adds the types in this group-version to the given scheme. | ||
| AddToScheme = SchemeBuilder.AddToScheme | ||
| ) | ||
|
|
||
| func addKnownTypes(s *runtime.Scheme) error { | ||
| s.AddKnownTypes(GroupVersion, | ||
| &Module{}, | ||
| &ModuleList{}, | ||
| &ModuleTemplate{}, | ||
| &ModuleTemplateList{}, | ||
| ) | ||
| metav1.AddToGroupVersion(s, GroupVersion) | ||
| return nil | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,129 @@ | ||||||
| /* | ||||||
| Copyright 2026 The OtterScale Authors. | ||||||
|
|
||||||
| 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" | ||||||
| "k8s.io/apimachinery/pkg/runtime" | ||||||
| ) | ||||||
|
|
||||||
| // ModuleSpec defines the desired state of an installed Module. | ||||||
| // A Module instantiates a ModuleTemplate by referencing it and optionally | ||||||
| // overriding the target namespace or Helm values. | ||||||
| type ModuleSpec struct { | ||||||
| // TemplateRef is the name of the ModuleTemplate to instantiate. | ||||||
| // This field is immutable after creation. | ||||||
| // +kubebuilder:validation:MinLength=1 | ||||||
| // +kubebuilder:validation:MaxLength=253 | ||||||
| // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="templateRef is immutable" | ||||||
| // +required | ||||||
| TemplateRef string `json:"templateRef"` | ||||||
|
|
||||||
| // Namespace overrides the default target namespace defined in the ModuleTemplate. | ||||||
| // If not specified, the namespace from the ModuleTemplate is used. | ||||||
| // +kubebuilder:validation:MinLength=1 | ||||||
| // +kubebuilder:validation:MaxLength=63 | ||||||
| // +kubebuilder:validation:Pattern=`^([a-z0-9]([-a-z0-9]*[a-z0-9])?)$` | ||||||
| // +optional | ||||||
| Namespace *string `json:"namespace,omitempty"` | ||||||
|
|
||||||
| // Values overrides the default values for HelmRelease-based modules. | ||||||
| // Only applicable when the referenced ModuleTemplate uses a HelmRelease template. | ||||||
| // Ignored for Kustomization-based modules. | ||||||
| // Accepts arbitrary JSON; RawExtension is used instead of apiextensionsv1.JSON | ||||||
| // to avoid pulling in the k8s.io/apiextensions-apiserver dependency. | ||||||
| // +kubebuilder:pruning:PreserveUnknownFields | ||||||
| // +optional | ||||||
| Values *runtime.RawExtension `json:"values,omitempty"` | ||||||
| } | ||||||
|
|
||||||
| // ResourceReference is a lightweight reference to a Kubernetes resource managed by the operator. | ||||||
| type ResourceReference struct { | ||||||
| // Name is the name of the referenced resource. | ||||||
| // +required | ||||||
| Name string `json:"name"` | ||||||
|
|
||||||
| // Namespace is the namespace of the referenced resource. | ||||||
| // +optional | ||||||
| Namespace string `json:"namespace,omitempty"` | ||||||
| } | ||||||
|
|
||||||
| // ModuleStatus defines the observed state of a Module. | ||||||
| // It contains references to the actual FluxCD resources created by the controller | ||||||
| // and reflects their health status. | ||||||
| type ModuleStatus struct { | ||||||
| // ObservedGeneration is the most recent generation observed by the controller. | ||||||
| // It corresponds to the Module's generation, which is updated on mutation by the API Server. | ||||||
| // +optional | ||||||
| ObservedGeneration int64 `json:"observedGeneration,omitempty"` | ||||||
|
|
||||||
| // TemplateGeneration tracks the observed generation of the referenced ModuleTemplate. | ||||||
| // This allows the controller to detect and reconcile template changes. | ||||||
| // +optional | ||||||
| TemplateGeneration int64 `json:"templateGeneration,omitempty"` | ||||||
|
|
||||||
| // HelmReleaseRef is a reference to the FluxCD HelmRelease managed by this Module. | ||||||
| // +optional | ||||||
| HelmReleaseRef *ResourceReference `json:"helmReleaseRef,omitempty"` | ||||||
|
|
||||||
| // KustomizationRef is a reference to the FluxCD Kustomization managed by this Module. | ||||||
| // +optional | ||||||
| KustomizationRef *ResourceReference `json:"kustomizationRef,omitempty"` | ||||||
|
|
||||||
| // Conditions store the status conditions of the Module (e.g., Ready, TemplateNotFound). | ||||||
| // +listType=map | ||||||
| // +listMapKey=type | ||||||
| // +optional | ||||||
| Conditions []metav1.Condition `json:"conditions,omitempty"` | ||||||
| } | ||||||
|
|
||||||
| // +kubebuilder:object:root=true | ||||||
| // +kubebuilder:subresource:status | ||||||
| // +kubebuilder:resource:scope=Cluster | ||||||
| // +kubebuilder:printcolumn:name="Template",type=string,JSONPath=`.spec.templateRef` | ||||||
| // +kubebuilder:printcolumn:name="Namespace",type=string,JSONPath=`.spec.namespace` | ||||||
| // +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status` | ||||||
| // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" | ||||||
|
|
||||||
| // Module is the Schema for the modules API. | ||||||
| // A Module represents an installed platform addon instantiated from a ModuleTemplate. | ||||||
| // The controller creates the corresponding FluxCD HelmRelease or Kustomization | ||||||
| // and reflects its status back to the Module. | ||||||
| type Module struct { | ||||||
| metav1.TypeMeta `json:",inline"` | ||||||
|
|
||||||
| // Standard object's metadata. | ||||||
| // +optional | ||||||
| metav1.ObjectMeta `json:"metadata,omitzero"` | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The JSON tag
Suggested change
|
||||||
|
|
||||||
| // Spec defines the desired behavior of the Module. | ||||||
| // +required | ||||||
| Spec ModuleSpec `json:"spec"` | ||||||
|
|
||||||
| // Status represents the current information about the Module. | ||||||
| // +optional | ||||||
| Status ModuleStatus `json:"status,omitzero"` | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| } | ||||||
|
|
||||||
| // +kubebuilder:object:root=true | ||||||
|
|
||||||
| // ModuleList contains a list of Module resources. | ||||||
| type ModuleList struct { | ||||||
| metav1.TypeMeta `json:",inline"` | ||||||
| metav1.ListMeta `json:"metadata,omitzero"` | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| Items []Module `json:"items"` | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
prototarget deletes generated files usingrmwithout-f. If buf generation output changes (or the files are already absent), this will makemake protofail even though generation succeeded. Userm -f(or guard withtest -e) to keep the target idempotent.