├── chart ├── templates │ ├── NOTES.txt │ ├── _helpers.yaml │ ├── deployment.yaml │ ├── rbac.yaml │ └── crds │ │ └── integration.rock8s.com_deferredresources.yaml ├── app-readme.md ├── Makefile ├── .helmignore ├── values.yaml ├── Chart.yaml └── questions.yaml ├── config ├── manager │ ├── kustomization.yaml │ └── manager.yaml ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── scorecard │ ├── bases │ │ └── config.yaml │ ├── patches │ │ ├── basic.config.yaml │ │ └── olm.config.yaml │ └── kustomization.yaml ├── default │ ├── manager_config_patch.yaml │ ├── manager_auth_proxy_patch.yaml │ └── kustomization.yaml ├── samples │ ├── integration_v1beta1_plug.yaml │ ├── kustomization.yaml │ ├── integration_v1beta1_deferredresource.yaml │ ├── rbac.yaml │ └── integration_v1beta1_socket.yaml ├── crd │ ├── patches │ │ ├── cainjection_in_plugs.yaml │ │ ├── cainjection_in_sockets.yaml │ │ ├── cainjection_in_deferredresources.yaml │ │ ├── webhook_in_plugs.yaml │ │ ├── webhook_in_sockets.yaml │ │ └── webhook_in_deferredresources.yaml │ ├── kustomizeconfig.yaml │ ├── kustomization.yaml │ └── bases │ │ └── integration.rock8s.com_deferredresources.yaml ├── rbac │ ├── service_account.yaml │ ├── auth_proxy_client_clusterrole.yaml │ ├── role_binding.yaml │ ├── auth_proxy_role_binding.yaml │ ├── leader_election_role_binding.yaml │ ├── auth_proxy_role.yaml │ ├── auth_proxy_service.yaml │ ├── plug_viewer_role.yaml │ ├── socket_viewer_role.yaml │ ├── kustomization.yaml │ ├── plug_editor_role.yaml │ ├── deferredresource_viewer_role.yaml │ ├── socket_editor_role.yaml │ ├── deferredresource_editor_role.yaml │ ├── leader_election_role.yaml │ └── role.yaml ├── manifests │ └── kustomization.yaml └── main.go ├── .gitattributes ├── images ├── typea.png ├── typed.png ├── integration-operator.jpg └── icon.svg ├── operator ├── ci.yaml └── 1.2.0 │ └── metadata │ └── annotations.yaml ├── .mkpm └── cache.tar.gz ├── .editorconfig ├── mkpm.json ├── .vscode ├── extensions.json └── settings.json ├── cspell.json ├── .gitignore ├── .dockerignore ├── hack └── boilerplate.go.txt ├── docker ├── docker-bake.hcl ├── Dockerfile └── Makefile ├── project-words.txt ├── coupler ├── main.go ├── decouple.go ├── update.go ├── plug.go ├── socket.go └── couple.go ├── PROJECT ├── Mkpmfile ├── .envrc ├── api └── v1beta1 │ ├── groupversion_info.go │ ├── deferredresource_types.go │ ├── shared_types.go │ ├── plug_types.go │ └── socket_types.go ├── mkpm ├── util ├── main.go ├── data.go ├── helpers.go ├── var.go ├── kubectl.go ├── event.go ├── plug.go └── config.go ├── settings.json ├── controllers ├── suite_test.go ├── plug_controller.go ├── deferredresource_controller.go └── socket_controller.go ├── go.mod └── main.go /chart/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | Thank you for installing 2 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | .mkpm/cache.tar.gz filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /chart/app-readme.md: -------------------------------------------------------------------------------- 1 | # integration-operator 2 | 3 | > kubernetes operator to integrate deployments 4 | -------------------------------------------------------------------------------- /images/typea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clayrisser/integration-operator/HEAD/images/typea.png -------------------------------------------------------------------------------- /images/typed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clayrisser/integration-operator/HEAD/images/typed.png -------------------------------------------------------------------------------- /images/integration-operator.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clayrisser/integration-operator/HEAD/images/integration-operator.jpg -------------------------------------------------------------------------------- /operator/ci.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Use `replaces-mode` or `semver-mode`. Once you switch to `semver-mode`, there is no easy way back. 3 | updateGraph: semver-mode 4 | -------------------------------------------------------------------------------- /.mkpm/cache.tar.gz: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b323bcf98a52ea178a4623eeb24138c5b1e9351ccc47c4163983e1ede69100f2 3 | size 17814 4 | -------------------------------------------------------------------------------- /config/scorecard/bases/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: [] 8 | -------------------------------------------------------------------------------- /chart/Makefile: -------------------------------------------------------------------------------- 1 | include $(MKPM)/mkpm 2 | include $(MKPM)/gnu 3 | 4 | export DOCKER_DEFAULT_PLATFORM=linux/amd64 5 | export PATCHES := 6 | 7 | include $(MKPM)/patch 8 | 9 | .PHONY: prepack 10 | prepack: patch-apply 11 | -------------------------------------------------------------------------------- /config/default/manager_config_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [Mkpmfile] 2 | charset = utf-8 3 | indent_size = 4 4 | indent_style = tab 5 | [{M,m}akefile{,.*}] 6 | charset = utf-8 7 | indent_size = 4 8 | indent_style = tab 9 | [*.mk] 10 | charset = utf-8 11 | indent_size = 4 12 | indent_style = tab 13 | -------------------------------------------------------------------------------- /mkpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": { 3 | "default": { 4 | "docker": "0.2.0", 5 | "gnu": "0.1.0", 6 | "patch": "0.0.5" 7 | } 8 | }, 9 | "repos": { 10 | "default": "https://gitlab.com/bitspur/mkpm/packages.git" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chart/.helmignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | *.orig 3 | *.swp 4 | *.tmp 5 | *.tmproj 6 | *~ 7 | .DS_Store 8 | .bzr/ 9 | .bzrignore 10 | .git/ 11 | .gitignore 12 | .hg/ 13 | .hgignore 14 | .idea/ 15 | .mkpm/ 16 | .project 17 | .svn/ 18 | .vscode/ 19 | /Makefile 20 | /patches 21 | -------------------------------------------------------------------------------- /config/samples/integration_v1beta1_plug.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: integration.rock8s.com/v1beta1 2 | kind: Plug 3 | metadata: 4 | name: postgres 5 | namespace: app 6 | spec: 7 | socket: 8 | name: postgres 9 | namespace: postgres-namespace 10 | config: 11 | database: app 12 | -------------------------------------------------------------------------------- /config/scorecard/patches/basic.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - basic-check-spec 7 | image: quay.io/operator-framework/scorecard-test:v1.32.0 8 | labels: 9 | suite: basic 10 | test: basic-check-spec-test 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "golang.go", 4 | "ms-azuretools.vscode-docker", 5 | "ms-kubernetes-tools.vscode-kubernetes-tools", 6 | "streetsidesoftware.code-spell-checker", 7 | "mkhl.direnv", 8 | "hashicorp.hcl", 9 | "fredwangwang.vscode-hcl-format" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_plugs.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: plugs.integration.rock8s.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_sockets.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: sockets.integration.rock8s.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_deferredresources.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: deferredresources.integration.rock8s.com 8 | -------------------------------------------------------------------------------- /config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | ## Append samples you want in your CSV to this file as resources ## 2 | resources: 3 | - integration_v1beta1_socket.yaml 4 | - integration_v1beta1_plug.yaml 5 | - rbac.yaml 6 | - integration_v1beta1_deferresource.yaml 7 | - integration_v1beta1_deferedresource.yaml 8 | - integration_v1beta1_deferredresource.yaml 9 | #+kubebuilder:scaffold:manifestskustomizesamples 10 | -------------------------------------------------------------------------------- /cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", 3 | "version": "0.2", 4 | "dictionaryDefinitions": [ 5 | { 6 | "name": "project-words", 7 | "path": "./project-words.txt", 8 | "addWords": true 9 | } 10 | ], 11 | "dictionaries": ["project-words"], 12 | "ignorePaths": ["node_modules", "/project-words.txt"] 13 | } 14 | -------------------------------------------------------------------------------- /operator/1.2.0/metadata/annotations.yaml: -------------------------------------------------------------------------------- 1 | annotations: 2 | operators.operatorframework.io.bundle.channel.default.v1: stable 3 | operators.operatorframework.io.bundle.channels.v1: stable 4 | operators.operatorframework.io.bundle.manifests.v1: manifests/ 5 | operators.operatorframework.io.bundle.mediatype.v1: registry+v1 6 | operators.operatorframework.io.bundle.metadata.v1: metadata/ 7 | operators.operatorframework.io.bundle.package.v1: integration-operator 8 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: serviceaccount 6 | app.kubernetes.io/instance: controller-manager 7 | app.kubernetes.io/component: rbac 8 | app.kubernetes.io/created-by: integration-operator 9 | app.kubernetes.io/part-of: integration-operator 10 | app.kubernetes.io/managed-by: kustomize 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/scorecard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - bases/config.yaml 3 | patchesJson6902: 4 | - path: patches/basic.config.yaml 5 | target: 6 | group: scorecard.operatorframework.io 7 | version: v1alpha3 8 | kind: Configuration 9 | name: config 10 | - path: patches/olm.config.yaml 11 | target: 12 | group: scorecard.operatorframework.io 13 | version: v1alpha3 14 | kind: Configuration 15 | name: config 16 | #+kubebuilder:scaffold:patchesJson6902 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_plugs.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: plugs.integration.rock8s.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_sockets.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: sockets.integration.rock8s.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/samples/integration_v1beta1_deferredresource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: integration.rock8s.com/v1beta1 2 | kind: DeferredResource 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: deferredresource 6 | app.kubernetes.io/instance: deferredresource-sample 7 | app.kubernetes.io/part-of: integration-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | app.kubernetes.io/created-by: integration-operator 10 | name: deferredresource-sample 11 | spec: 12 | # TODO(user): Add fields here 13 | -------------------------------------------------------------------------------- /chart/values.yaml: -------------------------------------------------------------------------------- 1 | images: 2 | resourceBindingOperator: 3 | repository: registry.gitlab.com/bitspur/rock8s/integration-operator 4 | tag: 1.2.0 5 | 6 | config: 7 | imagePullPolicy: Always 8 | updateStrategy: RollingUpdate 9 | debug: false 10 | replicas: 1 11 | maxConcurrentReconciles: 3 12 | resourceBindingOperator: 13 | resources: 14 | enabled: defaults 15 | requests: 16 | cpu: 100m 17 | memory: 50Mi 18 | limits: 19 | cpu: 200m 20 | memory: 100Mi 21 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_deferredresources.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: deferredresources.integration.rock8s.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /chart/Chart.yaml: -------------------------------------------------------------------------------- 1 | name: integration-operator 2 | apiVersion: v1 3 | home: https://gitlab.com/bitspur/rock8s/integration-operator 4 | icon: https://gitlab.com/bitspur/rock8s/integration-operator/-/raw/main/images/icon.svg 5 | version: 1.2.0 6 | appVersion: 1.2.0 7 | description: "bind a resource to a resource" 8 | keywords: 9 | - controller 10 | - operator 11 | - crossplane 12 | sources: 13 | - https://gitlab.com/bitspur/rock8s/integration-operator.git 14 | maintainers: 15 | - name: "Clay Risser" 16 | email: email@clayrisser.com 17 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: clusterrole 6 | app.kubernetes.io/instance: metrics-reader 7 | app.kubernetes.io/component: kube-rbac-proxy 8 | app.kubernetes.io/created-by: integration-operator 9 | app.kubernetes.io/part-of: integration-operator 10 | app.kubernetes.io/managed-by: kustomize 11 | name: metrics-reader 12 | rules: 13 | - nonResourceURLs: 14 | - "/metrics" 15 | verbs: 16 | - get 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | bin 8 | testbin/* 9 | Dockerfile.cross 10 | 11 | # Test binary, build with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Kubernetes Generated files - skip generated files, except for vendored files 18 | 19 | !vendor/**/zz_generated.* 20 | 21 | # editor and IDE paraphernalia 22 | .idea 23 | *.swp 24 | *.swo 25 | *~ 26 | 27 | # mkpm 28 | .mkpm/mkpm 29 | 30 | # app 31 | /integration-operator 32 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | bin 8 | testbin/* 9 | Dockerfile.cross 10 | 11 | # Test binary, build with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Kubernetes Generated files - skip generated files, except for vendored files 18 | 19 | !vendor/**/zz_generated.* 20 | 21 | # editor and IDE paraphernalia 22 | .idea 23 | *.swp 24 | *.swo 25 | *~ 26 | 27 | # mkpm 28 | .mkpm/mkpm 29 | 30 | # app 31 | /integration-operator 32 | -------------------------------------------------------------------------------- /config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | version: v1 8 | group: apiextensions.k8s.io 9 | path: spec/conversion/webhook/clientConfig/service/name 10 | 11 | namespace: 12 | - kind: CustomResourceDefinition 13 | version: v1 14 | group: apiextensions.k8s.io 15 | path: spec/conversion/webhook/clientConfig/service/namespace 16 | create: false 17 | 18 | varReference: 19 | - path: metadata/annotations 20 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ -------------------------------------------------------------------------------- /chart/templates/_helpers.yaml: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/** 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "integration-operator.name" }} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 7 | {{- end }} 8 | 9 | {{/** 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this 12 | (by the DNS naming spec). 13 | */}} 14 | {{- define "integration-operator.fullname" }} 15 | {{- $name := default .Chart.Name .Values.nameOverride }} 16 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 17 | {{- end }} 18 | -------------------------------------------------------------------------------- /config/samples/rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | name: plug-role 5 | namespace: default 6 | rules: 7 | - apiGroups: 8 | - '' 9 | resources: 10 | - configmaps 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | --- 20 | apiVersion: rbac.authorization.k8s.io/v1 21 | kind: RoleBinding 22 | metadata: 23 | name: plug-rolebinding 24 | namespace: default 25 | roleRef: 26 | apiGroup: rbac.authorization.k8s.io 27 | kind: Role 28 | name: plug-role 29 | subjects: 30 | - kind: ServiceAccount 31 | name: default 32 | namespace: default 33 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: clusterrolebinding 6 | app.kubernetes.io/instance: manager-rolebinding 7 | app.kubernetes.io/component: rbac 8 | app.kubernetes.io/created-by: integration-operator 9 | app.kubernetes.io/part-of: integration-operator 10 | app.kubernetes.io/managed-by: kustomize 11 | name: manager-rolebinding 12 | roleRef: 13 | apiGroup: rbac.authorization.k8s.io 14 | kind: ClusterRole 15 | name: manager-role 16 | subjects: 17 | - kind: ServiceAccount 18 | name: controller-manager 19 | namespace: system 20 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: clusterrolebinding 6 | app.kubernetes.io/instance: proxy-rolebinding 7 | app.kubernetes.io/component: kube-rbac-proxy 8 | app.kubernetes.io/created-by: integration-operator 9 | app.kubernetes.io/part-of: integration-operator 10 | app.kubernetes.io/managed-by: kustomize 11 | name: proxy-rolebinding 12 | roleRef: 13 | apiGroup: rbac.authorization.k8s.io 14 | kind: ClusterRole 15 | name: proxy-role 16 | subjects: 17 | - kind: ServiceAccount 18 | name: controller-manager 19 | namespace: system 20 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: rolebinding 6 | app.kubernetes.io/instance: leader-election-rolebinding 7 | app.kubernetes.io/component: rbac 8 | app.kubernetes.io/created-by: integration-operator 9 | app.kubernetes.io/part-of: integration-operator 10 | app.kubernetes.io/managed-by: kustomize 11 | name: leader-election-rolebinding 12 | roleRef: 13 | apiGroup: rbac.authorization.k8s.io 14 | kind: Role 15 | name: leader-election-role 16 | subjects: 17 | - kind: ServiceAccount 18 | name: controller-manager 19 | namespace: system 20 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: clusterrole 6 | app.kubernetes.io/instance: proxy-role 7 | app.kubernetes.io/component: kube-rbac-proxy 8 | app.kubernetes.io/created-by: integration-operator 9 | app.kubernetes.io/part-of: integration-operator 10 | app.kubernetes.io/managed-by: kustomize 11 | name: proxy-role 12 | rules: 13 | - apiGroups: 14 | - authentication.k8s.io 15 | resources: 16 | - tokenreviews 17 | verbs: 18 | - create 19 | - apiGroups: 20 | - authorization.k8s.io 21 | resources: 22 | - subjectaccessreviews 23 | verbs: 24 | - create 25 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | app.kubernetes.io/name: service 7 | app.kubernetes.io/instance: controller-manager-metrics-service 8 | app.kubernetes.io/component: kube-rbac-proxy 9 | app.kubernetes.io/created-by: integration-operator 10 | app.kubernetes.io/part-of: integration-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: controller-manager-metrics-service 13 | namespace: system 14 | spec: 15 | ports: 16 | - name: https 17 | port: 8443 18 | protocol: TCP 19 | targetPort: https 20 | selector: 21 | control-plane: controller-manager 22 | -------------------------------------------------------------------------------- /docker/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | variable "REGISTRY" { 2 | default = "docker.io/library" 3 | } 4 | 5 | variable "NAME" { 6 | default = "" 7 | } 8 | 9 | variable "VERSION" { 10 | default = "" 11 | } 12 | 13 | variable "GIT_COMMIT" { 14 | default = "" 15 | } 16 | 17 | target "default" { 18 | context = ".." 19 | dockerfile = "docker/Dockerfile" 20 | platforms = ["linux/amd64", "linux/arm64"] 21 | output = ["type=registry"] 22 | tags = [ 23 | "${REGISTRY}/${NAME}:${GIT_COMMIT}", 24 | "${REGISTRY}/${NAME}:${VERSION}", 25 | "${REGISTRY}/${NAME}:${split(".", VERSION)[0]}", 26 | "${REGISTRY}/${NAME}:${split(".", VERSION)[0]}.${split(".", VERSION)[1]}", 27 | "${REGISTRY}/${NAME}:latest", 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /config/rbac/plug_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view plugs. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: plug-viewer-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: integration-operator 10 | app.kubernetes.io/part-of: integration-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: plug-viewer-role 13 | rules: 14 | - apiGroups: 15 | - integration.rock8s.com 16 | resources: 17 | - plugs 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - apiGroups: 23 | - integration.rock8s.com 24 | resources: 25 | - plugs/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/socket_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view sockets. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: socket-viewer-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: integration-operator 10 | app.kubernetes.io/part-of: integration-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: socket-viewer-role 13 | rules: 14 | - apiGroups: 15 | - integration.rock8s.com 16 | resources: 17 | - sockets 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - apiGroups: 23 | - integration.rock8s.com 24 | resources: 25 | - sockets/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | - leader_election_role.yaml 11 | - leader_election_role_binding.yaml 12 | # Comment the following 4 lines if you want to disable 13 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 14 | # which protects your /metrics endpoint. 15 | - auth_proxy_service.yaml 16 | - auth_proxy_role.yaml 17 | - auth_proxy_role_binding.yaml 18 | - auth_proxy_client_clusterrole.yaml 19 | -------------------------------------------------------------------------------- /config/rbac/plug_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit plugs. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: plug-editor-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: integration-operator 10 | app.kubernetes.io/part-of: integration-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: plug-editor-role 13 | rules: 14 | - apiGroups: 15 | - integration.rock8s.com 16 | resources: 17 | - plugs 18 | verbs: 19 | - create 20 | - delete 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - apiGroups: 27 | - integration.rock8s.com 28 | resources: 29 | - plugs/status 30 | verbs: 31 | - get 32 | -------------------------------------------------------------------------------- /config/rbac/deferredresource_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view deferredresources. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: deferredresource-viewer-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: integration-operator 10 | app.kubernetes.io/part-of: integration-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: deferredresource-viewer-role 13 | rules: 14 | - apiGroups: 15 | - integration.rock8s.com 16 | resources: 17 | - deferredresources 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - apiGroups: 23 | - integration.rock8s.com 24 | resources: 25 | - deferredresources/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/socket_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit sockets. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: socket-editor-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: integration-operator 10 | app.kubernetes.io/part-of: integration-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: socket-editor-role 13 | rules: 14 | - apiGroups: 15 | - integration.rock8s.com 16 | resources: 17 | - sockets 18 | verbs: 19 | - create 20 | - delete 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - apiGroups: 27 | - integration.rock8s.com 28 | resources: 29 | - sockets/status 30 | verbs: 31 | - get 32 | -------------------------------------------------------------------------------- /config/rbac/deferredresource_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit deferredresources. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: deferredresource-editor-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: integration-operator 10 | app.kubernetes.io/part-of: integration-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: deferredresource-editor-role 13 | rules: 14 | - apiGroups: 15 | - integration.rock8s.com 16 | resources: 17 | - deferredresources 18 | verbs: 19 | - create 20 | - delete 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - apiGroups: 27 | - integration.rock8s.com 28 | resources: 29 | - deferredresources/status 30 | verbs: 31 | - get 32 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | app.kubernetes.io/name: servicemonitor 9 | app.kubernetes.io/instance: controller-manager-metrics-monitor 10 | app.kubernetes.io/component: metrics 11 | app.kubernetes.io/created-by: integration-operator 12 | app.kubernetes.io/part-of: integration-operator 13 | app.kubernetes.io/managed-by: kustomize 14 | name: controller-manager-metrics-monitor 15 | namespace: system 16 | spec: 17 | endpoints: 18 | - path: /metrics 19 | port: https 20 | scheme: https 21 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 22 | tlsConfig: 23 | insecureSkipVerify: true 24 | selector: 25 | matchLabels: 26 | control-plane: controller-manager 27 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: role 7 | app.kubernetes.io/instance: leader-election-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: integration-operator 10 | app.kubernetes.io/part-of: integration-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: leader-election-role 13 | rules: 14 | - apiGroups: 15 | - "" 16 | resources: 17 | - configmaps 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - create 23 | - update 24 | - patch 25 | - delete 26 | - apiGroups: 27 | - coordination.k8s.io 28 | resources: 29 | - leases 30 | verbs: 31 | - get 32 | - list 33 | - watch 34 | - create 35 | - update 36 | - patch 37 | - delete 38 | - apiGroups: 39 | - "" 40 | resources: 41 | - events 42 | verbs: 43 | - create 44 | - patch 45 | -------------------------------------------------------------------------------- /project-words.txt: -------------------------------------------------------------------------------- 1 | apiextensions 2 | apiextensionsv 3 | apiextv 4 | apimachinery 5 | apiserver 6 | automount 7 | bitspur 8 | cainjection 9 | certmanager 10 | clientgoscheme 11 | clientset 12 | configmap 13 | controllerutil 14 | corev 15 | crdkustomizecainjectionpatch 16 | crdkustomizeresource 17 | crdkustomizewebhookpatch 18 | crds 19 | crossplane 20 | customresourcedefinitions 21 | deepcopy 22 | deferredresource 23 | deferredresources 24 | devel 25 | fieldref 26 | finalizer 27 | finalizers 28 | gjson 29 | groupversion 30 | healthz 31 | integrationv 32 | intstr 33 | istio 34 | keycloak 35 | kubebuilder 36 | kustomization 37 | kustomize 38 | kustomizeconfig 39 | kyverno 40 | logr 41 | mediatype 42 | metav 43 | mkpm 44 | nerr 45 | objref 46 | oidc 47 | operatorframework 48 | protobuf 49 | readyz 50 | resid 51 | restmapper 52 | resty 53 | risser 54 | rolebinding 55 | serrors 56 | serviceaccount 57 | serviceaccounts 58 | servicemonitor 59 | sjson 60 | subresource 61 | tdewolff 62 | templating 63 | tidwall 64 | utilruntime 65 | -------------------------------------------------------------------------------- /coupler/main.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /coupler/main.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 15:18:21 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package coupler 28 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/integration.rock8s.com_sockets.yaml 6 | - bases/integration.rock8s.com_plugs.yaml 7 | - bases/integration.rock8s.com_deferredresources.yaml 8 | #+kubebuilder:scaffold:crdkustomizeresource 9 | 10 | patchesStrategicMerge: [] 11 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. 12 | # patches here are for enabling the conversion webhook for each CRD 13 | #- patches/webhook_in_sockets.yaml 14 | #- patches/webhook_in_plugs.yaml 15 | #- patches/webhook_in_deferredresources.yaml 16 | #+kubebuilder:scaffold:crdkustomizewebhookpatch 17 | 18 | # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. 19 | # patches here are for enabling the CA injection for each CRD 20 | #- patches/cainjection_in_sockets.yaml 21 | #- patches/cainjection_in_plugs.yaml 22 | #- patches/cainjection_in_deferredresources.yaml 23 | #+kubebuilder:scaffold:crdkustomizecainjectionpatch 24 | 25 | # the following config is for teaching kustomize how to do kustomization for CRDs. 26 | configurations: 27 | - kustomizeconfig.yaml 28 | -------------------------------------------------------------------------------- /config/manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # These resources constitute the fully configured set of manifests 2 | # used to generate the 'manifests/' directory in a bundle. 3 | resources: 4 | - bases/integration-operator.clusterserviceversion.yaml 5 | - ../default 6 | - ../samples 7 | - ../scorecard 8 | 9 | # [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix. 10 | # Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager. 11 | # These patches remove the unnecessary "cert" volume and its manager container volumeMount. 12 | #patchesJson6902: 13 | #- target: 14 | # group: apps 15 | # version: v1 16 | # kind: Deployment 17 | # name: controller-manager 18 | # namespace: system 19 | # patch: |- 20 | # # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs. 21 | # # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment. 22 | # - op: remove 23 | # path: /spec/template/spec/containers/1/volumeMounts/0 24 | # # Remove the "cert" volume, since OLM will create and mount a set of certs. 25 | # # Update the indices in this path if adding or removing volumes in the manager's Deployment. 26 | # - op: remove 27 | # path: /spec/template/spec/volumes/0 28 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | # Code generated by tool. DO NOT EDIT. 2 | # This file is used to track the info used to scaffold your project 3 | # and allow the plugins properly work. 4 | # More info: https://book.kubebuilder.io/reference/project-config.html 5 | domain: rock8s.com 6 | layout: 7 | - go.kubebuilder.io/v3 8 | plugins: 9 | manifests.sdk.operatorframework.io/v2: {} 10 | scorecard.sdk.operatorframework.io/v2: {} 11 | projectName: integration-operator 12 | repo: gitlab.com/bitspur/rock8s/integration-operator 13 | resources: 14 | - api: 15 | crdVersion: v1 16 | namespaced: true 17 | controller: true 18 | domain: rock8s.com 19 | group: integration 20 | kind: Socket 21 | path: gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1 22 | version: v1beta1 23 | - api: 24 | crdVersion: v1 25 | namespaced: true 26 | controller: true 27 | domain: rock8s.com 28 | group: integration 29 | kind: Plug 30 | path: gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1 31 | version: v1beta1 32 | - api: 33 | crdVersion: v1 34 | namespaced: true 35 | controller: true 36 | domain: rock8s.com 37 | group: integration 38 | kind: DeferredResource 39 | path: gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1 40 | version: v1beta1 41 | version: "3" 42 | -------------------------------------------------------------------------------- /config/scorecard/patches/olm.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - olm-bundle-validation 7 | image: quay.io/operator-framework/scorecard-test:v1.32.0 8 | labels: 9 | suite: olm 10 | test: olm-bundle-validation-test 11 | - op: add 12 | path: /stages/0/tests/- 13 | value: 14 | entrypoint: 15 | - scorecard-test 16 | - olm-crds-have-validation 17 | image: quay.io/operator-framework/scorecard-test:v1.32.0 18 | labels: 19 | suite: olm 20 | test: olm-crds-have-validation-test 21 | - op: add 22 | path: /stages/0/tests/- 23 | value: 24 | entrypoint: 25 | - scorecard-test 26 | - olm-crds-have-resources 27 | image: quay.io/operator-framework/scorecard-test:v1.32.0 28 | labels: 29 | suite: olm 30 | test: olm-crds-have-resources-test 31 | - op: add 32 | path: /stages/0/tests/- 33 | value: 34 | entrypoint: 35 | - scorecard-test 36 | - olm-spec-descriptors 37 | image: quay.io/operator-framework/scorecard-test:v1.32.0 38 | labels: 39 | suite: olm 40 | test: olm-spec-descriptors-test 41 | - op: add 42 | path: /stages/0/tests/- 43 | value: 44 | entrypoint: 45 | - scorecard-test 46 | - olm-status-descriptors 47 | image: quay.io/operator-framework/scorecard-test:v1.32.0 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the manager binary 2 | FROM golang:1.19 as builder 3 | ARG TARGETOS 4 | ARG TARGETARCH 5 | 6 | WORKDIR /workspace 7 | # Copy the Go Modules manifests 8 | COPY go.mod go.mod 9 | COPY go.sum go.sum 10 | # cache deps before building and copying source so that we don't need to re-download as much 11 | # and so that source changes don't invalidate our downloaded layer 12 | RUN go mod download 13 | 14 | # Copy the go source 15 | COPY main.go main.go 16 | COPY api/ api/ 17 | COPY controllers/ controllers/ 18 | COPY config/ config/ 19 | COPY coupler/ coupler/ 20 | COPY util/ util/ 21 | 22 | # Build 23 | # the GOARCH has not a default value to allow the binary be built according to the host where the command 24 | # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO 25 | # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, 26 | # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. 27 | RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager main.go 28 | 29 | # Use distroless as minimal base image to package the manager binary 30 | # Refer to https://github.com/GoogleContainerTools/distroless for more details 31 | FROM gcr.io/distroless/static:nonroot 32 | WORKDIR / 33 | COPY --from=builder /workspace/manager . 34 | USER 65532:65532 35 | 36 | ENTRYPOINT ["/manager"] 37 | -------------------------------------------------------------------------------- /config/main.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /config/main.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 14:02:00 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package config 28 | 29 | import ( 30 | "os" 31 | "time" 32 | 33 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 | ) 35 | 36 | var MaxRequeueDuration time.Duration = time.Duration(float64(time.Hour.Nanoseconds() * 6)) 37 | 38 | var StartTime metav1.Time = metav1.Now() 39 | 40 | var DebugPlugEndpoint = os.Getenv("DEBUG_PLUG_ENDPOINT") 41 | 42 | var DebugSocketEndpoint = os.Getenv("DEBUG_SOCKET_ENDPOINT") 43 | -------------------------------------------------------------------------------- /docker/Makefile: -------------------------------------------------------------------------------- 1 | # File: /docker/Makefile 2 | # Project: integration-operator 3 | # File Created: 13-10-2023 15:44:20 4 | # Author: Clay Risser 5 | # ----- 6 | # BitSpur (c) Copyright 2021 - 2023 7 | # 8 | # Licensed under the GNU Affero General Public License (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # https://www.gnu.org/licenses/agpl-3.0.en.html 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # 20 | # You can be released from the requirements of the license by purchasing 21 | # a commercial license. Buying such a license is mandatory as soon as you 22 | # develop commercial activities involving this software without disclosing 23 | # the source code of your own applications. 24 | 25 | include $(MKPM)/mkpm 26 | include $(MKPM)/gnu 27 | 28 | export DOCKER_COMPOSE := docker-compose 29 | export CONTEXT := .. 30 | export NAME := bitspur/rock8s/integration-operator 31 | export REGISTRY := registry.gitlab.com 32 | export VERSION := 1.2.0 33 | export GIT_COMMIT ?= $(shell git describe --tags --always --dirty 2>/dev/null) 34 | 35 | include $(MKPM)/docker 36 | 37 | BUILDX ?= $(call ternary,$(WHICH) buildx,buildx,$(call ternary,$(WHICH) docker-buildx,docker-buildx,$(DOCKER) buildx)) 38 | 39 | .PHONY: bake 40 | bake: 41 | @$(BUILDX) bake --provenance false 42 | -------------------------------------------------------------------------------- /Mkpmfile: -------------------------------------------------------------------------------- 1 | # File: /Mkpmfile 2 | # Project: integration-operator 3 | # File Created: 13-10-2023 15:40:24 4 | # Author: Clay Risser 5 | # ----- 6 | # BitSpur (c) Copyright 2021 - 2023 7 | # 8 | # Licensed under the GNU Affero General Public License (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # https://www.gnu.org/licenses/agpl-3.0.en.html 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # 20 | # You can be released from the requirements of the license by purchasing 21 | # a commercial license. Buying such a license is mandatory as soon as you 22 | # develop commercial activities involving this software without disclosing 23 | # the source code of your own applications. 24 | 25 | include $(MKPM)/mkpm 26 | include $(MKPM)/gnu 27 | 28 | CLOC ?= cloc 29 | 30 | .PHONY: of/% build generate manifests install uninstall start reinstall 31 | build: of/build 32 | dev: of/run 33 | generate: of/generate 34 | install: of/install 35 | manifests: generate of/manifests 36 | uninstall: of/uninstall 37 | reinstall: uninstall install 38 | of/%: 39 | @$(MAKE) -s $* 40 | 41 | .PHONY: count 42 | count: 43 | @$(CLOC) $(shell ($(GIT) ls-files && ($(GIT) lfs ls-files | $(CUT) -d' ' -f3)) | $(SORT) | $(UNIQ) -u) 44 | 45 | .PHONY: docker/% 46 | docker/%: 47 | @$(MAKE) -sC docker $* 48 | -------------------------------------------------------------------------------- /config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the 2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: controller-manager 7 | namespace: system 8 | spec: 9 | template: 10 | spec: 11 | affinity: 12 | nodeAffinity: 13 | requiredDuringSchedulingIgnoredDuringExecution: 14 | nodeSelectorTerms: 15 | - matchExpressions: 16 | - key: kubernetes.io/arch 17 | operator: In 18 | values: 19 | - amd64 20 | - arm64 21 | - ppc64le 22 | - s390x 23 | - key: kubernetes.io/os 24 | operator: In 25 | values: 26 | - linux 27 | containers: 28 | - name: kube-rbac-proxy 29 | securityContext: 30 | allowPrivilegeEscalation: false 31 | capabilities: 32 | drop: 33 | - "ALL" 34 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.1 35 | args: 36 | - "--secure-listen-address=0.0.0.0:8443" 37 | - "--upstream=http://127.0.0.1:8080/" 38 | - "--logtostderr=true" 39 | - "--v=0" 40 | ports: 41 | - containerPort: 8443 42 | protocol: TCP 43 | name: https 44 | resources: 45 | limits: 46 | cpu: 500m 47 | memory: 128Mi 48 | requests: 49 | cpu: 5m 50 | memory: 64Mi 51 | - name: manager 52 | args: 53 | - "--health-probe-bind-address=:8081" 54 | - "--metrics-bind-address=127.0.0.1:8080" 55 | - "--leader-elect" 56 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | # File: /.envrc 2 | # Project: integration-operator 3 | # File Created: 17-10-2023 10:51:29 4 | # Author: Clay Risser 5 | # ----- 6 | # BitSpur (c) Copyright 2021 - 2023 7 | # 8 | # Licensed under the GNU Affero General Public License (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # https://www.gnu.org/licenses/agpl-3.0.en.html 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # 20 | # You can be released from the requirements of the license by purchasing 21 | # a commercial license. Buying such a license is mandatory as soon as you 22 | # develop commercial activities involving this software without disclosing 23 | # the source code of your own applications. 24 | 25 | export GO_VERSION="$(cat go.mod | grep -E '^go ' | sed 's|^go ||g')" 26 | 27 | use_golang() { 28 | export GVM_PKGSET=$(pwd | sed 's|.*\/||g') 29 | [[ -s "$GVM_ROOT/scripts/gvm" ]] && source "$GVM_ROOT/scripts/gvm" 30 | if ! (gvm list | grep -q "$GO_VERSION"); then 31 | INSTALL_LOG=$(mktemp) 32 | gvm install go$GO_VERSION -B 2>&1 | tee $INSTALL_LOG 33 | cat $INSTALL_LOG 34 | if (cat $INSTALL_LOG | grep -q "ERROR: Binary Go unavailable for this platform"); then 35 | gvm install go$GO_VERSION 36 | fi 37 | rm -rf $INSTALL_LOG 2>/dev/null || true 38 | fi 39 | gvm use go$GO_VERSION 40 | if ! (gvm pkgset list | grep -q "$GVM_PKGSET"); then 41 | gvm pkgset create $GVM_PKGSET 42 | fi 43 | gvm pkgset use $GVM_PKGSET 44 | } 45 | 46 | use_golang 47 | -------------------------------------------------------------------------------- /api/v1beta1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /api/v1beta1/groupversion_info.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 10:50:35 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | // Package v1beta1 contains API Schema definitions for the integration v1beta1 API group 28 | // +kubebuilder:object:generate=true 29 | // +groupName=integration.rock8s.com 30 | package v1beta1 31 | 32 | import ( 33 | "k8s.io/apimachinery/pkg/runtime/schema" 34 | "sigs.k8s.io/controller-runtime/pkg/scheme" 35 | ) 36 | 37 | var ( 38 | // GroupVersion is group version used to register these objects 39 | GroupVersion = schema.GroupVersion{Group: "integration.rock8s.com", Version: "v1beta1"} 40 | 41 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 42 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 43 | 44 | // AddToScheme adds the types in this group-version to the given scheme. 45 | AddToScheme = SchemeBuilder.AddToScheme 46 | ) 47 | -------------------------------------------------------------------------------- /config/samples/integration_v1beta1_socket.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: integration.rock8s.com/v1beta1 2 | kind: Socket 3 | metadata: 4 | name: postgres 5 | namespace: postgres-namespace 6 | spec: 7 | interface: 8 | config: 9 | socket: 10 | protocol: 11 | required: true 12 | username: 13 | required: true 14 | password: 15 | required: true 16 | hostname: 17 | required: true 18 | port: 19 | required: true 20 | plug: 21 | database: 22 | required: true 23 | config: 24 | protocol: psql 25 | username: postgres 26 | hostname: postgres-service.postgres-namespace.svc.cluster.local 27 | port: "5432" 28 | configSecretName: postgres-secret 29 | resources: 30 | - when: [coupled, updated] 31 | do: recreate 32 | template: 33 | apiVersion: batch/v1 34 | kind: Job 35 | metadata: 36 | name: postgres-coupled-or-updated-{% .plug.metadata.namespace %} 37 | spec: 38 | template: 39 | spec: 40 | containers: 41 | - name: psql 42 | image: registry.gitlab.com/bitspur/rock8s/images/kube-commands-psql:0.0.1 43 | env: 44 | - name: POSTGRES_PROTOCOL 45 | value: "{% .socketConfig.protocol %}" 46 | - name: POSTGRES_USERNAME 47 | value: "{% .socketConfig.username %}" 48 | - name: POSTGRES_HOSTNAME 49 | value: "{% .socketConfig.hostname %}" 50 | - name: POSTGRES_PORT 51 | value: "{% .socketConfig.port %}" 52 | - name: POSTGRES_DATABASE 53 | value: "{% .plugConfig.database %}" 54 | - name: POSTGRES_PASSWORD 55 | value: "{% .socketConfig.password %}" 56 | command: 57 | - sh 58 | - -c 59 | - | 60 | export PGPASSFILE="/tmp/.pgpass" 61 | export STDOUT="/tmp/createdb.out" 62 | echo "*:*:*:*:$POSTGRES_PASSWORD" > $PGPASSFILE 63 | chmod 600 $PGPASSFILE 64 | createdb -h $POSTGRES_HOSTNAME -U $POSTGRES_USERNAME -p $POSTGRES_PORT -w $POSTGRES_DATABASE || true 65 | -------------------------------------------------------------------------------- /mkpm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | MKPM_VERSION="1.1.0" 4 | MKPM_SH_URL="${MKPM_SH_URL:-https://gitlab.com/api/v4/projects/48207162/packages/generic/mkpm/${MKPM_VERSION}/mkpm.sh}" 5 | alias download="$(curl --version >/dev/null 2>&1 && echo curl -Lo || echo wget -O)" 6 | alias echo="$([ "$(echo -e)" = "-e" ] && echo "echo" || echo "echo -e")" 7 | _SUPPORTS_COLORS=$( (which tput >/dev/null 2>&1 && [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]) && echo 1 || true) 8 | _CWD="$(pwd)" 9 | if [ "$_SUPPORTS_COLORS" = "1" ]; then 10 | export C_END='\033[0m' 11 | export C_RED='\033[31m' 12 | export C_YELLOW='\033[33m' 13 | fi 14 | _error() { echo "${C_RED}MKPM [E]:${C_END} $@" 1>&2; } 15 | _debug() { [ "$MKPM_DEBUG" = "1" ] && echo "${C_YELLOW}MKPM [D]:${C_END} $@" || true; } 16 | _project_root() { 17 | _ROOT="$1" 18 | if [ "$_ROOT" = "" ]; then 19 | _ROOT="$(pwd)" 20 | fi 21 | if [ -f "$_ROOT/mkpm.json" ]; then 22 | echo "$_ROOT" 23 | return 24 | fi 25 | _PARENT="$(echo "$_ROOT" | sed 's|\/[^\/]\+$||g')" 26 | if ([ "$_PARENT" = "" ] || [ "$_PARENT" = "/" ]); then 27 | echo "/" 28 | return 29 | fi 30 | echo "$(_project_root "$_PARENT")" 31 | return 32 | } 33 | _is_mkpm_proxy_required() { 34 | while test $# -gt 0; do 35 | case "$1" in 36 | -h | --help) 37 | return 1 38 | ;; 39 | -*) 40 | shift 41 | ;; 42 | v | version | init) 43 | return 1 44 | ;; 45 | *) 46 | break 47 | ;; 48 | esac 49 | done 50 | [ "$1" = "" ] && return 1 || true 51 | } 52 | export PROJECT_ROOT="$(_project_root)" 53 | if [ "$PROJECT_ROOT" = "/" ]; then 54 | if _is_mkpm_proxy_required "$@"; then 55 | _error "not an mkpm project" && exit 1 56 | else 57 | PROJECT_ROOT="$_CWD" 58 | fi 59 | fi 60 | MKPM_ROOT="$PROJECT_ROOT/.mkpm" 61 | MKPM="$MKPM_ROOT/mkpm" 62 | MKPM_BIN="$MKPM/.bin" 63 | if [ ! -f "$MKPM_BIN/mkpm" ]; then 64 | mkdir -p "$MKPM_BIN" 65 | if [ -f "$MKPM_ROOT/cache.tar.gz" ]; then 66 | mkdir -p "$MKPM" 67 | cd "$MKPM" 68 | tar -xzf "$MKPM_ROOT/cache.tar.gz" 69 | cd "$_CWD" 70 | _debug restored cache 71 | else 72 | download "$MKPM_BIN/mkpm" "$MKPM_SH_URL" >/dev/null 73 | _debug downloaded mkpm.sh 74 | fi 75 | chmod +x "$MKPM_BIN/mkpm" 76 | fi 77 | exec "$MKPM_BIN/mkpm" "$@" 78 | -------------------------------------------------------------------------------- /images/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 45 | 54 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | creationTimestamp: null 6 | name: manager-role 7 | rules: 8 | - apiGroups: 9 | - "" 10 | resources: 11 | - configmaps 12 | - secrets 13 | verbs: 14 | - get 15 | - list 16 | - watch 17 | - apiGroups: 18 | - "" 19 | resources: 20 | - events 21 | verbs: 22 | - create 23 | - delete 24 | - get 25 | - list 26 | - patch 27 | - update 28 | - watch 29 | - apiGroups: 30 | - "" 31 | resources: 32 | - pods 33 | - services 34 | verbs: 35 | - create 36 | - delete 37 | - get 38 | - list 39 | - patch 40 | - update 41 | - watch 42 | - apiGroups: 43 | - "" 44 | resources: 45 | - serviceaccounts 46 | verbs: 47 | - impersonate 48 | - apiGroups: 49 | - coordination.k8s.io 50 | resources: 51 | - leases 52 | verbs: 53 | - create 54 | - delete 55 | - get 56 | - list 57 | - patch 58 | - update 59 | - watch 60 | - apiGroups: 61 | - integration.rock8s.com 62 | resources: 63 | - deferredresources 64 | verbs: 65 | - create 66 | - delete 67 | - get 68 | - list 69 | - patch 70 | - update 71 | - watch 72 | - apiGroups: 73 | - integration.rock8s.com 74 | resources: 75 | - deferredresources/finalizers 76 | verbs: 77 | - update 78 | - apiGroups: 79 | - integration.rock8s.com 80 | resources: 81 | - deferredresources/status 82 | verbs: 83 | - get 84 | - patch 85 | - update 86 | - apiGroups: 87 | - integration.rock8s.com 88 | resources: 89 | - plugs 90 | verbs: 91 | - create 92 | - delete 93 | - get 94 | - list 95 | - patch 96 | - update 97 | - watch 98 | - apiGroups: 99 | - integration.rock8s.com 100 | resources: 101 | - plugs/finalizers 102 | verbs: 103 | - update 104 | - apiGroups: 105 | - integration.rock8s.com 106 | resources: 107 | - plugs/status 108 | verbs: 109 | - get 110 | - patch 111 | - update 112 | - apiGroups: 113 | - integration.rock8s.com 114 | resources: 115 | - sockets 116 | verbs: 117 | - create 118 | - delete 119 | - get 120 | - list 121 | - patch 122 | - update 123 | - watch 124 | - apiGroups: 125 | - integration.rock8s.com 126 | resources: 127 | - sockets/finalizers 128 | verbs: 129 | - update 130 | - apiGroups: 131 | - integration.rock8s.com 132 | resources: 133 | - sockets/status 134 | verbs: 135 | - get 136 | - patch 137 | - update 138 | -------------------------------------------------------------------------------- /chart/questions.yaml: -------------------------------------------------------------------------------- 1 | categories: 2 | - Server 3 | questions: 4 | # Config 5 | - variable: config.imagePullPolicy 6 | description: "" 7 | type: enum 8 | options: 9 | - IfNotPresent 10 | - Always 11 | required: true 12 | label: "pull policy" 13 | group: Config 14 | - variable: config.updateStrategy 15 | description: "" 16 | type: enum 17 | options: 18 | - RollingUpdate 19 | - Recreate 20 | - OnDelete 21 | required: true 22 | label: "update strategy" 23 | group: Config 24 | - variable: config.debug 25 | description: "" 26 | type: boolean 27 | required: true 28 | label: debug 29 | group: Config 30 | - variable: config.replicas 31 | description: "" 32 | type: int 33 | required: true 34 | label: "replicas" 35 | group: Config 36 | - variable: config.maxConcurrentReconciles 37 | description: "" 38 | type: int 39 | required: true 40 | label: "max concurrent reconciles" 41 | group: Config 42 | - variable: config.resourceBindingOperator.resources.enabled 43 | description: "" 44 | type: enum 45 | options: 46 | - defaults 47 | - custom 48 | - "false" 49 | required: true 50 | label: "resource binding operator resources enabled" 51 | show_subquestion_if: custom 52 | group: Config 53 | subquestions: 54 | - variable: config.resourceBindingOperator.resources.requests.cpu 55 | description: "" 56 | type: string 57 | required: true 58 | label: "resource binding operator resources requests cpu" 59 | - variable: config.resourceBindingOperator.resources.requests.memory 60 | description: "" 61 | type: string 62 | required: true 63 | label: "resource binding operator resources requests memory" 64 | - variable: config.resourceBindingOperator.resources.limits.cpu 65 | description: "" 66 | type: string 67 | required: true 68 | label: "resource binding operator resources limits cpu" 69 | - variable: config.resourceBindingOperator.resources.limits.memory 70 | description: "" 71 | type: string 72 | required: true 73 | label: "resource binding operator resources limits memory" 74 | 75 | # Images 76 | - variable: images.resourceBindingOperator.repository 77 | description: "" 78 | type: string 79 | required: true 80 | label: "resource binding operator repository" 81 | group: Images 82 | - variable: images.resourceBindingOperator.tag 83 | description: "" 84 | type: string 85 | required: true 86 | label: "resource binding operator tag" 87 | group: Images 88 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: rock8s-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: rock8s- 10 | 11 | # Labels to add to all resources and selectors. 12 | #commonLabels: 13 | # someName: someValue 14 | 15 | bases: 16 | - ../crd 17 | - ../rbac 18 | - ../manager 19 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 20 | # crd/kustomization.yaml 21 | #- ../webhook 22 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. 23 | #- ../certmanager 24 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 25 | #- ../prometheus 26 | 27 | patchesStrategicMerge: 28 | # Protect the /metrics endpoint by putting it behind auth. 29 | # If you want your controller-manager to expose the /metrics 30 | # endpoint w/o any authn/z, please comment the following line. 31 | - manager_auth_proxy_patch.yaml 32 | 33 | 34 | 35 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 36 | # crd/kustomization.yaml 37 | #- manager_webhook_patch.yaml 38 | 39 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 40 | # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. 41 | # 'CERTMANAGER' needs to be enabled to use ca injection 42 | #- webhookcainjection_patch.yaml 43 | 44 | # the following config is for teaching kustomize how to do var substitution 45 | vars: [] 46 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. 47 | #- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR 48 | # objref: 49 | # kind: Certificate 50 | # group: cert-manager.io 51 | # version: v1 52 | # name: serving-cert # this name should match the one in certificate.yaml 53 | # fieldref: 54 | # fieldpath: metadata.namespace 55 | #- name: CERTIFICATE_NAME 56 | # objref: 57 | # kind: Certificate 58 | # group: cert-manager.io 59 | # version: v1 60 | # name: serving-cert # this name should match the one in certificate.yaml 61 | #- name: SERVICE_NAMESPACE # namespace of the service 62 | # objref: 63 | # kind: Service 64 | # version: v1 65 | # name: webhook-service 66 | # fieldref: 67 | # fieldpath: metadata.namespace 68 | #- name: SERVICE_NAME 69 | # objref: 70 | # kind: Service 71 | # version: v1 72 | # name: webhook-service 73 | -------------------------------------------------------------------------------- /coupler/decouple.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /coupler/decouple.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 18:17:21 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package coupler 28 | 29 | import ( 30 | "context" 31 | 32 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 33 | "gitlab.com/bitspur/rock8s/integration-operator/util" 34 | "k8s.io/client-go/tools/record" 35 | ctrl "sigs.k8s.io/controller-runtime" 36 | "sigs.k8s.io/controller-runtime/pkg/client" 37 | ) 38 | 39 | func Decouple( 40 | client *client.Client, 41 | ctx context.Context, 42 | req *ctrl.Request, 43 | plugUtil *util.PlugUtil, 44 | socketUtil *util.SocketUtil, 45 | plug *integrationv1beta1.Plug, 46 | socket *integrationv1beta1.Socket, 47 | recorder record.EventRecorder, 48 | ) error { 49 | configUtil := util.NewConfigUtil(ctx) 50 | if plug == nil { 51 | var err error 52 | plug, err = plugUtil.Get() 53 | if err != nil { 54 | return err 55 | } 56 | } 57 | if socket == nil { 58 | var err error 59 | socket, err = socketUtil.Get() 60 | if err != nil { 61 | return err 62 | } 63 | } 64 | 65 | plugConfig, err := configUtil.GetPlugConfig(plug, socket) 66 | if err != nil { 67 | return err 68 | } 69 | socketConfig, err := configUtil.GetSocketConfig(plug, socket) 70 | if err != nil { 71 | socketUtil.Error(err, socket) 72 | return err 73 | } 74 | 75 | if err := DecoupledPlug(plug, socket, plugConfig, socketConfig, recorder); err != nil { 76 | return err 77 | } 78 | if err := DecoupledSocket(plug, socket, plugConfig, socketConfig, recorder); err != nil { 79 | socketUtil.Error(err, socket) 80 | return err 81 | } 82 | 83 | if _, err := socketUtil.UpdateRemoveCoupledPlugStatus(plug.UID, socket, false); err != nil { 84 | return err 85 | } 86 | return nil 87 | } 88 | -------------------------------------------------------------------------------- /coupler/update.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /coupler/update.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 16:09:52 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package coupler 28 | 29 | import ( 30 | "context" 31 | 32 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 33 | "gitlab.com/bitspur/rock8s/integration-operator/util" 34 | "k8s.io/client-go/tools/record" 35 | ctrl "sigs.k8s.io/controller-runtime" 36 | "sigs.k8s.io/controller-runtime/pkg/client" 37 | ) 38 | 39 | func Update( 40 | client *client.Client, 41 | ctx context.Context, 42 | req *ctrl.Request, 43 | plugUtil *util.PlugUtil, 44 | socketUtil *util.SocketUtil, 45 | plug *integrationv1beta1.Plug, 46 | socket *integrationv1beta1.Socket, 47 | recorder record.EventRecorder, 48 | ) error { 49 | configUtil := util.NewConfigUtil(ctx) 50 | if plug == nil { 51 | var err error 52 | plug, err = plugUtil.Get() 53 | if err != nil { 54 | return err 55 | } 56 | } 57 | if socket == nil { 58 | var err error 59 | socket, err = socketUtil.Get() 60 | if err != nil { 61 | return err 62 | } 63 | } 64 | 65 | if !socketUtil.CoupledPlugExists(socket.Status.CoupledPlugs, plug.UID) || plug.Status.CoupledSocket == nil { 66 | return nil 67 | } 68 | 69 | plugConfig, err := configUtil.GetPlugConfig(plug, socket) 70 | if err != nil { 71 | return err 72 | } 73 | socketConfig, err := configUtil.GetSocketConfig(plug, socket) 74 | if err != nil { 75 | socketUtil.Error(err, socket) 76 | return err 77 | } 78 | 79 | if err = UpdatedPlug(plug, socket, plugConfig, socketConfig, recorder); err != nil { 80 | return err 81 | } 82 | if err = UpdatedSocket(plug, socket, plugConfig, socketConfig, recorder); err != nil { 83 | socketUtil.Error(err, socket) 84 | return err 85 | } 86 | 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /util/main.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /util/main.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 13:49:54 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package util 28 | 29 | import ( 30 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 31 | "k8s.io/apimachinery/pkg/runtime/serializer/yaml" 32 | ) 33 | 34 | var ( 35 | decUnstructured = yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) 36 | ) 37 | 38 | type ConditionCoupledReason string 39 | 40 | const ( 41 | CouplingInProcess ConditionCoupledReason = "CouplingInProcess" 42 | CouplingSucceeded ConditionCoupledReason = "CouplingSucceeded" 43 | Error ConditionCoupledReason = "Error" 44 | PlugCreated ConditionCoupledReason = "PlugCreated" 45 | SocketCoupled ConditionCoupledReason = "SocketCoupled" 46 | SocketCreated ConditionCoupledReason = "SocketCreated" 47 | SocketEmpty ConditionCoupledReason = "SocketEmpty" 48 | SocketNotCreated ConditionCoupledReason = "SocketNotCreated" 49 | UpdatingInProcess ConditionCoupledReason = "UpdatingInProcess" 50 | ) 51 | 52 | type ConditionType string 53 | 54 | const ( 55 | ConditionTypeCoupled ConditionType = "Coupled" 56 | ConditionTypeFailed ConditionType = "Failed" 57 | ) 58 | 59 | type DeferredResourceConditionResolvedReason string 60 | 61 | const ( 62 | DeferredResourcePending DeferredResourceConditionResolvedReason = "Pending" 63 | DeferredResourceError DeferredResourceConditionResolvedReason = "Error" 64 | DeferredResourceSuccess DeferredResourceConditionResolvedReason = "Success" 65 | ) 66 | 67 | type DeferredResourceConditionType string 68 | 69 | const ( 70 | DeferredResourceConditionTypeResolved ConditionType = "Resolved" 71 | DeferredResourceConditionTypeFailed ConditionType = "Failed" 72 | ) 73 | 74 | type Config map[string]string 75 | 76 | type Result map[string]string 77 | -------------------------------------------------------------------------------- /settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "psi-header.variables": [["projectCreationYear", "2021"]], 3 | "psi-header.config": { 4 | "blankLinesAfter": 1, 5 | "company": "BitSpur", 6 | "copyrightHolder": "BitSpur", 7 | "creationDateZero": "asIs", 8 | "forceToTop": true, 9 | "initials": "CR", 10 | "license": "Custom" 11 | }, 12 | "psi-header.license-text": [ 13 | " Licensed under the Apache License, Version 2.0 (the \"License\");", 14 | " you may not use this file except in compliance with the License.", 15 | " You may obtain a copy of the License at", 16 | "", 17 | " http://www.apache.org/licenses/LICENSE-2.0", 18 | "", 19 | " Unless required by applicable law or agreed to in writing, software", 20 | " distributed under the License is distributed on an \"AS IS\" BASIS,", 21 | " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", 22 | " See the License for the specific language governing permissions and", 23 | " limitations under the License." 24 | ], 25 | "psi-header.templates": [ 26 | { 27 | "language": "*", 28 | "template": [ 29 | " File: <>", 30 | " Project: <>", 31 | " File Created: <>", 32 | " Author: <>", 33 | " -----", 34 | " <> (c) Copyright <>, now)>>", 35 | "", 36 | "<>" 37 | ] 38 | } 39 | ], 40 | "psi-header.lang-config": [ 41 | { 42 | "language": "*", 43 | "begin": "/**", 44 | "blankLinesAfter": 1, 45 | "end": " */", 46 | "forceToTop": true, 47 | "lineLength": 80, 48 | "modAuthor": "Modified By:", 49 | "modDate": "Last Modified:", 50 | "modDateFormat": "dd-MM-yyyy HH:mm:ss", 51 | "prefix": " *", 52 | "replace": ["File:"], 53 | "rootDirFileName": "LICENSE", 54 | "suffix": "" 55 | }, 56 | { 57 | "language": "makefile", 58 | "begin": "", 59 | "blankLinesAfter": 1, 60 | "end": "", 61 | "forceToTop": true, 62 | "lineLength": 80, 63 | "modAuthor": "Modified By:", 64 | "modDate": "Last Modified:", 65 | "modDateFormat": "dd-MM-yyyy HH:mm:ss", 66 | "prefix": "#", 67 | "replace": ["File:"], 68 | "rootDirFileName": "LICENSE", 69 | "suffix": "" 70 | } 71 | ], 72 | "psi-header.changes-tracking": { 73 | "autoHeader": "autoSave", 74 | "enforceHeader": true, 75 | "include": ["javascript", "makefile", "typescript", "typescriptreact"], 76 | "isActive": true, 77 | "modAuthor": "Modified By:", 78 | "modDate": "Last Modified:", 79 | "modDateFormat": "dd-MM-yyyy hh:nn:ss", 80 | "replace": ["Filename:"], 81 | "updateLicenseVariables": true 82 | }, 83 | "psi-header.license-reference": { 84 | "uri": "../LICENSE", 85 | "uriIsLocalFile": true 86 | }, 87 | "files.associations": { 88 | "Mkpmfile": "makefile" 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /controllers/suite_test.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /controllers/suite_test.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 10:50:35 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package controllers 28 | 29 | import ( 30 | "path/filepath" 31 | "testing" 32 | 33 | . "github.com/onsi/ginkgo/v2" 34 | . "github.com/onsi/gomega" 35 | 36 | "k8s.io/client-go/kubernetes/scheme" 37 | "k8s.io/client-go/rest" 38 | "sigs.k8s.io/controller-runtime/pkg/client" 39 | "sigs.k8s.io/controller-runtime/pkg/envtest" 40 | logf "sigs.k8s.io/controller-runtime/pkg/log" 41 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 42 | 43 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 44 | //+kubebuilder:scaffold:imports 45 | ) 46 | 47 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to 48 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 49 | 50 | var cfg *rest.Config 51 | var k8sClient client.Client 52 | var testEnv *envtest.Environment 53 | 54 | func TestAPIs(t *testing.T) { 55 | RegisterFailHandler(Fail) 56 | 57 | RunSpecs(t, "Controller Suite") 58 | } 59 | 60 | var _ = BeforeSuite(func() { 61 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) 62 | 63 | By("bootstrapping test environment") 64 | testEnv = &envtest.Environment{ 65 | CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, 66 | ErrorIfCRDPathMissing: true, 67 | } 68 | 69 | var err error 70 | // cfg is defined in this file globally. 71 | cfg, err = testEnv.Start() 72 | Expect(err).NotTo(HaveOccurred()) 73 | Expect(cfg).NotTo(BeNil()) 74 | 75 | err = integrationv1beta1.AddToScheme(scheme.Scheme) 76 | Expect(err).NotTo(HaveOccurred()) 77 | 78 | //+kubebuilder:scaffold:scheme 79 | 80 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 81 | Expect(err).NotTo(HaveOccurred()) 82 | Expect(k8sClient).NotTo(BeNil()) 83 | 84 | }) 85 | 86 | var _ = AfterSuite(func() { 87 | By("tearing down the test environment") 88 | err := testEnv.Stop() 89 | Expect(err).NotTo(HaveOccurred()) 90 | }) 91 | -------------------------------------------------------------------------------- /coupler/plug.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /coupler/plug.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 15:20:41 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package coupler 28 | 29 | import ( 30 | "context" 31 | 32 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 33 | "gitlab.com/bitspur/rock8s/integration-operator/util" 34 | "k8s.io/client-go/tools/record" 35 | ) 36 | 37 | func CreatedPlug( 38 | plug *integrationv1beta1.Plug, 39 | recorder record.EventRecorder, 40 | ) error { 41 | ctx, cancel := context.WithCancel(context.Background()) 42 | defer cancel() 43 | eventUtil := util.NewEventUtil(ctx) 44 | return eventUtil.PlugCreated(plug, recorder) 45 | } 46 | 47 | func DeletedPlug( 48 | plug *integrationv1beta1.Plug, 49 | recorder record.EventRecorder, 50 | ) error { 51 | ctx, cancel := context.WithCancel(context.Background()) 52 | defer cancel() 53 | eventUtil := util.NewEventUtil(ctx) 54 | return eventUtil.PlugDeleted(plug, recorder) 55 | } 56 | 57 | func CoupledPlug( 58 | plug *integrationv1beta1.Plug, 59 | socket *integrationv1beta1.Socket, 60 | plugConfig util.Config, 61 | socketConfig util.Config, 62 | recorder record.EventRecorder, 63 | ) error { 64 | ctx, cancel := context.WithCancel(context.Background()) 65 | defer cancel() 66 | eventUtil := util.NewEventUtil(ctx) 67 | return eventUtil.PlugCoupled(plug, socket, &plugConfig, &socketConfig, recorder) 68 | } 69 | 70 | func UpdatedPlug( 71 | plug *integrationv1beta1.Plug, 72 | socket *integrationv1beta1.Socket, 73 | plugConfig util.Config, 74 | socketConfig util.Config, 75 | recorder record.EventRecorder, 76 | ) error { 77 | ctx, cancel := context.WithCancel(context.Background()) 78 | defer cancel() 79 | eventUtil := util.NewEventUtil(ctx) 80 | return eventUtil.PlugUpdated(plug, socket, &plugConfig, &socketConfig, recorder) 81 | } 82 | 83 | func DecoupledPlug( 84 | plug *integrationv1beta1.Plug, 85 | socket *integrationv1beta1.Socket, 86 | plugConfig util.Config, 87 | socketConfig util.Config, 88 | recorder record.EventRecorder, 89 | ) error { 90 | ctx, cancel := context.WithCancel(context.Background()) 91 | defer cancel() 92 | eventUtil := util.NewEventUtil(ctx) 93 | return eventUtil.PlugDecoupled(plug, socket, &plugConfig, &socketConfig, recorder) 94 | } 95 | -------------------------------------------------------------------------------- /chart/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ template "integration-operator.name" . }} 5 | labels: 6 | app.kubernetes.io/name: {{ template "integration-operator.name" . }} 7 | helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} 8 | app.kubernetes.io/instance: {{ .Release.Name }} 9 | app.kubernetes.io/managed-by: {{ .Release.Service }} 10 | spec: 11 | selector: 12 | matchLabels: 13 | app.kubernetes.io/name: {{ template "integration-operator.name" . }} 14 | app.kubernetes.io/instance: {{ .Release.Name }} 15 | replicas: {{ .Values.config.replicas }} 16 | strategy: 17 | type: {{ .Values.config.updateStrategy }} 18 | template: 19 | metadata: 20 | labels: 21 | app.kubernetes.io/name: {{ template "integration-operator.name" . }} 22 | app.kubernetes.io/instance: {{ .Release.Name }} 23 | spec: 24 | serviceAccountName: {{ template "integration-operator.name" . }} 25 | affinity: 26 | nodeAffinity: 27 | requiredDuringSchedulingIgnoredDuringExecution: 28 | nodeSelectorTerms: 29 | - matchExpressions: 30 | - key: kubernetes.io/arch 31 | operator: In 32 | values: 33 | - amd64 34 | containers: 35 | - image: {{ .Values.images.resourceBindingOperator.repository }}:{{ .Values.images.resourceBindingOperator.tag }} 36 | imagePullPolicy: {{ .Values.config.imagePullPolicy }} 37 | name: {{ template "integration-operator.name" . }} 38 | args: 39 | - '--leader-elect' 40 | - '--health-probe-bind-address=:8081' 41 | - '--zap-devel={{ .Values.config.debug | ternary "true" "false" }}' 42 | {{- if (and .Values.config.resourceBindingOperator.resources.enabled (not (eq .Values.config.resourceBindingOperator.resources.enabled "false"))) }} 43 | resources: 44 | requests: 45 | {{ toYaml .Values.config.resourceBindingOperator.resources.requests | indent 14 }} 46 | limits: 47 | {{ toYaml .Values.config.resourceBindingOperator.resources.limits | indent 14 }} 48 | {{- else }} 49 | resources: {} 50 | {{- end }} 51 | env: 52 | - name: WATCH_NAMESPACE 53 | valueFrom: 54 | fieldRef: 55 | fieldPath: "metadata.annotations['olm.targetNamespaces']" 56 | - name: POD_NAME 57 | valueFrom: 58 | fieldRef: 59 | fieldPath: metadata.name 60 | - name: OPERATOR_NAME 61 | value: integration-operator 62 | - name: MAX_CONCURRENT_RECONCILES 63 | value: {{ .Values.config.maxConcurrentReconciles | quote }} 64 | nodeSelector: 65 | beta.kubernetes.io/os: linux 66 | livenessProbe: 67 | httpGet: 68 | path: /healthz 69 | port: 8081 70 | initialDelaySeconds: 15 71 | periodSeconds: 20 72 | readinessProbe: 73 | httpGet: 74 | path: /readyz 75 | port: 8081 76 | initialDelaySeconds: 5 77 | periodSeconds: 10 78 | -------------------------------------------------------------------------------- /coupler/socket.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /coupler/socket.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 15:20:41 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package coupler 28 | 29 | import ( 30 | "context" 31 | 32 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 33 | "gitlab.com/bitspur/rock8s/integration-operator/util" 34 | "k8s.io/client-go/tools/record" 35 | ) 36 | 37 | func CreatedSocket( 38 | socket *integrationv1beta1.Socket, 39 | recorder record.EventRecorder, 40 | ) error { 41 | ctx, cancel := context.WithCancel(context.Background()) 42 | defer cancel() 43 | eventUtil := util.NewEventUtil(ctx) 44 | return eventUtil.SocketCreated(socket, recorder) 45 | } 46 | 47 | func DeletedSocket( 48 | socket *integrationv1beta1.Socket, 49 | recorder record.EventRecorder, 50 | ) error { 51 | ctx, cancel := context.WithCancel(context.Background()) 52 | defer cancel() 53 | eventUtil := util.NewEventUtil(ctx) 54 | return eventUtil.SocketDeleted(socket, recorder) 55 | } 56 | 57 | func CoupledSocket( 58 | plug *integrationv1beta1.Plug, 59 | socket *integrationv1beta1.Socket, 60 | plugConfig util.Config, 61 | socketConfig util.Config, 62 | recorder record.EventRecorder, 63 | ) error { 64 | ctx, cancel := context.WithCancel(context.Background()) 65 | defer cancel() 66 | eventUtil := util.NewEventUtil(ctx) 67 | return eventUtil.SocketCoupled(plug, socket, &plugConfig, &socketConfig, recorder) 68 | } 69 | 70 | func UpdatedSocket( 71 | plug *integrationv1beta1.Plug, 72 | socket *integrationv1beta1.Socket, 73 | plugConfig util.Config, 74 | socketConfig util.Config, 75 | recorder record.EventRecorder, 76 | ) error { 77 | ctx, cancel := context.WithCancel(context.Background()) 78 | defer cancel() 79 | eventUtil := util.NewEventUtil(ctx) 80 | return eventUtil.SocketUpdated(plug, socket, &plugConfig, &socketConfig, recorder) 81 | } 82 | 83 | func DecoupledSocket( 84 | plug *integrationv1beta1.Plug, 85 | socket *integrationv1beta1.Socket, 86 | plugConfig util.Config, 87 | socketConfig util.Config, 88 | recorder record.EventRecorder, 89 | ) error { 90 | ctx, cancel := context.WithCancel(context.Background()) 91 | defer cancel() 92 | eventUtil := util.NewEventUtil(ctx) 93 | return eventUtil.SocketDecoupled(plug, socket, &plugConfig, &socketConfig, recorder) 94 | } 95 | -------------------------------------------------------------------------------- /util/data.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /util/data.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 13:49:54 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package util 28 | 29 | import ( 30 | "context" 31 | 32 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 33 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 | "k8s.io/client-go/kubernetes" 35 | ctrl "sigs.k8s.io/controller-runtime" 36 | ) 37 | 38 | type DataUtil struct { 39 | client *kubernetes.Clientset 40 | ctx context.Context 41 | } 42 | 43 | func NewDataUtil(ctx context.Context) *DataUtil { 44 | return &DataUtil{ 45 | client: kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie()), 46 | } 47 | } 48 | 49 | func (u *DataUtil) GetPlugData(plug *integrationv1beta1.Plug) (map[string]string, error) { 50 | plugData := make(map[string]string) 51 | if plug.Spec.DataSecretName != "" { 52 | secret, err := u.client.CoreV1().Secrets(plug.Namespace).Get( 53 | u.ctx, 54 | plug.Spec.DataSecretName, 55 | metav1.GetOptions{}, 56 | ) 57 | if err != nil { 58 | return nil, err 59 | } 60 | for key, value := range secret.Data { 61 | plugData[key] = string(value) 62 | } 63 | } 64 | if plug.Spec.Data != nil { 65 | for key, value := range plug.Spec.Data { 66 | plugData[key] = value 67 | } 68 | } 69 | if plug.Spec.ConfigConfigMapName != "" { 70 | configMap, err := u.client.CoreV1().ConfigMaps(plug.Namespace).Get( 71 | u.ctx, 72 | plug.Spec.ConfigConfigMapName, 73 | metav1.GetOptions{}, 74 | ) 75 | if err != nil { 76 | return nil, err 77 | } 78 | for key, value := range configMap.Data { 79 | plugData[key] = value 80 | } 81 | } 82 | return plugData, nil 83 | } 84 | 85 | func (u *DataUtil) GetSocketData(socket *integrationv1beta1.Socket) (map[string]string, error) { 86 | socketData := make(map[string]string) 87 | if socket.Spec.DataSecretName != "" { 88 | secret, err := u.client.CoreV1().Secrets(socket.Namespace).Get( 89 | u.ctx, 90 | socket.Spec.DataSecretName, 91 | metav1.GetOptions{}, 92 | ) 93 | if err != nil { 94 | return nil, err 95 | } 96 | for key, value := range secret.Data { 97 | socketData[key] = string(value) 98 | } 99 | } 100 | if socket.Spec.Data != nil { 101 | for key, value := range socket.Spec.Data { 102 | socketData[key] = value 103 | } 104 | } 105 | if socket.Spec.ConfigConfigMapName != "" { 106 | configMap, err := u.client.CoreV1().ConfigMaps(socket.Namespace).Get( 107 | u.ctx, 108 | socket.Spec.DataConfigMapName, 109 | metav1.GetOptions{}, 110 | ) 111 | if err != nil { 112 | return nil, err 113 | } 114 | for key, value := range configMap.Data { 115 | socketData[key] = value 116 | } 117 | } 118 | return socketData, nil 119 | } 120 | -------------------------------------------------------------------------------- /api/v1beta1/deferredresource_types.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /api/v1beta1/deferredresource_types.go 3 | * Project: integration-operator 4 | * File Created: 17-12-2023 11:14:58 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package v1beta1 28 | 29 | import ( 30 | apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 31 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 | "sigs.k8s.io/kustomize/api/resid" 33 | ) 34 | 35 | // DeferredResourceSpec defines the desired state of DeferredResource 36 | type DeferredResourceSpec struct { 37 | // Timeout is the maximum time to wait before creating the resource 38 | Timeout int64 `json:"timeout,omitempty"` 39 | 40 | // WaitFor is a list of resources to wait for before creating the resource 41 | WaitFor *[]*WaitForTarget `json:"waitFor,omitempty"` 42 | 43 | // Resource is the resource to create after the defer is resolved 44 | Resource *apiextv1.JSON `json:"resource,omitempty"` 45 | // ServiceAccountName is the name of the ServiceAccount to use to create deferred resources from. 46 | // More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ 47 | // +optional 48 | ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"bytes,8,opt,name=serviceAccountName"` 49 | } 50 | 51 | // DeferredResourceStatus defines the observed state of DeferredResource 52 | type DeferredResourceStatus struct { 53 | Conditions []metav1.Condition `json:"conditions,omitempty"` 54 | OwnerReference metav1.OwnerReference `json:"ownerReferences,omitempty"` 55 | } 56 | 57 | //+kubebuilder:object:root=true 58 | //+kubebuilder:subresource:status 59 | 60 | // DeferredResource is the Schema for the deferredresources API 61 | type DeferredResource struct { 62 | metav1.TypeMeta `json:",inline"` 63 | metav1.ObjectMeta `json:"metadata,omitempty"` 64 | 65 | Spec DeferredResourceSpec `json:"spec,omitempty"` 66 | Status DeferredResourceStatus `json:"status,omitempty"` 67 | } 68 | 69 | //+kubebuilder:object:root=true 70 | 71 | // DeferredResourceList contains a list of DeferredResource 72 | type DeferredResourceList struct { 73 | metav1.TypeMeta `json:",inline"` 74 | metav1.ListMeta `json:"metadata,omitempty"` 75 | Items []DeferredResource `json:"items"` 76 | } 77 | 78 | // Target refers to a kubernetes object by Group, Version, Kind and Name 79 | // gvk.Gvk contains Group, Version and Kind 80 | // APIVersion is added to keep the backward compatibility of using ObjectReference 81 | // for Var.ObjRef 82 | type WaitForTarget struct { 83 | resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"` 84 | APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` 85 | Name string `json:"name,omitempty" yaml:"name,omitempty"` 86 | } 87 | 88 | func init() { 89 | SchemeBuilder.Register(&DeferredResource{}, &DeferredResourceList{}) 90 | } 91 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "psi-header.variables": [["projectCreationYear", "2021"]], 3 | "psi-header.config": { 4 | "blankLinesAfter": 1, 5 | "company": "BitSpur", 6 | "copyrightHolder": "BitSpur", 7 | "creationDateZero": "asIs", 8 | "forceToTop": true, 9 | "initials": "CR", 10 | "license": "Custom" 11 | }, 12 | "psi-header.license-text": [ 13 | " Licensed under the GNU Affero General Public License (the \"License\");", 14 | " you may not use this file except in compliance with the License.", 15 | " You may obtain a copy of the License at", 16 | "", 17 | " https://www.gnu.org/licenses/agpl-3.0.en.html", 18 | "", 19 | " Unless required by applicable law or agreed to in writing, software", 20 | " distributed under the License is distributed on an \"AS IS\" BASIS,", 21 | " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", 22 | " See the License for the specific language governing permissions and", 23 | " limitations under the License.", 24 | "", 25 | " You can be released from the requirements of the license by purchasing", 26 | " a commercial license. Buying such a license is mandatory as soon as you", 27 | " develop commercial activities involving this software without disclosing", 28 | " the source code of your own applications." 29 | ], 30 | "psi-header.templates": [ 31 | { 32 | "language": "*", 33 | "template": [ 34 | " File: <>", 35 | " Project: <>", 36 | " File Created: <>", 37 | " Author: <>", 38 | " -----", 39 | " <> (c) Copyright <>, now)>>", 40 | "", 41 | "<>" 42 | ] 43 | } 44 | ], 45 | "psi-header.lang-config": [ 46 | { 47 | "language": "*", 48 | "begin": "/**", 49 | "blankLinesAfter": 1, 50 | "end": " */", 51 | "forceToTop": true, 52 | "lineLength": 80, 53 | "modAuthor": "Modified By:", 54 | "modDate": "Last Modified:", 55 | "modDateFormat": "dd-MM-yyyy HH:mm:ss", 56 | "prefix": " *", 57 | "replace": ["File:"], 58 | "rootDirFileName": "LICENSE", 59 | "suffix": "" 60 | }, 61 | { 62 | "language": "makefile", 63 | "begin": "", 64 | "blankLinesAfter": 1, 65 | "end": "", 66 | "forceToTop": true, 67 | "lineLength": 80, 68 | "modAuthor": "Modified By:", 69 | "modDate": "Last Modified:", 70 | "modDateFormat": "dd-MM-yyyy HH:mm:ss", 71 | "prefix": "#", 72 | "replace": ["File:"], 73 | "rootDirFileName": "LICENSE", 74 | "suffix": "" 75 | }, 76 | { 77 | "language": "shellscript", 78 | "begin": "", 79 | "blankLinesAfter": 1, 80 | "end": "", 81 | "forceToTop": true, 82 | "lineLength": 80, 83 | "modAuthor": "Modified By:", 84 | "modDate": "Last Modified:", 85 | "modDateFormat": "dd-MM-yyyy HH:mm:ss", 86 | "prefix": "#", 87 | "replace": ["File:"], 88 | "rootDirFileName": "LICENSE", 89 | "suffix": "" 90 | } 91 | ], 92 | "psi-header.changes-tracking": { 93 | "autoHeader": "autoSave", 94 | "enforceHeader": true, 95 | "include": ["go", "makefile", "shellscript"], 96 | "isActive": true, 97 | "modAuthor": "Modified By:", 98 | "modDate": "Last Modified:", 99 | "modDateFormat": "dd-MM-yyyy hh:nn:ss", 100 | "replace": ["Filename:"], 101 | "updateLicenseVariables": true 102 | }, 103 | "psi-header.license-reference": { 104 | "uri": "../LICENSE", 105 | "uriIsLocalFile": true 106 | }, 107 | "files.associations": { 108 | "Mkpmfile": "makefile" 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | app.kubernetes.io/name: namespace 7 | app.kubernetes.io/instance: system 8 | app.kubernetes.io/component: manager 9 | app.kubernetes.io/created-by: integration-operator 10 | app.kubernetes.io/part-of: integration-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: system 13 | --- 14 | apiVersion: apps/v1 15 | kind: Deployment 16 | metadata: 17 | name: controller-manager 18 | namespace: system 19 | labels: 20 | control-plane: controller-manager 21 | app.kubernetes.io/name: deployment 22 | app.kubernetes.io/instance: controller-manager 23 | app.kubernetes.io/component: manager 24 | app.kubernetes.io/created-by: integration-operator 25 | app.kubernetes.io/part-of: integration-operator 26 | app.kubernetes.io/managed-by: kustomize 27 | spec: 28 | selector: 29 | matchLabels: 30 | control-plane: controller-manager 31 | replicas: 1 32 | template: 33 | metadata: 34 | annotations: 35 | kubectl.kubernetes.io/default-container: manager 36 | labels: 37 | control-plane: controller-manager 38 | spec: 39 | # TODO(user): Uncomment the following code to configure the nodeAffinity expression 40 | # according to the platforms which are supported by your solution. 41 | # It is considered best practice to support multiple architectures. You can 42 | # build your manager image using the makefile target docker-buildx. 43 | # affinity: 44 | # nodeAffinity: 45 | # requiredDuringSchedulingIgnoredDuringExecution: 46 | # nodeSelectorTerms: 47 | # - matchExpressions: 48 | # - key: kubernetes.io/arch 49 | # operator: In 50 | # values: 51 | # - amd64 52 | # - arm64 53 | # - ppc64le 54 | # - s390x 55 | # - key: kubernetes.io/os 56 | # operator: In 57 | # values: 58 | # - linux 59 | securityContext: 60 | runAsNonRoot: true 61 | # TODO(user): For common cases that do not require escalating privileges 62 | # it is recommended to ensure that all your Pods/Containers are restrictive. 63 | # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted 64 | # Please uncomment the following code if your project does NOT have to work on old Kubernetes 65 | # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). 66 | # seccompProfile: 67 | # type: RuntimeDefault 68 | containers: 69 | - command: 70 | - /manager 71 | args: 72 | - --leader-elect 73 | image: controller:latest 74 | name: manager 75 | securityContext: 76 | allowPrivilegeEscalation: false 77 | capabilities: 78 | drop: 79 | - "ALL" 80 | livenessProbe: 81 | httpGet: 82 | path: /healthz 83 | port: 8081 84 | initialDelaySeconds: 15 85 | periodSeconds: 20 86 | readinessProbe: 87 | httpGet: 88 | path: /readyz 89 | port: 8081 90 | initialDelaySeconds: 5 91 | periodSeconds: 10 92 | # TODO(user): Configure the resources accordingly based on the project requirements. 93 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 94 | resources: 95 | limits: 96 | cpu: 500m 97 | memory: 128Mi 98 | requests: 99 | cpu: 10m 100 | memory: 64Mi 101 | serviceAccountName: controller-manager 102 | terminationGracePeriodSeconds: 10 103 | -------------------------------------------------------------------------------- /util/helpers.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /util/helpers.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 13:49:54 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package util 28 | 29 | import ( 30 | "bytes" 31 | "encoding/json" 32 | "fmt" 33 | "os" 34 | "regexp" 35 | "text/template" 36 | 37 | "github.com/Masterminds/sprig" 38 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 39 | "k8s.io/apimachinery/pkg/types" 40 | ) 41 | 42 | func Default(value string, defaultValue string) string { 43 | if value == "" { 44 | value = defaultValue 45 | } 46 | return value 47 | } 48 | 49 | func EnsureServiceAccount(serviceAccountName string) string { 50 | return Default(serviceAccountName, "default") 51 | } 52 | 53 | func EnsureNamespacedName( 54 | partialNamespacedName *integrationv1beta1.NamespacedName, 55 | defaultNamespace string, 56 | ) types.NamespacedName { 57 | return types.NamespacedName{ 58 | Name: partialNamespacedName.Name, 59 | Namespace: Default(partialNamespacedName.Namespace, defaultNamespace), 60 | } 61 | } 62 | 63 | func GetOperatorNamespace() string { 64 | operatorNamespace := os.Getenv("POD_NAMESPACE") 65 | if operatorNamespace == "" { 66 | operatorNamespace = "kube-system" 67 | } 68 | return operatorNamespace 69 | } 70 | 71 | func JsonToHashMap(body []byte) (map[string]string, error) { 72 | hashMap := make(map[string]string) 73 | var obj map[string]interface{} 74 | if err := json.Unmarshal(body, &obj); err != nil { 75 | return nil, err 76 | } 77 | for key, value := range obj { 78 | hashMap[key] = fmt.Sprintf("%v", value) 79 | } 80 | return hashMap, nil 81 | } 82 | 83 | func WhenInWhenSlice(when integrationv1beta1.When, whenSlice *[]integrationv1beta1.When) bool { 84 | if whenSlice == nil { 85 | return false 86 | } 87 | for _, whenItem := range *whenSlice { 88 | if when == whenItem { 89 | return true 90 | } 91 | } 92 | return false 93 | } 94 | 95 | func Validate(plug *integrationv1beta1.Plug, socket *integrationv1beta1.Socket) error { 96 | if socket.Spec.Validation == nil { 97 | return nil 98 | } 99 | if socket.Spec.Validation.NamespaceBlacklist != nil { 100 | for _, namespace := range socket.Spec.Validation.NamespaceBlacklist { 101 | match, _ := regexp.MatchString(namespace, plug.Namespace) 102 | if match { 103 | return fmt.Errorf("namespace %s is blacklisted", plug.Namespace) 104 | } 105 | } 106 | } 107 | if socket.Spec.Validation.NamespaceWhitelist != nil { 108 | for _, namespace := range socket.Spec.Validation.NamespaceWhitelist { 109 | match, _ := regexp.MatchString(namespace, plug.Namespace) 110 | if match { 111 | return nil 112 | } 113 | } 114 | return fmt.Errorf("namespace %s is not whitelisted", plug.Namespace) 115 | } 116 | return nil 117 | } 118 | 119 | func Template( 120 | data *map[string]interface{}, 121 | templateValue string, 122 | ) (string, error) { 123 | t, err := template.New("").Funcs(sprig.TxtFuncMap()).Delims("{%", "%}").Parse(templateValue) 124 | if err != nil { 125 | return "", err 126 | } 127 | var buff bytes.Buffer 128 | err = t.Execute(&buff, data) 129 | if err != nil { 130 | return "", err 131 | } 132 | return buff.String(), nil 133 | } 134 | -------------------------------------------------------------------------------- /chart/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: {{ template "integration-operator.name" . }} 5 | labels: 6 | app.kubernetes.io/name: {{ template "integration-operator.name" . }} 7 | helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} 8 | app.kubernetes.io/instance: {{ .Release.Name }} 9 | app.kubernetes.io/managed-by: {{ .Release.Service }} 10 | --- 11 | apiVersion: rbac.authorization.k8s.io/v1 12 | kind: ClusterRoleBinding 13 | metadata: 14 | name: {{ template "integration-operator.name" . }} 15 | labels: 16 | app.kubernetes.io/name: {{ template "integration-operator.name" . }} 17 | helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} 18 | app.kubernetes.io/instance: {{ .Release.Name }} 19 | app.kubernetes.io/managed-by: {{ .Release.Service }} 20 | roleRef: 21 | apiGroup: rbac.authorization.k8s.io 22 | kind: ClusterRole 23 | name: {{ template "integration-operator.name" . }} 24 | subjects: 25 | - kind: ServiceAccount 26 | name: {{ template "integration-operator.name" . }} 27 | namespace: {{ .Release.Namespace }} 28 | --- 29 | apiVersion: rbac.authorization.k8s.io/v1 30 | kind: ClusterRole 31 | metadata: 32 | name: {{ template "integration-operator.name" . }} 33 | labels: 34 | app.kubernetes.io/name: {{ template "integration-operator.name" . }} 35 | helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} 36 | app.kubernetes.io/instance: {{ .Release.Name }} 37 | app.kubernetes.io/managed-by: {{ .Release.Service }} 38 | rules: 39 | - apiGroups: 40 | - "" 41 | resources: 42 | - configmaps 43 | - secrets 44 | verbs: 45 | - get 46 | - list 47 | - watch 48 | - apiGroups: 49 | - "" 50 | resources: 51 | - events 52 | verbs: 53 | - create 54 | - delete 55 | - get 56 | - list 57 | - patch 58 | - update 59 | - watch 60 | - apiGroups: 61 | - "" 62 | resources: 63 | - pods 64 | - services 65 | verbs: 66 | - create 67 | - delete 68 | - get 69 | - list 70 | - patch 71 | - update 72 | - watch 73 | - apiGroups: 74 | - "" 75 | resources: 76 | - serviceaccounts 77 | verbs: 78 | - impersonate 79 | - apiGroups: 80 | - coordination.k8s.io 81 | resources: 82 | - leases 83 | verbs: 84 | - create 85 | - delete 86 | - get 87 | - list 88 | - patch 89 | - update 90 | - watch 91 | - apiGroups: 92 | - integration.rock8s.com 93 | resources: 94 | - deferredresources 95 | verbs: 96 | - create 97 | - delete 98 | - get 99 | - list 100 | - patch 101 | - update 102 | - watch 103 | - apiGroups: 104 | - integration.rock8s.com 105 | resources: 106 | - deferredresources/finalizers 107 | verbs: 108 | - update 109 | - apiGroups: 110 | - integration.rock8s.com 111 | resources: 112 | - deferredresources/status 113 | verbs: 114 | - get 115 | - patch 116 | - update 117 | - apiGroups: 118 | - integration.rock8s.com 119 | resources: 120 | - plugs 121 | verbs: 122 | - create 123 | - delete 124 | - get 125 | - list 126 | - patch 127 | - update 128 | - watch 129 | - apiGroups: 130 | - integration.rock8s.com 131 | resources: 132 | - plugs/finalizers 133 | verbs: 134 | - update 135 | - apiGroups: 136 | - integration.rock8s.com 137 | resources: 138 | - plugs/status 139 | verbs: 140 | - get 141 | - patch 142 | - update 143 | - apiGroups: 144 | - integration.rock8s.com 145 | resources: 146 | - sockets 147 | verbs: 148 | - create 149 | - delete 150 | - get 151 | - list 152 | - patch 153 | - update 154 | - watch 155 | - apiGroups: 156 | - integration.rock8s.com 157 | resources: 158 | - sockets/finalizers 159 | verbs: 160 | - update 161 | - apiGroups: 162 | - integration.rock8s.com 163 | resources: 164 | - sockets/status 165 | verbs: 166 | - get 167 | - patch 168 | - update 169 | -------------------------------------------------------------------------------- /util/var.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /util/var.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 13:49:54 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package util 28 | 29 | import ( 30 | "context" 31 | "encoding/json" 32 | 33 | "github.com/tidwall/gjson" 34 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 35 | "k8s.io/client-go/kubernetes" 36 | ctrl "sigs.k8s.io/controller-runtime" 37 | kustomizeTypes "sigs.k8s.io/kustomize/api/types" 38 | ) 39 | 40 | type VarUtil struct { 41 | client *kubernetes.Clientset 42 | resourceUtil *ResourceUtil 43 | } 44 | 45 | func NewVarUtil(ctx context.Context) *VarUtil { 46 | return &VarUtil{ 47 | client: kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie()), 48 | resourceUtil: NewResourceUtil(ctx), 49 | } 50 | } 51 | 52 | func (u *VarUtil) GetVars( 53 | namespace string, 54 | vars []*integrationv1beta1.Var, 55 | kubectlUtil *KubectlUtil, 56 | plug *integrationv1beta1.Plug, 57 | socket *integrationv1beta1.Socket, 58 | ) (map[string]string, error) { 59 | resultMap := make(map[string]string) 60 | for _, v := range vars { 61 | varResult, err := u.GetVar(namespace, v, kubectlUtil, plug, socket) 62 | if err != nil { 63 | return nil, err 64 | } 65 | resultMap[v.Name] = varResult 66 | } 67 | return resultMap, nil 68 | } 69 | 70 | func (u *VarUtil) GetVar( 71 | namespace string, 72 | v *integrationv1beta1.Var, 73 | kubectlUtil *KubectlUtil, 74 | plug *integrationv1beta1.Plug, 75 | socket *integrationv1beta1.Socket, 76 | ) (string, error) { 77 | objRef := kustomizeTypes.Target{ 78 | APIVersion: v.ObjRef.APIVersion, 79 | Name: v.ObjRef.Name, 80 | Namespace: v.ObjRef.Namespace, 81 | } 82 | objRef.Kind = v.ObjRef.Kind 83 | var err error 84 | if v.ObjRef.TemplateNamespace != "" { 85 | objRef.Namespace, err = u.varTemplateLookup(v.ObjRef.TemplateNamespace, plug, socket) 86 | if err != nil { 87 | return "", err 88 | } 89 | } 90 | if v.ObjRef.TemplateName != "" { 91 | objRef.Name, err = u.varTemplateLookup(v.ObjRef.TemplateName, plug, socket) 92 | if err != nil { 93 | return "", err 94 | } 95 | } 96 | resource, err := u.resourceUtil.GetResource(namespace, objRef, kubectlUtil) 97 | if err != nil { 98 | return "", err 99 | } 100 | bResource, err := json.Marshal(resource) 101 | if err != nil { 102 | return "", err 103 | } 104 | return gjson.Parse(string(bResource)).Get(v.FieldRef.FieldPath).String(), nil 105 | } 106 | 107 | func (u *VarUtil) varTemplateLookup( 108 | varTemplate string, 109 | plug *integrationv1beta1.Plug, 110 | socket *integrationv1beta1.Socket, 111 | ) (string, error) { 112 | data, err := u.buildVarTemplateData(socket, plug) 113 | if err != nil { 114 | return "", err 115 | } 116 | return Template(&data, varTemplate) 117 | } 118 | 119 | func (u *VarUtil) buildVarTemplateData( 120 | socket *integrationv1beta1.Socket, 121 | plug *integrationv1beta1.Plug, 122 | ) (map[string]interface{}, error) { 123 | dataMap := map[string]interface{}{} 124 | if socket != nil { 125 | dataMap["socket"] = socket 126 | } 127 | if plug != nil { 128 | dataMap["plug"] = plug 129 | } 130 | bData, err := json.Marshal(dataMap) 131 | if err != nil { 132 | return nil, err 133 | } 134 | var data map[string]interface{} 135 | if err := json.Unmarshal(bData, &data); err != nil { 136 | return nil, err 137 | } 138 | return data, nil 139 | } 140 | -------------------------------------------------------------------------------- /api/v1beta1/shared_types.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /api/v1beta1/shared_types.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 12:06:48 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package v1beta1 28 | 29 | import ( 30 | v1 "k8s.io/api/core/v1" 31 | apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 32 | "sigs.k8s.io/kustomize/api/resid" 33 | kustomizeTypes "sigs.k8s.io/kustomize/api/types" 34 | ) 35 | 36 | const Finalizer = "integration.rock8s.com/finalizer" 37 | 38 | type When string 39 | 40 | const ( 41 | CoupledWhen When = "coupled" 42 | CreatedWhen When = "created" 43 | DecoupledWhen When = "decoupled" 44 | DeletedWhen When = "deleted" 45 | UpdatedWhen When = "updated" 46 | ) 47 | 48 | type Do string 49 | 50 | const ( 51 | ApplyDo Do = "apply" 52 | DeleteDo Do = "delete" 53 | RecreateDo Do = "recreate" 54 | ) 55 | 56 | type ResourceAction struct { 57 | Do Do `json:"do,omitempty"` 58 | Template *apiextv1.JSON `json:"template,omitempty"` 59 | Templates *[]*apiextv1.JSON `json:"templates,omitempty"` 60 | StringTemplate string `json:"stringTemplate,omitempty"` 61 | StringTemplates *[]string `json:"stringTemplates,omitempty"` 62 | } 63 | 64 | type Resource struct { 65 | ResourceAction `json:",inline"` 66 | RetainWhenDecoupled bool `json:"retainWhenDecoupled,omitempty"` 67 | When *[]When `json:"when,omitempty"` 68 | } 69 | 70 | type NamespacedName struct { 71 | // name 72 | Name string `json:"name"` 73 | 74 | // namespace 75 | Namespace string `json:"namespace,omitempty"` 76 | } 77 | 78 | type SpecApparatus struct { 79 | // endpoint 80 | Endpoint string `json:"endpoint,omitempty"` 81 | 82 | // terminate apparatus after idle for timeout in milliseconds 83 | IdleTimeout uint `json:"idleTimeout,omitempty"` 84 | 85 | // List of containers belonging to the apparatus. 86 | // Containers cannot currently be added or removed. 87 | // There must be at least one container in an apparatus. 88 | // Cannot be updated. 89 | // +patchMergeKey=name 90 | // +patchStrategy=merge 91 | Containers *[]v1.Container `json:"containers"` 92 | } 93 | 94 | // Var represents a variable whose value will be sourced 95 | // from a field in a Kubernetes object. 96 | type Var struct { 97 | // Value of identifier name e.g. FOO used in container args, annotations 98 | // Appears in pod template as $(FOO) 99 | Name string `json:"name" yaml:"name"` 100 | 101 | // ObjRef must refer to a Kubernetes resource under the 102 | // purview of this kustomization. ObjRef should use the 103 | // raw name of the object (the name specified in its YAML, 104 | // before addition of a namePrefix and a nameSuffix). 105 | ObjRef Target `json:"objref" yaml:"objref"` 106 | 107 | // FieldRef refers to the field of the object referred to by 108 | // ObjRef whose value will be extracted for use in 109 | // replacing $(FOO). 110 | // If unspecified, this defaults to fieldPath: $defaultFieldPath 111 | FieldRef kustomizeTypes.FieldSelector `json:"fieldref,omitempty" yaml:"fieldref,omitempty"` 112 | } 113 | 114 | // Target refers to a kubernetes object by Group, Version, Kind and Name 115 | // gvk.Gvk contains Group, Version and Kind 116 | // APIVersion is added to keep the backward compatibility of using ObjectReference 117 | // for Var.ObjRef 118 | type Target struct { 119 | APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` 120 | resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"` 121 | Name string `json:"name,omitempty" yaml:"name,omitempty"` 122 | Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` 123 | TemplateName string `json:"templateName,omitempty" yaml:"templateName,omitempty"` 124 | TemplateNamespace string `json:"templateNamespace,omitempty" yaml:"templateNamespace,omitempty"` 125 | } 126 | -------------------------------------------------------------------------------- /util/kubectl.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /util/kubectl.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 13:49:54 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package util 28 | 29 | import ( 30 | "context" 31 | "encoding/json" 32 | "fmt" 33 | 34 | "k8s.io/apimachinery/pkg/api/meta" 35 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 36 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 37 | "k8s.io/apimachinery/pkg/types" 38 | "k8s.io/client-go/discovery" 39 | memory "k8s.io/client-go/discovery/cached" 40 | "k8s.io/client-go/dynamic" 41 | "k8s.io/client-go/rest" 42 | "k8s.io/client-go/restmapper" 43 | ctrl "sigs.k8s.io/controller-runtime" 44 | ) 45 | 46 | type KubectlUtil struct { 47 | ctx context.Context 48 | cfg *rest.Config 49 | } 50 | 51 | func NewKubectlUtil(ctx context.Context, namespace string, serviceAccountName string) *KubectlUtil { 52 | cfg := ctrl.GetConfigOrDie() 53 | cfg.Impersonate = rest.ImpersonationConfig{ 54 | UserName: fmt.Sprintf("system:serviceaccount:%s:%s", namespace, Default(serviceAccountName, "default")), 55 | } 56 | return &KubectlUtil{ 57 | cfg: cfg, 58 | ctx: ctx, 59 | } 60 | } 61 | 62 | func (u *KubectlUtil) Create(body []byte) error { 63 | dr, obj, err := u.prepareDynamic(body) 64 | if err != nil { 65 | return err 66 | } 67 | if _, err := dr.Create(u.ctx, obj, metav1.CreateOptions{ 68 | FieldManager: "integration-operator", 69 | }); err != nil { 70 | return err 71 | } 72 | return nil 73 | } 74 | 75 | func (u *KubectlUtil) Update(body []byte) error { 76 | dr, obj, err := u.prepareDynamic(body) 77 | if err != nil { 78 | return err 79 | } 80 | if _, err := dr.Update(u.ctx, obj, metav1.UpdateOptions{ 81 | FieldManager: "integration-operator", 82 | }); err != nil { 83 | return err 84 | } 85 | return nil 86 | } 87 | 88 | func (u *KubectlUtil) Apply(body []byte) error { 89 | dr, obj, err := u.prepareDynamic(body) 90 | if err != nil { 91 | return err 92 | } 93 | data, err := json.Marshal(obj) 94 | if err != nil { 95 | return err 96 | } 97 | if _, err = dr.Patch(u.ctx, obj.GetName(), types.ApplyPatchType, data, metav1.PatchOptions{ 98 | FieldManager: "integration-operator", 99 | }); err != nil { 100 | return err 101 | } 102 | return nil 103 | } 104 | 105 | func (u *KubectlUtil) Delete(body []byte) error { 106 | dr, obj, err := u.prepareDynamic(body) 107 | if err != nil { 108 | return err 109 | } 110 | if err = dr.Delete(u.ctx, obj.GetName(), metav1.DeleteOptions{}); err != nil { 111 | return err 112 | } 113 | return nil 114 | } 115 | 116 | func (u *KubectlUtil) Get(body []byte) (*unstructured.Unstructured, error) { 117 | dr, obj, err := u.prepareDynamic(body) 118 | if err != nil { 119 | return nil, err 120 | } 121 | return dr.Get(u.ctx, obj.GetName(), metav1.GetOptions{}) 122 | } 123 | 124 | // https://ymmt2005.hatenablog.com/entry/2020/04/14/An_example_of_using_dynamic_client_of_k8s.io/client-go 125 | func (u *KubectlUtil) prepareDynamic(resource []byte) (dynamic.ResourceInterface, *unstructured.Unstructured, error) { 126 | // 1. Prepare a RESTMapper to find GVR 127 | dc, err := discovery.NewDiscoveryClientForConfig(u.cfg) 128 | if err != nil { 129 | return nil, nil, err 130 | } 131 | mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(dc)) 132 | 133 | // 2. Prepare the dynamic client 134 | dyn, err := dynamic.NewForConfig(u.cfg) 135 | if err != nil { 136 | return nil, nil, err 137 | } 138 | 139 | // 3. Decode YAML manifest into unstructured.Unstructured 140 | obj := &unstructured.Unstructured{} 141 | _, gvk, err := decUnstructured.Decode(resource, nil, obj) 142 | if err != nil { 143 | return nil, nil, err 144 | } 145 | 146 | // 4. Find GVR 147 | mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version) 148 | if err != nil { 149 | return nil, nil, err 150 | } 151 | 152 | // 5. Obtain REST interface for the GVR 153 | var dr dynamic.ResourceInterface 154 | if mapping.Scope.Name() == meta.RESTScopeNameNamespace { 155 | // namespaced resources should specify the namespace 156 | dr = dyn.Resource(mapping.Resource).Namespace(obj.GetNamespace()) 157 | } else { 158 | // for cluster-wide resources 159 | dr = dyn.Resource(mapping.Resource) 160 | } 161 | 162 | return dr, obj, nil 163 | } 164 | -------------------------------------------------------------------------------- /api/v1beta1/plug_types.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /api/v1beta1/plug_types.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 10:50:57 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package v1beta1 28 | 29 | import ( 30 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 | "k8s.io/apimachinery/pkg/types" 32 | ) 33 | 34 | // PlugSpec defines the desired state of Plug 35 | type PlugSpec struct { 36 | // socket 37 | Socket NamespacedName `json:"socket,omitempty"` 38 | 39 | // vars 40 | Vars []*Var `json:"vars,omitempty" yaml:"vars,omitempty"` 41 | 42 | // result vars 43 | ResultVars []*Var `json:"resultVars,omitempty" yaml:"vars,omitempty"` 44 | 45 | // data 46 | Data map[string]string `json:"data,omitempty"` 47 | 48 | // data configmap name 49 | DataConfigMapName string `json:"dataConfigMapName,omitempty"` 50 | 51 | // data secret name 52 | DataSecretName string `json:"dataSecretName,omitempty"` 53 | 54 | // config 55 | Config map[string]string `json:"config,omitempty"` 56 | 57 | // config configmap name 58 | ConfigConfigMapName string `json:"configConfigMapName,omitempty"` 59 | 60 | // config secret name 61 | ConfigSecretName string `json:"configSecretName,omitempty"` 62 | 63 | // config template 64 | ConfigTemplate map[string]string `json:"configTemplate,omitempty"` 65 | 66 | // result 67 | Result map[string]string `json:"result,omitempty"` 68 | 69 | // result configmap name 70 | ResultConfigMapName string `json:"resultConfigMapName,omitempty"` 71 | 72 | // result secret name 73 | ResultSecretName string `json:"resultSecretName,omitempty"` 74 | 75 | // result template 76 | ResultTemplate map[string]string `json:"resultTemplate,omitempty"` 77 | 78 | // ServiceAccountName is the name of the ServiceAccount to use to run integrations. 79 | // More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ 80 | // +optional 81 | ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"bytes,8,opt,name=serviceAccountName"` 82 | 83 | // apparatus 84 | Apparatus *SpecApparatus `json:"apparatus,omitempty"` 85 | 86 | // resources 87 | Resources []*Resource `json:"resources,omitempty"` 88 | 89 | // result resources 90 | ResultResources []*ResourceAction `json:"resultResources,omitempty"` 91 | 92 | // change epoch to force an update 93 | Epoch string `json:"epoch,omitempty"` 94 | } 95 | 96 | // PlugStatus defines the observed state of Plug 97 | type PlugStatus struct { 98 | // Conditions represent the latest available observations of an object's state 99 | Conditions []metav1.Condition `json:"conditions,omitempty"` 100 | 101 | // socket coupled to plug 102 | CoupledSocket *CoupledSocket `json:"coupledSocket,omitempty"` 103 | 104 | // coupled result 105 | CoupledResult *CoupledResultStatus `json:"coupledResult,omitempty"` 106 | } 107 | 108 | type CoupledResult struct { 109 | // plug result 110 | Plug map[string]string `json:"plug,omitempty"` 111 | 112 | // socket result 113 | Socket map[string]string `json:"socket,omitempty"` 114 | } 115 | 116 | type CoupledResultStatus struct { 117 | CoupledResult `json:",inline"` 118 | 119 | // observed generation 120 | ObservedGeneration int64 `json:"observedGeneration,omitempty"` 121 | } 122 | 123 | type CoupledSocket struct { 124 | // API version of the socket 125 | APIVersion string `json:"apiVersion,omitempty"` 126 | 127 | // Kind of the socket 128 | Kind string `json:"kind,omitempty"` 129 | 130 | // Name of the socket 131 | Name string `json:"name,omitempty"` 132 | 133 | // Namespace of the socket 134 | Namespace string `json:"namespace,omitempty"` 135 | 136 | // UID of the socket 137 | UID types.UID `json:"uid,omitempty"` 138 | } 139 | 140 | //+kubebuilder:object:root=true 141 | //+kubebuilder:subresource:status 142 | 143 | // Plug is the Schema for the plugs API 144 | type Plug struct { 145 | metav1.TypeMeta `json:",inline"` 146 | metav1.ObjectMeta `json:"metadata,omitempty"` 147 | 148 | Spec PlugSpec `json:"spec,omitempty"` 149 | Status PlugStatus `json:"status,omitempty"` 150 | } 151 | 152 | //+kubebuilder:object:root=true 153 | 154 | // PlugList contains a list of Plug 155 | type PlugList struct { 156 | metav1.TypeMeta `json:",inline"` 157 | metav1.ListMeta `json:"metadata,omitempty"` 158 | Items []Plug `json:"items"` 159 | } 160 | 161 | func init() { 162 | SchemeBuilder.Register(&Plug{}, &PlugList{}) 163 | } 164 | -------------------------------------------------------------------------------- /coupler/couple.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /coupler/couple.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 19:02:43 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package coupler 28 | 29 | import ( 30 | "context" 31 | 32 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 33 | "gitlab.com/bitspur/rock8s/integration-operator/util" 34 | k8serrors "k8s.io/apimachinery/pkg/api/errors" 35 | "k8s.io/client-go/tools/record" 36 | ctrl "sigs.k8s.io/controller-runtime" 37 | "sigs.k8s.io/controller-runtime/pkg/client" 38 | ) 39 | 40 | func Couple( 41 | client *client.Client, 42 | ctx context.Context, 43 | req *ctrl.Request, 44 | plugUtil *util.PlugUtil, 45 | socketUtil *util.SocketUtil, 46 | plug *integrationv1beta1.Plug, 47 | socket *integrationv1beta1.Socket, 48 | recorder record.EventRecorder, 49 | ) (ctrl.Result, error) { 50 | configUtil := util.NewConfigUtil(ctx) 51 | if plug == nil { 52 | var err error 53 | plug, err = plugUtil.Get() 54 | if err != nil { 55 | return ctrl.Result{}, err 56 | } 57 | } 58 | if socket == nil { 59 | var err error 60 | socket, err = socketUtil.Get() 61 | if err != nil { 62 | if k8serrors.IsNotFound(err) { 63 | return plugUtil.UpdateCoupledStatus(util.SocketNotCreated, plug, nil, true) 64 | } 65 | return plugUtil.Error(err, plug) 66 | } 67 | } 68 | 69 | if socketUtil.CoupledPlugExists(socket.Status.CoupledPlugs, plug.UID) && 70 | plug.Status.CoupledSocket != nil && 71 | plug.Status.CoupledResult != nil { 72 | coupledCondition, err := plugUtil.GetCoupledCondition(plug) 73 | if err != nil { 74 | return plugUtil.Error(err, plug) 75 | } 76 | if plug.Generation > coupledCondition.ObservedGeneration { 77 | return plugUtil.UpdateCoupledStatus(util.UpdatingInProcess, plug, socket, true) 78 | } 79 | if plug.Generation > plug.Status.CoupledResult.ObservedGeneration { 80 | if err := Update(client, ctx, req, plugUtil, socketUtil, plug, socket, recorder); err != nil { 81 | return plugUtil.Error(err, plug) 82 | } 83 | plugConfig, err := configUtil.GetPlugConfig(plug, socket) 84 | if err != nil { 85 | return plugUtil.Error(err, plug) 86 | } 87 | socketConfig, err := configUtil.GetSocketConfig(plug, socket) 88 | if err != nil { 89 | socketUtil.Error(err, socket) 90 | return plugUtil.Error(err, plug) 91 | } 92 | if _, err := socketUtil.UpdateCoupledStatus(util.SocketCoupled, socket, nil, false); err != nil { 93 | socketUtil.Error(err, socket) 94 | return plugUtil.Error(err, plug) 95 | } 96 | return plugUtil.UpdateResultStatus(plug, socket, plugConfig, socketConfig) 97 | } 98 | return ctrl.Result{}, nil 99 | } 100 | 101 | if err := util.Validate(plug, socket); err != nil { 102 | return plugUtil.Error(err, plug) 103 | } 104 | 105 | plugConfig, err := configUtil.GetPlugConfig(plug, socket) 106 | if err != nil { 107 | return plugUtil.Error(err, plug) 108 | } 109 | socketConfig, err := configUtil.GetSocketConfig(plug, socket) 110 | if err != nil { 111 | socketUtil.Error(err, socket) 112 | return plugUtil.Error(err, plug) 113 | } 114 | 115 | if !socketUtil.CoupledPlugExists(socket.Status.CoupledPlugs, plug.UID) { 116 | if plug.Status.CoupledSocket != nil { 117 | if _, err := socketUtil.UpdateAppendCoupledPlugStatus(plug, socket, false); err != nil { 118 | return plugUtil.Error(err, plug) 119 | } 120 | return plugUtil.UpdateCoupledStatus(util.CouplingInProcess, plug, socket, true) 121 | } 122 | coupledCondition, err := plugUtil.GetCoupledCondition(plug) 123 | if err != nil { 124 | return plugUtil.Error(err, plug) 125 | } 126 | if coupledCondition.Reason != string(util.CouplingInProcess) { 127 | return plugUtil.UpdateCoupledStatus(util.CouplingInProcess, plug, nil, true) 128 | } 129 | err = CoupledPlug(plug, socket, plugConfig, socketConfig, recorder) 130 | if err != nil { 131 | return plugUtil.Error(err, plug) 132 | } 133 | err = CoupledSocket(plug, socket, plugConfig, socketConfig, recorder) 134 | if err != nil { 135 | socketUtil.Error(err, socket) 136 | return plugUtil.Error(err, plug) 137 | } 138 | if _, err := socketUtil.UpdateAppendCoupledPlugStatus(plug, socket, false); err != nil { 139 | return plugUtil.Error(err, plug) 140 | } 141 | return plugUtil.UpdateCoupledStatus(util.CouplingInProcess, plug, socket, true) 142 | } 143 | 144 | if plug.Status.CoupledResult == nil { 145 | if _, err := socketUtil.UpdateCoupledStatus(util.SocketCoupled, socket, nil, false); err != nil { 146 | socketUtil.Error(err, socket) 147 | return plugUtil.Error(err, plug) 148 | } 149 | return plugUtil.UpdateResultStatus(plug, socket, plugConfig, socketConfig) 150 | } 151 | 152 | return ctrl.Result{}, nil 153 | } 154 | -------------------------------------------------------------------------------- /controllers/plug_controller.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /controllers/plug_controller.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 10:50:57 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package controllers 28 | 29 | import ( 30 | "context" 31 | "os" 32 | "strconv" 33 | 34 | "k8s.io/apimachinery/pkg/api/errors" 35 | "k8s.io/apimachinery/pkg/runtime" 36 | "k8s.io/client-go/tools/record" 37 | ctrl "sigs.k8s.io/controller-runtime" 38 | "sigs.k8s.io/controller-runtime/pkg/client" 39 | "sigs.k8s.io/controller-runtime/pkg/controller" 40 | "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 41 | "sigs.k8s.io/controller-runtime/pkg/event" 42 | "sigs.k8s.io/controller-runtime/pkg/log" 43 | "sigs.k8s.io/controller-runtime/pkg/predicate" 44 | 45 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 46 | "gitlab.com/bitspur/rock8s/integration-operator/coupler" 47 | "gitlab.com/bitspur/rock8s/integration-operator/util" 48 | ) 49 | 50 | // PlugReconciler reconciles a Plug object 51 | type PlugReconciler struct { 52 | client.Client 53 | Scheme *runtime.Scheme 54 | Recorder record.EventRecorder 55 | } 56 | 57 | //+kubebuilder:rbac:groups=integration.rock8s.com,resources=plugs,verbs=get;list;watch;create;update;patch;delete 58 | //+kubebuilder:rbac:groups=integration.rock8s.com,resources=plugs/status,verbs=get;update;patch 59 | //+kubebuilder:rbac:groups=integration.rock8s.com,resources=plugs/finalizers,verbs=update 60 | 61 | func (r *PlugReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 62 | logger := log.FromContext(ctx) 63 | logger.V(1).Info("Plug Reconcile") 64 | namespacedName := integrationv1beta1.NamespacedName{ 65 | Name: req.NamespacedName.Name, 66 | Namespace: req.NamespacedName.Namespace, 67 | } 68 | plug := &integrationv1beta1.Plug{} 69 | if err := r.Client.Get(ctx, util.EnsureNamespacedName(&namespacedName, util.GetOperatorNamespace()), plug); err != nil { 70 | return ctrl.Result{}, client.IgnoreNotFound(err) 71 | } 72 | plug = plug.DeepCopy() 73 | socketUtil := util.NewSocketUtil(&r.Client, ctx, &req, &integrationv1beta1.NamespacedName{ 74 | Name: plug.Spec.Socket.Name, 75 | Namespace: util.Default(plug.Spec.Socket.Namespace, req.NamespacedName.Namespace), 76 | }) 77 | socket, err := socketUtil.Get() 78 | plugUtil := util.NewPlugUtil(&r.Client, ctx, &req, &namespacedName, socket) 79 | if err != nil && !errors.IsNotFound(err) { 80 | return plugUtil.Error(err, plug) 81 | } 82 | 83 | if plug.GetDeletionTimestamp() != nil { 84 | if controllerutil.ContainsFinalizer(plug, integrationv1beta1.Finalizer) { 85 | if plug.Status.CoupledSocket != nil && socket != nil { 86 | if err := coupler.Decouple(&r.Client, ctx, &req, plugUtil, socketUtil, plug, socket, r.Recorder); err != nil { 87 | return plugUtil.Error(err, plug) 88 | } 89 | } 90 | if err := coupler.DeletedPlug(plug, r.Recorder); err != nil { 91 | return plugUtil.Error(err, plug) 92 | } 93 | controllerutil.RemoveFinalizer(plug, integrationv1beta1.Finalizer) 94 | return plugUtil.Update(plug, true) 95 | } 96 | return ctrl.Result{}, nil 97 | } 98 | 99 | if !controllerutil.ContainsFinalizer(plug, integrationv1beta1.Finalizer) { 100 | controllerutil.AddFinalizer(plug, integrationv1beta1.Finalizer) 101 | return plugUtil.Update(plug, true) 102 | } 103 | 104 | coupledCondition, err := plugUtil.GetCoupledCondition(plug) 105 | if err != nil { 106 | return plugUtil.Error(err, plug) 107 | } 108 | if coupledCondition == nil { 109 | if err := coupler.CreatedPlug(plug, r.Recorder); err != nil { 110 | return plugUtil.Error(err, plug) 111 | } 112 | return plugUtil.UpdateCoupledStatus(util.PlugCreated, plug, nil, true) 113 | } 114 | 115 | return coupler.Couple(&r.Client, ctx, &req, plugUtil, socketUtil, plug, socket, r.Recorder) 116 | } 117 | 118 | func filterPlugPredicate() predicate.Predicate { 119 | return predicate.Funcs{ 120 | UpdateFunc: func(e event.UpdateEvent) bool { 121 | return e.ObjectNew.GetDeletionTimestamp() != nil || e.ObjectNew.GetGeneration() > e.ObjectOld.GetGeneration() 122 | }, 123 | DeleteFunc: func(e event.DeleteEvent) bool { 124 | return !e.DeleteStateUnknown 125 | }, 126 | } 127 | } 128 | 129 | // SetupWithManager sets up the controller with the Manager. 130 | func (r *PlugReconciler) SetupWithManager(mgr ctrl.Manager) error { 131 | maxConcurrentReconciles := 3 132 | if value := os.Getenv("MAX_CONCURRENT_RECONCILES"); value != "" { 133 | if val, err := strconv.Atoi(value); err == nil { 134 | maxConcurrentReconciles = val 135 | } 136 | } 137 | return ctrl.NewControllerManagedBy(mgr). 138 | WithOptions(controller.Options{MaxConcurrentReconciles: maxConcurrentReconciles}). 139 | WithEventFilter(filterPlugPredicate()). 140 | For(&integrationv1beta1.Plug{}). 141 | Complete(r) 142 | } 143 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module gitlab.com/bitspur/rock8s/integration-operator 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/Masterminds/sprig v2.22.0+incompatible 7 | github.com/go-logr/logr v1.2.4 8 | github.com/go-resty/resty/v2 v2.10.0 9 | github.com/onsi/ginkgo/v2 v2.11.0 10 | github.com/onsi/gomega v1.27.10 11 | github.com/tdewolff/minify v2.3.6+incompatible 12 | github.com/tidwall/gjson v1.17.0 13 | github.com/tidwall/sjson v1.2.5 14 | k8s.io/api v0.26.1 15 | k8s.io/apiextensions-apiserver v0.26.1 16 | k8s.io/apimachinery v0.26.9 17 | k8s.io/apiserver v0.26.1 18 | k8s.io/client-go v0.26.1 19 | sigs.k8s.io/controller-runtime v0.14.6 20 | sigs.k8s.io/kustomize/api v0.8.9 21 | ) 22 | 23 | require ( 24 | github.com/Masterminds/goutils v1.1.1 // indirect 25 | github.com/Masterminds/semver v1.5.0 // indirect 26 | github.com/beorn7/perks v1.0.1 // indirect 27 | github.com/blang/semver/v4 v4.0.0 // indirect 28 | github.com/cenkalti/backoff/v4 v4.2.1 // indirect 29 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 30 | github.com/coreos/go-semver v0.3.1 // indirect 31 | github.com/coreos/go-systemd/v22 v22.5.0 // indirect 32 | github.com/davecgh/go-spew v1.1.1 // indirect 33 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 34 | github.com/evanphx/json-patch/v5 v5.7.0 // indirect 35 | github.com/felixge/httpsnoop v1.0.3 // indirect 36 | github.com/fsnotify/fsnotify v1.6.0 // indirect 37 | github.com/go-errors/errors v1.5.1 // indirect 38 | github.com/go-logr/stdr v1.2.2 // indirect 39 | github.com/go-logr/zapr v1.2.4 // indirect 40 | github.com/go-openapi/jsonpointer v0.20.0 // indirect 41 | github.com/go-openapi/jsonreference v0.20.2 // indirect 42 | github.com/go-openapi/swag v0.22.4 // indirect 43 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect 44 | github.com/gogo/protobuf v1.3.2 // indirect 45 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 46 | github.com/golang/protobuf v1.5.3 // indirect 47 | github.com/google/gnostic v0.7.0 // indirect 48 | github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect 49 | github.com/google/go-cmp v0.6.0 // indirect 50 | github.com/google/gofuzz v1.2.0 // indirect 51 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect 52 | github.com/google/uuid v1.3.1 // indirect 53 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect 54 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect 55 | github.com/huandu/xstrings v1.4.0 // indirect 56 | github.com/imdario/mergo v0.3.16 // indirect 57 | github.com/josharian/intern v1.0.0 // indirect 58 | github.com/json-iterator/go v1.1.12 // indirect 59 | github.com/mailru/easyjson v0.7.7 // indirect 60 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect 61 | github.com/mitchellh/copystructure v1.2.0 // indirect 62 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 63 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 64 | github.com/modern-go/reflect2 v1.0.2 // indirect 65 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 66 | github.com/pkg/errors v0.9.1 // indirect 67 | github.com/prometheus/client_golang v1.17.0 // indirect 68 | github.com/prometheus/client_model v0.5.0 // indirect 69 | github.com/prometheus/common v0.44.0 // indirect 70 | github.com/prometheus/procfs v0.12.0 // indirect 71 | github.com/spf13/pflag v1.0.5 // indirect 72 | github.com/tdewolff/parse v2.3.4+incompatible // indirect 73 | github.com/tdewolff/test v1.0.9 // indirect 74 | github.com/tidwall/match v1.1.1 // indirect 75 | github.com/tidwall/pretty v1.2.1 // indirect 76 | go.etcd.io/etcd/api/v3 v3.5.9 // indirect 77 | go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect 78 | go.etcd.io/etcd/client/v3 v3.5.9 // indirect 79 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 // indirect 80 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect 81 | go.opentelemetry.io/otel v1.19.0 // indirect 82 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect 83 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect 84 | go.opentelemetry.io/otel/metric v1.19.0 // indirect 85 | go.opentelemetry.io/otel/sdk v1.19.0 // indirect 86 | go.opentelemetry.io/otel/trace v1.19.0 // indirect 87 | go.opentelemetry.io/proto/otlp v1.0.0 // indirect 88 | go.uber.org/multierr v1.11.0 // indirect 89 | go.uber.org/zap v1.26.0 // indirect 90 | golang.org/x/crypto v0.14.0 // indirect 91 | golang.org/x/net v0.17.0 // indirect 92 | golang.org/x/oauth2 v0.13.0 // indirect 93 | golang.org/x/sys v0.13.0 // indirect 94 | golang.org/x/term v0.13.0 // indirect 95 | golang.org/x/text v0.13.0 // indirect 96 | golang.org/x/time v0.3.0 // indirect 97 | golang.org/x/tools v0.14.0 // indirect 98 | gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect 99 | google.golang.org/appengine v1.6.8 // indirect 100 | google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect 101 | google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect 102 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect 103 | google.golang.org/grpc v1.58.3 // indirect 104 | google.golang.org/protobuf v1.31.0 // indirect 105 | gopkg.in/inf.v0 v0.9.1 // indirect 106 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect 107 | gopkg.in/yaml.v2 v2.4.0 // indirect 108 | gopkg.in/yaml.v3 v3.0.1 // indirect 109 | k8s.io/component-base v0.26.1 // indirect 110 | k8s.io/klog/v2 v2.100.1 // indirect 111 | k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect 112 | k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect 113 | sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0 // indirect 114 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect 115 | sigs.k8s.io/kustomize/kyaml v0.14.3 // indirect 116 | sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect 117 | sigs.k8s.io/yaml v1.3.0 // indirect 118 | ) 119 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /main.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 10:48:34 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package main 28 | 29 | import ( 30 | "flag" 31 | "os" 32 | 33 | // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) 34 | // to ensure that exec-entrypoint and run can make use of them. 35 | _ "k8s.io/client-go/plugin/pkg/client/auth" 36 | 37 | "k8s.io/apimachinery/pkg/runtime" 38 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 39 | clientgoscheme "k8s.io/client-go/kubernetes/scheme" 40 | ctrl "sigs.k8s.io/controller-runtime" 41 | "sigs.k8s.io/controller-runtime/pkg/healthz" 42 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 43 | 44 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 45 | "gitlab.com/bitspur/rock8s/integration-operator/controllers" 46 | //+kubebuilder:scaffold:imports 47 | ) 48 | 49 | var ( 50 | scheme = runtime.NewScheme() 51 | setupLog = ctrl.Log.WithName("setup") 52 | ) 53 | 54 | func init() { 55 | utilruntime.Must(clientgoscheme.AddToScheme(scheme)) 56 | 57 | utilruntime.Must(integrationv1beta1.AddToScheme(scheme)) 58 | //+kubebuilder:scaffold:scheme 59 | } 60 | 61 | //+kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;watch;create;update;patch;delete 62 | //+kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;update;patch;delete 63 | //+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=impersonate 64 | //+kubebuilder:rbac:groups="",resources=services;pods,verbs=create;delete;get;list;patch;update;watch 65 | //+kubebuilder:rbac:groups="",resources=configmaps;secrets,verbs=get;list;watch 66 | 67 | func main() { 68 | var enableLeaderElection bool 69 | var metricsAddr string 70 | var probeAddr string 71 | flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") 72 | flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") 73 | flag.BoolVar(&enableLeaderElection, "leader-elect", false, 74 | "Enable leader election for controller manager. "+ 75 | "Enabling this will ensure there is only one active controller manager.") 76 | opts := zap.Options{ 77 | Development: true, 78 | } 79 | opts.BindFlags(flag.CommandLine) 80 | flag.Parse() 81 | 82 | ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) 83 | 84 | mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ 85 | Scheme: scheme, 86 | MetricsBindAddress: metricsAddr, 87 | Port: 9443, 88 | HealthProbeBindAddress: probeAddr, 89 | LeaderElection: enableLeaderElection, 90 | LeaderElectionID: "cb0904ca.rock8s.com", 91 | Namespace: os.Getenv("WATCH_NAMESPACE"), 92 | // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily 93 | // when the Manager ends. This requires the binary to immediately end when the 94 | // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly 95 | // speeds up voluntary leader transitions as the new leader don't have to wait 96 | // LeaseDuration time first. 97 | // 98 | // In the default scaffold provided, the program ends immediately after 99 | // the manager stops, so would be fine to enable this option. However, 100 | // if you are doing or is intended to do any operation such as perform cleanups 101 | // after the manager stops then its usage might be unsafe. 102 | // LeaderElectionReleaseOnCancel: true, 103 | }) 104 | if err != nil { 105 | setupLog.Error(err, "unable to start manager") 106 | os.Exit(1) 107 | } 108 | 109 | if err = (&controllers.SocketReconciler{ 110 | Client: mgr.GetClient(), 111 | Scheme: mgr.GetScheme(), 112 | Recorder: mgr.GetEventRecorderFor("socket-controller"), 113 | }).SetupWithManager(mgr); err != nil { 114 | setupLog.Error(err, "unable to create controller", "controller", "Socket") 115 | os.Exit(1) 116 | } 117 | if err = (&controllers.PlugReconciler{ 118 | Client: mgr.GetClient(), 119 | Scheme: mgr.GetScheme(), 120 | Recorder: mgr.GetEventRecorderFor("plug-controller"), 121 | }).SetupWithManager(mgr); err != nil { 122 | setupLog.Error(err, "unable to create controller", "controller", "Plug") 123 | os.Exit(1) 124 | } 125 | if err = (&controllers.DeferredResourceReconciler{ 126 | Client: mgr.GetClient(), 127 | Scheme: mgr.GetScheme(), 128 | }).SetupWithManager(mgr); err != nil { 129 | setupLog.Error(err, "unable to create controller", "controller", "DeferredResource") 130 | os.Exit(1) 131 | } 132 | //+kubebuilder:scaffold:builder 133 | 134 | if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { 135 | setupLog.Error(err, "unable to set up health check") 136 | os.Exit(1) 137 | } 138 | if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { 139 | setupLog.Error(err, "unable to set up ready check") 140 | os.Exit(1) 141 | } 142 | 143 | setupLog.Info("starting manager") 144 | if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { 145 | setupLog.Error(err, "problem running manager") 146 | os.Exit(1) 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /api/v1beta1/socket_types.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /api/v1beta1/socket_types.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 10:50:35 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package v1beta1 28 | 29 | import ( 30 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 | "k8s.io/apimachinery/pkg/types" 32 | ) 33 | 34 | // SocketSpec defines the desired state of Socket 35 | type SocketSpec struct { 36 | // interface 37 | Interface *Interface `json:"interface,omitempty"` 38 | 39 | // limit 40 | Limit int32 `json:"limit,omitempty"` 41 | 42 | // vars 43 | Vars []*Var `json:"vars,omitempty" yaml:"vars,omitempty"` 44 | 45 | // result vars 46 | ResultVars []*Var `json:"resultVars,omitempty" yaml:"vars,omitempty"` 47 | 48 | // data 49 | Data map[string]string `json:"data,omitempty"` 50 | 51 | // data configmap name 52 | DataConfigMapName string `json:"dataConfigMapName,omitempty"` 53 | 54 | // data secret name 55 | DataSecretName string `json:"dataSecretName,omitempty"` 56 | 57 | // config 58 | Config map[string]string `json:"config,omitempty"` 59 | 60 | // config configmap name 61 | ConfigConfigMapName string `json:"configConfigMapName,omitempty"` 62 | 63 | // config secret name 64 | ConfigSecretName string `json:"configSecretName,omitempty"` 65 | 66 | // config template 67 | ConfigTemplate map[string]string `json:"configTemplate,omitempty"` 68 | 69 | // result 70 | Result map[string]string `json:"result,omitempty"` 71 | 72 | // result configmap name 73 | ResultConfigMapName string `json:"resultConfigMapName,omitempty"` 74 | 75 | // result secret name 76 | ResultSecretName string `json:"resultSecretName,omitempty"` 77 | 78 | // result template 79 | ResultTemplate map[string]string `json:"resultTemplate,omitempty"` 80 | 81 | // ServiceAccountName is the name of the ServiceAccount to use to run integrations. 82 | // More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ 83 | // +optional 84 | ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"bytes,8,opt,name=serviceAccountName"` 85 | 86 | // apparatus 87 | Apparatus *SpecApparatus `json:"apparatus,omitempty"` 88 | 89 | // resources 90 | Resources []*Resource `json:"resources,omitempty"` 91 | 92 | // result resources 93 | ResultResources []*ResourceAction `json:"resultResources,omitempty"` 94 | 95 | // change epoch to force an update 96 | Epoch string `json:"epoch,omitempty"` 97 | 98 | // validation 99 | Validation *SocketSpecValidation `json:"validation,omitempty"` 100 | } 101 | 102 | type Interface struct { 103 | // config interface 104 | Config *ConfigInterface `json:"config,omitempty"` 105 | 106 | // result interface 107 | Result *ResultInterface `json:"result,omitempty"` 108 | } 109 | 110 | type ConfigInterface struct { 111 | // plug config properties 112 | Plug map[string]*SchemaProperty `json:"plug,omitempty"` 113 | 114 | // socket config properties 115 | Socket map[string]*SchemaProperty `json:"socket,omitempty"` 116 | } 117 | 118 | type ResultInterface struct { 119 | // plug result properties 120 | Plug map[string]*SchemaProperty `json:"plug,omitempty"` 121 | 122 | // socket result properties 123 | Socket map[string]*SchemaProperty `json:"socket,omitempty"` 124 | } 125 | 126 | type SchemaProperty struct { 127 | Default string `json:"default,omitempty"` 128 | Description string `json:"description,omitempty"` 129 | Required bool `json:"required,omitempty"` 130 | } 131 | 132 | type SocketSpecValidation struct { 133 | // namespace whitelist 134 | NamespaceWhitelist []string `json:"namespaceWhitelist,omitempty"` 135 | 136 | // namespace blacklist 137 | NamespaceBlacklist []string `json:"namespaceBlacklist,omitempty"` 138 | } 139 | 140 | // SocketStatus defines the observed state of Socket 141 | type SocketStatus struct { 142 | // Conditions represent the latest available observations of an object's state 143 | Conditions []metav1.Condition `json:"conditions,omitempty"` 144 | 145 | // plugs coupled to socket 146 | CoupledPlugs []*CoupledPlug `json:"coupledPlugs,omitempty"` 147 | } 148 | 149 | type CoupledPlug struct { 150 | // API version of the plug 151 | APIVersion string `json:"apiVersion"` 152 | 153 | // Kind of the plug 154 | Kind string `json:"kind"` 155 | 156 | // Name of the plug 157 | Name string `json:"name"` 158 | 159 | // Namespace of the plug 160 | Namespace string `json:"namespace"` 161 | 162 | // UID of the plug 163 | UID types.UID `json:"uid"` 164 | } 165 | 166 | //+kubebuilder:object:root=true 167 | //+kubebuilder:subresource:status 168 | 169 | // Socket is the Schema for the sockets API 170 | type Socket struct { 171 | metav1.TypeMeta `json:",inline"` 172 | metav1.ObjectMeta `json:"metadata,omitempty"` 173 | 174 | Spec SocketSpec `json:"spec,omitempty"` 175 | Status SocketStatus `json:"status,omitempty"` 176 | } 177 | 178 | //+kubebuilder:object:root=true 179 | 180 | // SocketList contains a list of Socket 181 | type SocketList struct { 182 | metav1.TypeMeta `json:",inline"` 183 | metav1.ListMeta `json:"metadata,omitempty"` 184 | Items []Socket `json:"items"` 185 | } 186 | 187 | func init() { 188 | SchemeBuilder.Register(&Socket{}, &SocketList{}) 189 | } 190 | -------------------------------------------------------------------------------- /controllers/deferredresource_controller.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /controllers/deferredresource_controller.go 3 | * Project: integration-operator 4 | * File Created: 17-12-2023 03:35:18 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | /* 28 | Copyright 2023. 29 | 30 | Licensed under the Apache License, Version 2.0 (the "License"); 31 | you may not use this file except in compliance with the License. 32 | You may obtain a copy of the License at 33 | 34 | http://www.apache.org/licenses/LICENSE-2.0 35 | 36 | Unless required by applicable law or agreed to in writing, software 37 | distributed under the License is distributed on an "AS IS" BASIS, 38 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 39 | See the License for the specific language governing permissions and 40 | limitations under the License. 41 | */ 42 | 43 | package controllers 44 | 45 | import ( 46 | "context" 47 | "encoding/json" 48 | "os" 49 | "strconv" 50 | "time" 51 | 52 | "k8s.io/apimachinery/pkg/runtime" 53 | ctrl "sigs.k8s.io/controller-runtime" 54 | "sigs.k8s.io/controller-runtime/pkg/client" 55 | "sigs.k8s.io/controller-runtime/pkg/controller" 56 | "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 57 | "sigs.k8s.io/controller-runtime/pkg/log" 58 | 59 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 60 | "gitlab.com/bitspur/rock8s/integration-operator/util" 61 | k8serrors "k8s.io/apimachinery/pkg/api/errors" 62 | ) 63 | 64 | // DeferredResourceReconciler reconciles a DeferredResource object 65 | type DeferredResourceReconciler struct { 66 | client.Client 67 | Scheme *runtime.Scheme 68 | } 69 | 70 | //+kubebuilder:rbac:groups=integration.rock8s.com,resources=deferredresources,verbs=get;list;watch;create;update;patch;delete 71 | //+kubebuilder:rbac:groups=integration.rock8s.com,resources=deferredresources/status,verbs=get;update;patch 72 | //+kubebuilder:rbac:groups=integration.rock8s.com,resources=deferredresources/finalizers,verbs=update 73 | 74 | func (r *DeferredResourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 75 | logger := log.FromContext(ctx) 76 | logger.V(1).Info("DeferredResource Reconcile") 77 | deferredResourceUtil := util.NewDeferredResourceUtil(&r.Client, ctx, &req, &integrationv1beta1.NamespacedName{ 78 | Name: req.NamespacedName.Name, 79 | Namespace: req.NamespacedName.Namespace, 80 | }) 81 | deferredResource, err := deferredResourceUtil.Get() 82 | if err != nil { 83 | return ctrl.Result{}, client.IgnoreNotFound(err) 84 | } 85 | kubectlUtil := util.NewKubectlUtil( 86 | ctx, deferredResource.Namespace, 87 | util.EnsureServiceAccount(deferredResource.Spec.ServiceAccountName), 88 | ) 89 | 90 | if deferredResource.GetDeletionTimestamp() != nil { 91 | if controllerutil.ContainsFinalizer(deferredResource, integrationv1beta1.Finalizer) { 92 | if err := deferredResourceUtil.DeleteResource(deferredResource, kubectlUtil); err != nil { 93 | return deferredResourceUtil.Error(err, deferredResource) 94 | } 95 | controllerutil.RemoveFinalizer(deferredResource, integrationv1beta1.Finalizer) 96 | return deferredResourceUtil.Update(deferredResource, true) 97 | } 98 | return ctrl.Result{}, nil 99 | } 100 | 101 | if deferredResource.Spec.Timeout > 0 { 102 | if time.Since(deferredResource.CreationTimestamp.Time) < time.Duration(deferredResource.Spec.Timeout)*time.Second { 103 | return deferredResourceUtil.UpdateResolvedStatus( 104 | util.DeferredResourcePending, 105 | deferredResource, 106 | nil, 107 | "waiting for timeout", 108 | deferredResource.Spec.Timeout, 109 | ) 110 | } 111 | } 112 | 113 | if deferredResource.Spec.WaitFor != nil { 114 | for _, waitFor := range *deferredResource.Spec.WaitFor { 115 | apiVersion := waitFor.APIVersion 116 | if apiVersion == "" { 117 | group := "" 118 | if waitFor.Group != "" { 119 | group = waitFor.Group + "/" 120 | } 121 | apiVersion = group + waitFor.Version 122 | } 123 | body, err := json.Marshal( 124 | map[string]interface{}{ 125 | "apiVersion": apiVersion, 126 | "kind": waitFor.Kind, 127 | "metadata": map[string]interface{}{ 128 | "name": waitFor.Name, 129 | "namespace": deferredResource.Namespace, 130 | }, 131 | }, 132 | ) 133 | if err != nil { 134 | return deferredResourceUtil.Error(err, deferredResource) 135 | } 136 | if _, err := kubectlUtil.Get(body); err != nil { 137 | if k8serrors.IsNotFound(err) { 138 | return deferredResourceUtil.UpdateResolvedStatus( 139 | util.DeferredResourcePending, 140 | deferredResource, 141 | nil, 142 | "waiting for resource", 143 | 1, 144 | ) 145 | } 146 | return deferredResourceUtil.Error(err, deferredResource) 147 | } 148 | } 149 | } 150 | 151 | return deferredResourceUtil.ApplyResource(deferredResource, kubectlUtil) 152 | } 153 | 154 | // SetupWithManager sets up the controller with the Manager. 155 | func (r *DeferredResourceReconciler) SetupWithManager(mgr ctrl.Manager) error { 156 | maxConcurrentReconciles := 3 157 | if value := os.Getenv("MAX_CONCURRENT_RECONCILES"); value != "" { 158 | if val, err := strconv.Atoi(value); err == nil { 159 | maxConcurrentReconciles = val 160 | } 161 | } 162 | return ctrl.NewControllerManagedBy(mgr). 163 | WithOptions(controller.Options{MaxConcurrentReconciles: maxConcurrentReconciles}). 164 | For(&integrationv1beta1.DeferredResource{}). 165 | Complete(r) 166 | } 167 | -------------------------------------------------------------------------------- /controllers/socket_controller.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /controllers/socket_controller.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 10:50:35 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package controllers 28 | 29 | import ( 30 | "context" 31 | "os" 32 | "strconv" 33 | "time" 34 | 35 | "k8s.io/apimachinery/pkg/api/errors" 36 | "k8s.io/apimachinery/pkg/runtime" 37 | "k8s.io/client-go/tools/record" 38 | ctrl "sigs.k8s.io/controller-runtime" 39 | "sigs.k8s.io/controller-runtime/pkg/client" 40 | "sigs.k8s.io/controller-runtime/pkg/controller" 41 | "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 42 | "sigs.k8s.io/controller-runtime/pkg/event" 43 | "sigs.k8s.io/controller-runtime/pkg/log" 44 | "sigs.k8s.io/controller-runtime/pkg/predicate" 45 | 46 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 47 | "gitlab.com/bitspur/rock8s/integration-operator/coupler" 48 | "gitlab.com/bitspur/rock8s/integration-operator/util" 49 | ) 50 | 51 | // SocketReconciler reconciles a Socket object 52 | type SocketReconciler struct { 53 | client.Client 54 | Scheme *runtime.Scheme 55 | Recorder record.EventRecorder 56 | } 57 | 58 | //+kubebuilder:rbac:groups=integration.rock8s.com,resources=sockets,verbs=get;list;watch;create;update;patch;delete 59 | //+kubebuilder:rbac:groups=integration.rock8s.com,resources=sockets/status,verbs=get;update;patch 60 | //+kubebuilder:rbac:groups=integration.rock8s.com,resources=sockets/finalizers,verbs=update 61 | 62 | func (r *SocketReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 63 | logger := log.FromContext(ctx) 64 | logger.V(1).Info("Socket Reconcile") 65 | socketUtil := util.NewSocketUtil(&r.Client, ctx, &req, &integrationv1beta1.NamespacedName{ 66 | Name: req.NamespacedName.Name, 67 | Namespace: req.NamespacedName.Namespace, 68 | }) 69 | socket, err := socketUtil.Get() 70 | if err != nil { 71 | return ctrl.Result{}, client.IgnoreNotFound(err) 72 | } 73 | 74 | if socket.GetDeletionTimestamp() != nil { 75 | if controllerutil.ContainsFinalizer(socket, integrationv1beta1.Finalizer) { 76 | coupledPlugs := socket.Status.CoupledPlugs 77 | if len(coupledPlugs) > 0 { 78 | for _, coupledPlug := range coupledPlugs { 79 | if _, err := socketUtil.RemoveCoupledPlugStatus(coupledPlug.UID, socket); err != nil { 80 | return socketUtil.Error(err, socket) 81 | } 82 | } 83 | if _, err := socketUtil.UpdateCoupledStatus(util.SocketEmpty, socket, nil, true); err != nil { 84 | return socketUtil.Error(err, socket) 85 | } 86 | for _, coupledPlug := range coupledPlugs { 87 | plugUtil := util.NewPlugUtil(&r.Client, ctx, &req, &integrationv1beta1.NamespacedName{ 88 | Name: coupledPlug.Name, 89 | Namespace: coupledPlug.Namespace, 90 | }, socket) 91 | plug, err := plugUtil.Get() 92 | if err != nil { 93 | if errors.IsNotFound(err) { 94 | continue 95 | } 96 | return socketUtil.Error(err, socket) 97 | } 98 | if _, err := plugUtil.Delete(plug); err != nil { 99 | return socketUtil.Error(err, socket) 100 | } 101 | } 102 | return ctrl.Result{Requeue: true, RequeueAfter: 0}, nil 103 | } 104 | if socket == nil { 105 | return ctrl.Result{}, nil 106 | } 107 | if err := coupler.DeletedSocket(socket, r.Recorder); err != nil { 108 | return socketUtil.Error(err, socket) 109 | } 110 | controllerutil.RemoveFinalizer(socket, integrationv1beta1.Finalizer) 111 | return socketUtil.Update(socket, true) 112 | } 113 | return ctrl.Result{}, nil 114 | } 115 | 116 | if !controllerutil.ContainsFinalizer(socket, integrationv1beta1.Finalizer) { 117 | controllerutil.AddFinalizer(socket, integrationv1beta1.Finalizer) 118 | return socketUtil.Update(socket, true) 119 | } 120 | 121 | coupledCondition, err := socketUtil.GetCoupledCondition(socket) 122 | if err != nil { 123 | return socketUtil.Error(err, socket) 124 | } 125 | if coupledCondition == nil { 126 | if err := coupler.CreatedSocket(socket, r.Recorder); err != nil { 127 | return socketUtil.Error(err, socket) 128 | } 129 | return socketUtil.UpdateCoupledStatus(util.SocketCreated, socket, nil, true) 130 | } 131 | 132 | setSocketStatus := false 133 | for _, coupledPlug := range socket.Status.CoupledPlugs { 134 | plugUtil := util.NewPlugUtil(&r.Client, ctx, &req, &integrationv1beta1.NamespacedName{ 135 | Name: coupledPlug.Name, 136 | Namespace: coupledPlug.Namespace, 137 | }, socket) 138 | if _, err := plugUtil.Get(); err != nil { 139 | if errors.IsNotFound(err) { 140 | if _, err := socketUtil.RemoveCoupledPlugStatus(coupledPlug.UID, socket); err != nil { 141 | return socketUtil.Error(err, socket) 142 | } 143 | setSocketStatus = true 144 | continue 145 | } 146 | return socketUtil.Error(err, socket) 147 | } 148 | } 149 | if setSocketStatus { 150 | return socketUtil.UpdateStatus(socket, true) 151 | } 152 | 153 | for _, coupledPlug := range socket.Status.CoupledPlugs { 154 | plugUtil := util.NewPlugUtil(&r.Client, ctx, &req, &integrationv1beta1.NamespacedName{ 155 | Name: coupledPlug.Name, 156 | Namespace: coupledPlug.Namespace, 157 | }, socket) 158 | plug, err := plugUtil.Get() 159 | if err != nil { 160 | return socketUtil.Error(err, socket) 161 | } 162 | plug.Spec.Epoch = strconv.FormatInt(time.Now().Unix(), 10) 163 | if _, err := plugUtil.Update(plug, false); err != nil { 164 | return socketUtil.Error(err, socket) 165 | } 166 | } 167 | 168 | return socketUtil.UpdateCoupledStatus(util.SocketCoupled, socket, nil, false) 169 | } 170 | 171 | func filterSocketPredicate() predicate.Predicate { 172 | return predicate.Funcs{ 173 | UpdateFunc: func(e event.UpdateEvent) bool { 174 | return e.ObjectNew.GetDeletionTimestamp() != nil || e.ObjectNew.GetGeneration() > e.ObjectOld.GetGeneration() 175 | }, 176 | DeleteFunc: func(e event.DeleteEvent) bool { 177 | return !e.DeleteStateUnknown 178 | }, 179 | } 180 | } 181 | 182 | // SetupWithManager sets up the controller with the Manager. 183 | func (r *SocketReconciler) SetupWithManager(mgr ctrl.Manager) error { 184 | maxConcurrentReconciles := 3 185 | if value := os.Getenv("MAX_CONCURRENT_RECONCILES"); value != "" { 186 | if val, err := strconv.Atoi(value); err == nil { 187 | maxConcurrentReconciles = val 188 | } 189 | } 190 | return ctrl.NewControllerManagedBy(mgr). 191 | WithOptions(controller.Options{MaxConcurrentReconciles: maxConcurrentReconciles}). 192 | WithEventFilter(filterSocketPredicate()). 193 | For(&integrationv1beta1.Socket{}). 194 | Complete(r) 195 | } 196 | -------------------------------------------------------------------------------- /util/event.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /util/event.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 13:49:54 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package util 28 | 29 | import ( 30 | "context" 31 | "fmt" 32 | 33 | "github.com/go-logr/logr" 34 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 35 | "k8s.io/client-go/tools/record" 36 | "sigs.k8s.io/controller-runtime/pkg/log" 37 | ) 38 | 39 | type EventUtil struct { 40 | apparatusUtil *ApparatusUtil 41 | resourceUtil *ResourceUtil 42 | logger logr.Logger 43 | } 44 | 45 | func NewEventUtil( 46 | ctx context.Context, 47 | ) *EventUtil { 48 | return &EventUtil{ 49 | apparatusUtil: NewApparatusUtil(ctx), 50 | resourceUtil: NewResourceUtil(ctx), 51 | logger: log.FromContext(ctx), 52 | } 53 | } 54 | 55 | func (u *EventUtil) PlugCreated( 56 | plug *integrationv1beta1.Plug, 57 | recorder record.EventRecorder, 58 | ) error { 59 | u.logger.Info(fmt.Sprintf("plug %s/%s created", plug.Name, plug.Namespace)) 60 | if err := u.apparatusUtil.PlugCreated(plug); err != nil { 61 | return err 62 | } 63 | if err := u.resourceUtil.PlugCreated(plug); err != nil { 64 | return err 65 | } 66 | recorder.Event(plug, "Normal", "PlugCreated", fmt.Sprintf("plug %s/%s created", plug.Name, plug.Namespace)) 67 | return nil 68 | } 69 | 70 | func (u *EventUtil) PlugCoupled( 71 | plug *integrationv1beta1.Plug, 72 | socket *integrationv1beta1.Socket, 73 | plugConfig *Config, 74 | socketConfig *Config, 75 | recorder record.EventRecorder, 76 | ) error { 77 | u.logger.Info(fmt.Sprintf("plug %s/%s coupled", plug.Name, plug.Namespace)) 78 | if err := u.apparatusUtil.PlugCoupled(plug, socket, plugConfig, socketConfig); err != nil { 79 | return err 80 | } 81 | if err := u.resourceUtil.PlugCoupled(plug, socket, plugConfig, socketConfig); err != nil { 82 | return err 83 | } 84 | recorder.Event(plug, "Normal", "PlugCoupled", fmt.Sprintf("plug %s/%s coupled", plug.Name, plug.Namespace)) 85 | return nil 86 | } 87 | 88 | func (u *EventUtil) PlugUpdated( 89 | plug *integrationv1beta1.Plug, 90 | socket *integrationv1beta1.Socket, 91 | plugConfig *Config, 92 | socketConfig *Config, 93 | recorder record.EventRecorder, 94 | ) error { 95 | u.logger.Info(fmt.Sprintf("plug %s/%s updated", plug.Name, plug.Namespace)) 96 | if err := u.apparatusUtil.PlugUpdated(plug, socket, plugConfig, socketConfig); err != nil { 97 | return err 98 | } 99 | if err := u.resourceUtil.PlugUpdated(plug, socket, plugConfig, socketConfig); err != nil { 100 | return err 101 | } 102 | recorder.Event(plug, "Normal", "PlugUpdated", fmt.Sprintf("plug %s/%s updated", plug.Name, plug.Namespace)) 103 | return nil 104 | } 105 | 106 | func (u *EventUtil) PlugDecoupled( 107 | plug *integrationv1beta1.Plug, 108 | socket *integrationv1beta1.Socket, 109 | plugConfig *Config, 110 | socketConfig *Config, 111 | recorder record.EventRecorder, 112 | ) error { 113 | u.logger.Info(fmt.Sprintf("plug %s/%s decoupled", plug.Name, plug.Namespace)) 114 | if err := u.apparatusUtil.PlugDecoupled(plug, socket, plugConfig, socketConfig); err != nil { 115 | return err 116 | } 117 | if err := u.resourceUtil.PlugDecoupled(plug, socket, plugConfig, socketConfig); err != nil { 118 | return err 119 | } 120 | recorder.Event(plug, "Normal", "PlugDecoupled", fmt.Sprintf("plug %s/%s decoupled", plug.Name, plug.Namespace)) 121 | return nil 122 | } 123 | 124 | func (u *EventUtil) PlugDeleted( 125 | plug *integrationv1beta1.Plug, 126 | recorder record.EventRecorder, 127 | ) error { 128 | u.logger.Info(fmt.Sprintf("plug %s/%s deleted", plug.Name, plug.Namespace)) 129 | if err := u.apparatusUtil.PlugDeleted(plug); err != nil { 130 | return err 131 | } 132 | if err := u.resourceUtil.PlugDeleted(plug); err != nil { 133 | return err 134 | } 135 | recorder.Event(plug, "Normal", "PlugDeleted", fmt.Sprintf("plug %s/%s deleted", plug.Name, plug.Namespace)) 136 | return nil 137 | } 138 | 139 | func (u *EventUtil) SocketCreated( 140 | socket *integrationv1beta1.Socket, 141 | recorder record.EventRecorder, 142 | ) error { 143 | u.logger.Info(fmt.Sprintf("socket %s/%s created", socket.Name, socket.Namespace)) 144 | if err := u.apparatusUtil.SocketCreated(socket); err != nil { 145 | return err 146 | } 147 | if err := u.resourceUtil.SocketCreated(socket); err != nil { 148 | return err 149 | } 150 | recorder.Event(socket, "Normal", "SocketCreated", fmt.Sprintf("socket %s/%s created", socket.Name, socket.Namespace)) 151 | return nil 152 | } 153 | 154 | func (u *EventUtil) SocketCoupled( 155 | plug *integrationv1beta1.Plug, 156 | socket *integrationv1beta1.Socket, 157 | plugConfig *Config, 158 | socketConfig *Config, 159 | recorder record.EventRecorder, 160 | ) error { 161 | u.logger.Info(fmt.Sprintf("socket %s/%s coupled", socket.Name, socket.Namespace)) 162 | if err := u.apparatusUtil.SocketCoupled(plug, socket, plugConfig, socketConfig); err != nil { 163 | return err 164 | } 165 | if err := u.resourceUtil.SocketCoupled(plug, socket, plugConfig, socketConfig); err != nil { 166 | return err 167 | } 168 | recorder.Event(socket, "Normal", "SocketCoupled", fmt.Sprintf("socket %s/%s coupled", socket.Name, socket.Namespace)) 169 | return nil 170 | } 171 | 172 | func (u *EventUtil) SocketUpdated( 173 | plug *integrationv1beta1.Plug, 174 | socket *integrationv1beta1.Socket, 175 | plugConfig *Config, 176 | socketConfig *Config, 177 | recorder record.EventRecorder, 178 | ) error { 179 | u.logger.Info(fmt.Sprintf("socket %s/%s updated", socket.Name, socket.Namespace)) 180 | if err := u.apparatusUtil.SocketUpdated(plug, socket, plugConfig, socketConfig); err != nil { 181 | return err 182 | } 183 | if err := u.resourceUtil.SocketUpdated(plug, socket, plugConfig, socketConfig); err != nil { 184 | return err 185 | } 186 | recorder.Event(socket, "Normal", "SocketUpdated", fmt.Sprintf("socket %s/%s updated", socket.Name, socket.Namespace)) 187 | return nil 188 | } 189 | 190 | func (u *EventUtil) SocketDecoupled( 191 | plug *integrationv1beta1.Plug, 192 | socket *integrationv1beta1.Socket, 193 | plugConfig *Config, 194 | socketConfig *Config, 195 | recorder record.EventRecorder, 196 | ) error { 197 | u.logger.Info(fmt.Sprintf("socket %s/%s decoupled", socket.Name, socket.Namespace)) 198 | if err := u.apparatusUtil.SocketDecoupled(plug, socket, plugConfig, socketConfig); err != nil { 199 | return err 200 | } 201 | if err := u.resourceUtil.SocketDecoupled(plug, socket, plugConfig, socketConfig); err != nil { 202 | return err 203 | } 204 | recorder.Event(socket, "Normal", "SocketDecoupled", fmt.Sprintf("socket %s/%s decoupled", socket.Name, socket.Namespace)) 205 | return nil 206 | } 207 | 208 | func (u *EventUtil) SocketDeleted( 209 | socket *integrationv1beta1.Socket, 210 | recorder record.EventRecorder, 211 | ) error { 212 | u.logger.Info(fmt.Sprintf("socket %s/%s deleted", socket.Name, socket.Namespace)) 213 | if err := u.apparatusUtil.SocketDeleted(socket); err != nil { 214 | return err 215 | } 216 | if err := u.resourceUtil.SocketDeleted(socket); err != nil { 217 | return err 218 | } 219 | recorder.Event(socket, "Normal", "SocketDeleted", fmt.Sprintf("socket %s/%s deleted", socket.Name, socket.Namespace)) 220 | return nil 221 | } 222 | -------------------------------------------------------------------------------- /config/crd/bases/integration.rock8s.com_deferredresources.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.11.1 7 | creationTimestamp: null 8 | name: deferredresources.integration.rock8s.com 9 | spec: 10 | group: integration.rock8s.com 11 | names: 12 | kind: DeferredResource 13 | listKind: DeferredResourceList 14 | plural: deferredresources 15 | singular: deferredresource 16 | scope: Namespaced 17 | versions: 18 | - name: v1beta1 19 | schema: 20 | openAPIV3Schema: 21 | description: DeferredResource is the Schema for the deferredresources API 22 | properties: 23 | apiVersion: 24 | description: 'APIVersion defines the versioned schema of this representation 25 | of an object. Servers should convert recognized schemas to the latest 26 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 27 | type: string 28 | kind: 29 | description: 'Kind is a string value representing the REST resource this 30 | object represents. Servers may infer this from the endpoint the client 31 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 32 | type: string 33 | metadata: 34 | type: object 35 | spec: 36 | description: DeferredResourceSpec defines the desired state of DeferredResource 37 | properties: 38 | resource: 39 | description: Resource is the resource to create after the defer is 40 | resolved 41 | x-kubernetes-preserve-unknown-fields: true 42 | serviceAccountName: 43 | description: 'ServiceAccountName is the name of the ServiceAccount 44 | to use to create deferred resources from. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/' 45 | type: string 46 | timeout: 47 | description: Timeout is the maximum time to wait before creating the 48 | resource 49 | format: int64 50 | type: integer 51 | waitFor: 52 | description: WaitFor is a list of resources to wait for before creating 53 | the resource 54 | items: 55 | description: Target refers to a kubernetes object by Group, Version, 56 | Kind and Name gvk.Gvk contains Group, Version and Kind APIVersion 57 | is added to keep the backward compatibility of using ObjectReference 58 | for Var.ObjRef 59 | properties: 60 | apiVersion: 61 | type: string 62 | group: 63 | type: string 64 | kind: 65 | type: string 66 | name: 67 | type: string 68 | version: 69 | type: string 70 | type: object 71 | type: array 72 | type: object 73 | status: 74 | description: DeferredResourceStatus defines the observed state of DeferredResource 75 | properties: 76 | conditions: 77 | items: 78 | description: "Condition contains details for one aspect of the current 79 | state of this API Resource. --- This struct is intended for direct 80 | use as an array at the field path .status.conditions. For example, 81 | \n type FooStatus struct{ // Represents the observations of a 82 | foo's current state. // Known .status.conditions.type are: \"Available\", 83 | \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge 84 | // +listType=map // +listMapKey=type Conditions []metav1.Condition 85 | `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" 86 | protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" 87 | properties: 88 | lastTransitionTime: 89 | description: lastTransitionTime is the last time the condition 90 | transitioned from one status to another. This should be when 91 | the underlying condition changed. If that is not known, then 92 | using the time when the API field changed is acceptable. 93 | format: date-time 94 | type: string 95 | message: 96 | description: message is a human readable message indicating 97 | details about the transition. This may be an empty string. 98 | maxLength: 32768 99 | type: string 100 | observedGeneration: 101 | description: observedGeneration represents the .metadata.generation 102 | that the condition was set based upon. For instance, if .metadata.generation 103 | is currently 12, but the .status.conditions[x].observedGeneration 104 | is 9, the condition is out of date with respect to the current 105 | state of the instance. 106 | format: int64 107 | minimum: 0 108 | type: integer 109 | reason: 110 | description: reason contains a programmatic identifier indicating 111 | the reason for the condition's last transition. Producers 112 | of specific condition types may define expected values and 113 | meanings for this field, and whether the values are considered 114 | a guaranteed API. The value should be a CamelCase string. 115 | This field may not be empty. 116 | maxLength: 1024 117 | minLength: 1 118 | pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ 119 | type: string 120 | status: 121 | description: status of the condition, one of True, False, Unknown. 122 | enum: 123 | - "True" 124 | - "False" 125 | - Unknown 126 | type: string 127 | type: 128 | description: type of condition in CamelCase or in foo.example.com/CamelCase. 129 | --- Many .condition.type values are consistent across resources 130 | like Available, but because arbitrary conditions can be useful 131 | (see .node.status.conditions), the ability to deconflict is 132 | important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) 133 | maxLength: 316 134 | 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])$ 135 | type: string 136 | required: 137 | - lastTransitionTime 138 | - message 139 | - reason 140 | - status 141 | - type 142 | type: object 143 | type: array 144 | ownerReferences: 145 | description: OwnerReference contains enough information to let you 146 | identify an owning object. An owning object must be in the same 147 | namespace as the dependent, or be cluster-scoped, so there is no 148 | namespace field. 149 | properties: 150 | apiVersion: 151 | description: API version of the referent. 152 | type: string 153 | blockOwnerDeletion: 154 | description: If true, AND if the owner has the "foregroundDeletion" 155 | finalizer, then the owner cannot be deleted from the key-value 156 | store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion 157 | for how the garbage collector interacts with this field and 158 | enforces the foreground deletion. Defaults to false. To set 159 | this field, a user needs "delete" permission of the owner, otherwise 160 | 422 (Unprocessable Entity) will be returned. 161 | type: boolean 162 | controller: 163 | description: If true, this reference points to the managing controller. 164 | type: boolean 165 | kind: 166 | description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 167 | type: string 168 | name: 169 | description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' 170 | type: string 171 | uid: 172 | description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids' 173 | type: string 174 | required: 175 | - apiVersion 176 | - kind 177 | - name 178 | - uid 179 | type: object 180 | x-kubernetes-map-type: atomic 181 | type: object 182 | type: object 183 | served: true 184 | storage: true 185 | subresources: 186 | status: {} 187 | -------------------------------------------------------------------------------- /util/plug.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /util/plug.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 13:49:54 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package util 28 | 29 | import ( 30 | "context" 31 | "strings" 32 | "time" 33 | 34 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 35 | "k8s.io/apimachinery/pkg/api/meta" 36 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 37 | "k8s.io/apimachinery/pkg/types" 38 | "k8s.io/apiserver/pkg/registry/generic/registry" 39 | ctrl "sigs.k8s.io/controller-runtime" 40 | "sigs.k8s.io/controller-runtime/pkg/client" 41 | ) 42 | 43 | type PlugUtil struct { 44 | apparatusUtil *ApparatusUtil 45 | client *client.Client 46 | ctx context.Context 47 | namespacedName types.NamespacedName 48 | req *ctrl.Request 49 | resultUtil *ResultUtil 50 | socket *integrationv1beta1.Socket 51 | } 52 | 53 | func NewPlugUtil( 54 | client *client.Client, 55 | ctx context.Context, 56 | req *ctrl.Request, 57 | namespacedName *integrationv1beta1.NamespacedName, 58 | socket *integrationv1beta1.Socket, 59 | ) *PlugUtil { 60 | operatorNamespace := GetOperatorNamespace() 61 | return &PlugUtil{ 62 | apparatusUtil: NewApparatusUtil(ctx), 63 | client: client, 64 | ctx: ctx, 65 | namespacedName: EnsureNamespacedName(namespacedName, operatorNamespace), 66 | req: req, 67 | resultUtil: NewResultUtil(ctx), 68 | socket: socket, 69 | } 70 | } 71 | 72 | func (u *PlugUtil) Get() (*integrationv1beta1.Plug, error) { 73 | client := *u.client 74 | ctx := u.ctx 75 | plug := &integrationv1beta1.Plug{} 76 | if err := client.Get(ctx, u.namespacedName, plug); err != nil { 77 | return nil, err 78 | } 79 | return plug.DeepCopy(), nil 80 | } 81 | 82 | func (u *PlugUtil) Update(plug *integrationv1beta1.Plug, requeue bool) (ctrl.Result, error) { 83 | client := *u.client 84 | ctx := u.ctx 85 | if err := client.Update(ctx, plug); err != nil { 86 | return u.Error(err, plug) 87 | } 88 | if requeue { 89 | return ctrl.Result{Requeue: true, RequeueAfter: 0}, nil 90 | } 91 | return ctrl.Result{}, nil 92 | } 93 | 94 | func (u *PlugUtil) UpdateStatus( 95 | plug *integrationv1beta1.Plug, 96 | requeue bool, 97 | ) (ctrl.Result, error) { 98 | client := *u.client 99 | ctx := u.ctx 100 | if err := client.Status().Update(ctx, plug); err != nil { 101 | if strings.Contains(err.Error(), registry.OptimisticLockErrorMsg) { 102 | return ctrl.Result{Requeue: true}, nil 103 | } 104 | return ctrl.Result{}, err 105 | } 106 | if requeue { 107 | return ctrl.Result{Requeue: true, RequeueAfter: 0}, nil 108 | } 109 | return ctrl.Result{}, nil 110 | } 111 | 112 | func (u *PlugUtil) Delete(plug *integrationv1beta1.Plug) (ctrl.Result, error) { 113 | client := *u.client 114 | ctx := u.ctx 115 | if err := client.Delete(ctx, plug); err != nil { 116 | return u.Error(err, plug) 117 | } 118 | return ctrl.Result{}, nil 119 | } 120 | 121 | func (u *PlugUtil) GetCoupledCondition( 122 | plug *integrationv1beta1.Plug, 123 | ) (*metav1.Condition, error) { 124 | if plug == nil { 125 | var err error 126 | plug, err = u.Get() 127 | if err != nil { 128 | return nil, err 129 | } 130 | } 131 | coupledCondition := meta.FindStatusCondition(plug.Status.Conditions, string(ConditionTypeCoupled)) 132 | return coupledCondition, nil 133 | } 134 | 135 | func (u *PlugUtil) Error( 136 | err error, 137 | plug *integrationv1beta1.Plug, 138 | ) (ctrl.Result, error) { 139 | e := err 140 | if plug == nil { 141 | var err error 142 | plug, err = u.Get() 143 | if err != nil { 144 | return ctrl.Result{}, err 145 | } 146 | } 147 | if u.apparatusUtil.NotRunning(err) { 148 | requeueAfter := time.Duration(time.Second.Nanoseconds() * 10) 149 | started, err := u.apparatusUtil.Start(plug, u.socket, &requeueAfter) 150 | if err != nil { 151 | return u.UpdateErrorStatus(err, plug) 152 | } 153 | if started { 154 | return ctrl.Result{ 155 | Requeue: true, 156 | RequeueAfter: requeueAfter, 157 | }, nil 158 | } 159 | } 160 | result, err := u.UpdateErrorStatus(e, plug) 161 | if strings.Contains(e.Error(), "result property") && 162 | strings.Contains(e.Error(), "is required") { 163 | return ctrl.Result{Requeue: true}, nil 164 | } 165 | return result, err 166 | } 167 | 168 | func (u *PlugUtil) UpdateErrorStatus( 169 | err error, 170 | plug *integrationv1beta1.Plug, 171 | ) (ctrl.Result, error) { 172 | e := err 173 | if plug == nil { 174 | var err error 175 | plug, err = u.Get() 176 | if err != nil { 177 | return ctrl.Result{}, err 178 | } 179 | } 180 | if err = u.setErrorStatus(e, plug); err != nil { 181 | return ctrl.Result{}, err 182 | } 183 | if _, err := u.UpdateStatus(plug, true); err != nil { 184 | return ctrl.Result{}, err 185 | } 186 | if strings.Contains(e.Error(), registry.OptimisticLockErrorMsg) { 187 | return ctrl.Result{Requeue: true}, nil 188 | } 189 | return ctrl.Result{}, e 190 | } 191 | 192 | func (u *PlugUtil) UpdateCoupledStatus( 193 | conditionCoupledReason ConditionCoupledReason, 194 | plug *integrationv1beta1.Plug, 195 | socket *integrationv1beta1.Socket, 196 | requeue bool, 197 | ) (ctrl.Result, error) { 198 | if plug == nil { 199 | var err error 200 | plug, err = u.Get() 201 | if err != nil { 202 | return ctrl.Result{}, err 203 | } 204 | } 205 | if socket != nil { 206 | u.setCoupledSocketStatus(plug, socket) 207 | } 208 | if conditionCoupledReason != "" { 209 | u.setCoupledStatusCondition(conditionCoupledReason, "", plug) 210 | } 211 | return u.UpdateStatus(plug, requeue) 212 | } 213 | 214 | func (u *PlugUtil) UpdateResultStatus( 215 | plug *integrationv1beta1.Plug, 216 | socket *integrationv1beta1.Socket, 217 | plugConfig Config, 218 | socketConfig Config, 219 | ) (ctrl.Result, error) { 220 | coupledResult, err := u.resultUtil.GetResult(plug, socket, plugConfig, socketConfig) 221 | if err != nil { 222 | return u.Error(err, plug) 223 | } 224 | if err := u.resultUtil.SocketTemplateResultResources( 225 | plug, 226 | socket, 227 | plugConfig, 228 | socketConfig, 229 | coupledResult.Plug, 230 | coupledResult.Socket, 231 | ); err != nil { 232 | return u.Error(err, plug) 233 | } 234 | if err := u.resultUtil.PlugTemplateResultResources( 235 | plug, 236 | socket, 237 | plugConfig, 238 | socketConfig, 239 | coupledResult.Plug, 240 | coupledResult.Socket, 241 | ); err != nil { 242 | return u.Error(err, plug) 243 | } 244 | coupledResultStatus := integrationv1beta1.CoupledResultStatus{ 245 | ObservedGeneration: plug.Generation, 246 | } 247 | coupledResultStatus.Plug = coupledResult.Plug 248 | coupledResultStatus.Socket = coupledResult.Socket 249 | plug.Status.CoupledResult = &coupledResultStatus 250 | return u.UpdateCoupledStatus(CouplingSucceeded, plug, socket, false) 251 | } 252 | 253 | func (u *PlugUtil) setCoupledStatusCondition( 254 | conditionCoupledReason ConditionCoupledReason, 255 | message string, 256 | plug *integrationv1beta1.Plug, 257 | ) { 258 | coupledStatus := false 259 | if message == "" { 260 | if conditionCoupledReason == PlugCreated { 261 | message = "plug created" 262 | } else if conditionCoupledReason == SocketNotCreated { 263 | message = "waiting for socket to be created" 264 | } else if conditionCoupledReason == CouplingInProcess { 265 | message = "coupling to socket" 266 | } else if conditionCoupledReason == CouplingSucceeded { 267 | message = "coupling succeeded" 268 | } else if conditionCoupledReason == UpdatingInProcess { 269 | message = "updating coupling" 270 | } else if conditionCoupledReason == Error { 271 | message = "unknown error" 272 | } 273 | } 274 | if conditionCoupledReason != Error { 275 | plug.Status.Conditions = []metav1.Condition{} 276 | } 277 | if conditionCoupledReason == CouplingSucceeded { 278 | coupledStatus = true 279 | } 280 | condition := metav1.Condition{ 281 | Message: message, 282 | ObservedGeneration: plug.Generation, 283 | Reason: string(conditionCoupledReason), 284 | Status: "False", 285 | Type: string(ConditionTypeCoupled), 286 | } 287 | if coupledStatus { 288 | condition.Status = "True" 289 | } 290 | meta.SetStatusCondition(&plug.Status.Conditions, condition) 291 | } 292 | 293 | func (u *PlugUtil) setErrorStatus(err error, plug *integrationv1beta1.Plug) error { 294 | e := err 295 | if e == nil { 296 | return nil 297 | } 298 | if plug == nil { 299 | return nil 300 | } 301 | if strings.Contains(e.Error(), registry.OptimisticLockErrorMsg) { 302 | return nil 303 | } 304 | message := e.Error() 305 | coupledCondition, err := u.GetCoupledCondition(plug) 306 | if err != nil { 307 | return err 308 | } 309 | if coupledCondition != nil { 310 | u.setCoupledStatusCondition(Error, "coupling failed", plug) 311 | } 312 | failedCondition := metav1.Condition{ 313 | Message: message, 314 | ObservedGeneration: plug.Generation, 315 | Reason: "Error", 316 | Status: "True", 317 | Type: string(ConditionTypeFailed), 318 | } 319 | meta.SetStatusCondition(&plug.Status.Conditions, failedCondition) 320 | return nil 321 | } 322 | 323 | func (u *PlugUtil) setCoupledSocketStatus( 324 | plug *integrationv1beta1.Plug, 325 | socket *integrationv1beta1.Socket, 326 | ) { 327 | plug.Status.CoupledSocket = &integrationv1beta1.CoupledSocket{ 328 | APIVersion: socket.APIVersion, 329 | Kind: socket.Kind, 330 | Name: socket.Name, 331 | Namespace: socket.Namespace, 332 | UID: socket.UID, 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /util/config.go: -------------------------------------------------------------------------------- 1 | /** 2 | * File: /util/config.go 3 | * Project: integration-operator 4 | * File Created: 17-10-2023 13:49:54 5 | * Author: Clay Risser 6 | * ----- 7 | * BitSpur (c) Copyright 2021 - 2023 8 | * 9 | * Licensed under the GNU Affero General Public License (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * https://www.gnu.org/licenses/agpl-3.0.en.html 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | * You can be released from the requirements of the license by purchasing 22 | * a commercial license. Buying such a license is mandatory as soon as you 23 | * develop commercial activities involving this software without disclosing 24 | * the source code of your own applications. 25 | */ 26 | 27 | package util 28 | 29 | import ( 30 | "context" 31 | "encoding/json" 32 | "errors" 33 | 34 | integrationv1beta1 "gitlab.com/bitspur/rock8s/integration-operator/api/v1beta1" 35 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 36 | "k8s.io/client-go/kubernetes" 37 | ctrl "sigs.k8s.io/controller-runtime" 38 | ) 39 | 40 | type ConfigUtil struct { 41 | apparatusUtil *ApparatusUtil 42 | client *kubernetes.Clientset 43 | ctx context.Context 44 | dataUtil *DataUtil 45 | varUtil *VarUtil 46 | } 47 | 48 | func NewConfigUtil( 49 | ctx context.Context, 50 | ) *ConfigUtil { 51 | return &ConfigUtil{ 52 | apparatusUtil: NewApparatusUtil(ctx), 53 | client: kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie()), 54 | ctx: ctx, 55 | dataUtil: NewDataUtil(ctx), 56 | varUtil: NewVarUtil(ctx), 57 | } 58 | } 59 | 60 | func (u *ConfigUtil) GetPlugConfig( 61 | plug *integrationv1beta1.Plug, 62 | socket *integrationv1beta1.Socket, 63 | ) (map[string]string, error) { 64 | plugConfig := make(map[string]string) 65 | if plug.Spec.ConfigSecretName != "" { 66 | secret, err := u.client.CoreV1().Secrets(plug.Namespace).Get( 67 | u.ctx, 68 | plug.Spec.ConfigSecretName, 69 | metav1.GetOptions{}, 70 | ) 71 | if err != nil { 72 | return nil, err 73 | } 74 | for key, value := range secret.Data { 75 | plugConfig[key] = string(value) 76 | } 77 | } 78 | if plug.Spec.Config != nil { 79 | for key, value := range plug.Spec.Config { 80 | plugConfig[key] = value 81 | } 82 | } 83 | if plug.Spec.ConfigTemplate != nil { 84 | for key, value := range plug.Spec.ConfigTemplate { 85 | result, err := u.plugConfigTemplateLookup(plug, value, socket) 86 | if err != nil { 87 | return nil, err 88 | } 89 | plugConfig[key] = result 90 | } 91 | } 92 | if plug.Spec.ConfigConfigMapName != "" { 93 | configMap, err := u.client.CoreV1().ConfigMaps(plug.Namespace).Get( 94 | u.ctx, 95 | plug.Spec.ConfigConfigMapName, 96 | metav1.GetOptions{}, 97 | ) 98 | if err != nil { 99 | return nil, err 100 | } 101 | for key, value := range configMap.Data { 102 | plugConfig[key] = string(value) 103 | } 104 | } 105 | if plug.Spec.Apparatus != nil { 106 | body, err := u.apparatusUtil.GetPlugConfig(plug, socket) 107 | if err != nil { 108 | return nil, err 109 | } 110 | apparatusPlugConfig, err := JsonToHashMap(body) 111 | if err != nil { 112 | return nil, err 113 | } 114 | for key, value := range apparatusPlugConfig { 115 | plugConfig[key] = value 116 | } 117 | } 118 | if socket.Spec.Interface == nil { 119 | return plugConfig, nil 120 | } 121 | plugConfig, err := u.ValidatePlugConfig(plug, socket.Spec.Interface.Config, plugConfig) 122 | if err != nil { 123 | return nil, err 124 | } 125 | return plugConfig, nil 126 | } 127 | 128 | func (u *ConfigUtil) GetSocketConfig( 129 | plug *integrationv1beta1.Plug, 130 | socket *integrationv1beta1.Socket, 131 | ) (map[string]string, error) { 132 | socketConfig := make(map[string]string) 133 | if socket.Spec.ConfigSecretName != "" { 134 | secret, err := u.client.CoreV1().Secrets(socket.Namespace).Get( 135 | u.ctx, 136 | socket.Spec.ConfigSecretName, 137 | metav1.GetOptions{}, 138 | ) 139 | if err != nil { 140 | return nil, err 141 | } 142 | for key, value := range secret.Data { 143 | socketConfig[key] = string(value) 144 | } 145 | } 146 | if socket.Spec.Config != nil { 147 | for key, value := range socket.Spec.Config { 148 | socketConfig[key] = value 149 | } 150 | } 151 | if socket.Spec.ConfigTemplate != nil { 152 | for key, value := range socket.Spec.ConfigTemplate { 153 | result, err := u.socketConfigTemplateLookup(socket, value, plug) 154 | if err != nil { 155 | return nil, err 156 | } 157 | socketConfig[key] = result 158 | } 159 | } 160 | if socket.Spec.ConfigConfigMapName != "" { 161 | configMap, err := u.client.CoreV1().ConfigMaps(socket.Namespace).Get( 162 | u.ctx, 163 | socket.Spec.ConfigConfigMapName, 164 | metav1.GetOptions{}, 165 | ) 166 | if err != nil { 167 | return nil, err 168 | } 169 | for key, value := range configMap.Data { 170 | socketConfig[key] = value 171 | } 172 | } 173 | if socket.Spec.Apparatus != nil { 174 | body, err := u.apparatusUtil.GetSocketConfig(socket, plug) 175 | if err != nil { 176 | return nil, err 177 | } 178 | apparatusSocketConfig, err := JsonToHashMap(body) 179 | if err != nil { 180 | return nil, err 181 | } 182 | for key, value := range apparatusSocketConfig { 183 | socketConfig[key] = value 184 | } 185 | } 186 | socketConfig, err := u.ValidateSocketConfig(socket, socketConfig) 187 | if err != nil { 188 | return nil, err 189 | } 190 | return socketConfig, nil 191 | } 192 | 193 | func (u *ConfigUtil) ValidatePlugConfig( 194 | plug *integrationv1beta1.Plug, 195 | configInterface *integrationv1beta1.ConfigInterface, 196 | plugConfig map[string]string, 197 | ) (map[string]string, error) { 198 | if configInterface == nil { 199 | return plugConfig, nil 200 | } 201 | validatedPlugConfig := make(map[string]string) 202 | for propertyName, property := range configInterface.Plug { 203 | if value, found := plugConfig[propertyName]; found && value != "" { 204 | validatedPlugConfig[propertyName] = plugConfig[propertyName] 205 | } else { 206 | if property.Required { 207 | return plugConfig, errors.New("plug config property '" + propertyName + "' is required") 208 | } else if property.Default != "" { 209 | validatedPlugConfig[propertyName] = property.Default 210 | } 211 | } 212 | } 213 | return validatedPlugConfig, nil 214 | } 215 | 216 | func (u *ConfigUtil) ValidateSocketConfig( 217 | socket *integrationv1beta1.Socket, 218 | socketConfig map[string]string, 219 | ) (map[string]string, error) { 220 | if socket.Spec.Interface == nil { 221 | return socketConfig, nil 222 | } 223 | configInterface := socket.Spec.Interface.Config 224 | if configInterface == nil { 225 | return socketConfig, nil 226 | } 227 | validatedSocketConfig := make(map[string]string) 228 | for propertyName, property := range configInterface.Socket { 229 | if _, found := socketConfig[propertyName]; found { 230 | validatedSocketConfig[propertyName] = socketConfig[propertyName] 231 | } else { 232 | if property.Required { 233 | return socketConfig, errors.New("socket config property '" + propertyName + "' is required") 234 | } else if property.Default != "" { 235 | validatedSocketConfig[propertyName] = property.Default 236 | } 237 | } 238 | } 239 | return validatedSocketConfig, nil 240 | } 241 | 242 | func (u *ConfigUtil) plugConfigTemplateLookup(plug *integrationv1beta1.Plug, configTemplate string, socket *integrationv1beta1.Socket) (string, error) { 243 | data, err := u.buildPlugConfigTemplateData(*plug, socket) 244 | if err != nil { 245 | return "", err 246 | } 247 | return Template(&data, configTemplate) 248 | } 249 | 250 | func (u *ConfigUtil) socketConfigTemplateLookup( 251 | socket *integrationv1beta1.Socket, 252 | configTemplate string, 253 | plug *integrationv1beta1.Plug, 254 | ) (string, error) { 255 | data, err := u.buildSocketConfigTemplateData(*socket, plug) 256 | if err != nil { 257 | return "", err 258 | } 259 | return Template(&data, configTemplate) 260 | } 261 | 262 | func (u *ConfigUtil) buildPlugConfigTemplateData( 263 | plug integrationv1beta1.Plug, 264 | socket *integrationv1beta1.Socket, 265 | ) (map[string]interface{}, error) { 266 | kubectlUtil := NewKubectlUtil(u.ctx, plug.Namespace, EnsureServiceAccount(plug.Spec.ServiceAccountName)) 267 | dataMap := map[string]interface{}{} 268 | dataMap["plug"] = plug 269 | if socket != nil { 270 | dataMap["socket"] = socket 271 | } 272 | plugData, err := u.dataUtil.GetPlugData(&plug) 273 | if err != nil { 274 | return dataMap, err 275 | } 276 | dataMap["plugData"] = plugData 277 | socketData, err := u.dataUtil.GetSocketData(socket) 278 | if err != nil { 279 | return dataMap, err 280 | } 281 | dataMap["socketData"] = socketData 282 | if plug.Spec.Vars != nil { 283 | varsMap, err := u.varUtil.GetVars(plug.Namespace, plug.Spec.Vars, kubectlUtil, &plug, socket) 284 | if err != nil { 285 | return dataMap, err 286 | } 287 | dataMap["vars"] = varsMap 288 | } 289 | bData, err := json.Marshal(dataMap) 290 | if err != nil { 291 | return nil, err 292 | } 293 | var data map[string]interface{} 294 | if err := json.Unmarshal(bData, &data); err != nil { 295 | return nil, err 296 | } 297 | return data, nil 298 | } 299 | 300 | func (u *ConfigUtil) buildSocketConfigTemplateData( 301 | socket integrationv1beta1.Socket, 302 | plug *integrationv1beta1.Plug, 303 | ) (map[string]interface{}, error) { 304 | kubectlUtil := NewKubectlUtil(u.ctx, socket.Namespace, EnsureServiceAccount(socket.Spec.ServiceAccountName)) 305 | dataMap := map[string]interface{}{} 306 | dataMap["socket"] = socket 307 | if plug != nil { 308 | dataMap["plug"] = plug 309 | } 310 | socketData, err := u.dataUtil.GetSocketData(&socket) 311 | if err != nil { 312 | return dataMap, err 313 | } 314 | dataMap["socketData"] = socketData 315 | plugData, err := u.dataUtil.GetPlugData(plug) 316 | if err != nil { 317 | return dataMap, err 318 | } 319 | dataMap["plugData"] = plugData 320 | if socket.Spec.Vars != nil { 321 | varsMap, err := u.varUtil.GetVars(socket.Namespace, socket.Spec.Vars, kubectlUtil, plug, &socket) 322 | if err != nil { 323 | return dataMap, err 324 | } 325 | dataMap["vars"] = varsMap 326 | } 327 | bData, err := json.Marshal(dataMap) 328 | if err != nil { 329 | return nil, err 330 | } 331 | var data map[string]interface{} 332 | if err := json.Unmarshal(bData, &data); err != nil { 333 | return nil, err 334 | } 335 | return data, nil 336 | } 337 | -------------------------------------------------------------------------------- /chart/templates/crds/integration.rock8s.com_deferredresources.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.11.1 7 | creationTimestamp: null 8 | name: deferredresources.integration.rock8s.com 9 | spec: 10 | group: integration.rock8s.com 11 | names: 12 | kind: DeferredResource 13 | listKind: DeferredResourceList 14 | plural: deferredresources 15 | singular: deferredresource 16 | scope: Namespaced 17 | versions: 18 | - name: v1beta1 19 | schema: 20 | openAPIV3Schema: 21 | description: DeferredResource is the Schema for the deferredresources API 22 | properties: 23 | apiVersion: 24 | description: 25 | "APIVersion defines the versioned schema of this representation 26 | of an object. Servers should convert recognized schemas to the latest 27 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" 28 | type: string 29 | kind: 30 | description: 31 | "Kind is a string value representing the REST resource this 32 | object represents. Servers may infer this from the endpoint the client 33 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" 34 | type: string 35 | metadata: 36 | type: object 37 | spec: 38 | description: DeferredResourceSpec defines the desired state of DeferredResource 39 | properties: 40 | resource: 41 | description: 42 | Resource is the resource to create after the defer is 43 | resolved 44 | x-kubernetes-preserve-unknown-fields: true 45 | serviceAccountName: 46 | description: 47 | "ServiceAccountName is the name of the ServiceAccount 48 | to use to create deferred resources from. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/" 49 | type: string 50 | timeout: 51 | description: 52 | Timeout is the maximum time to wait before creating the 53 | resource 54 | format: int64 55 | type: integer 56 | waitFor: 57 | description: 58 | WaitFor is a list of resources to wait for before creating 59 | the resource 60 | items: 61 | description: 62 | Target refers to a kubernetes object by Group, Version, 63 | Kind and Name gvk.Gvk contains Group, Version and Kind APIVersion 64 | is added to keep the backward compatibility of using ObjectReference 65 | for Var.ObjRef 66 | properties: 67 | apiVersion: 68 | type: string 69 | group: 70 | type: string 71 | kind: 72 | type: string 73 | name: 74 | type: string 75 | version: 76 | type: string 77 | type: object 78 | type: array 79 | type: object 80 | status: 81 | description: DeferredResourceStatus defines the observed state of DeferredResource 82 | properties: 83 | conditions: 84 | items: 85 | description: 86 | "Condition contains details for one aspect of the current 87 | state of this API Resource. --- This struct is intended for direct 88 | use as an array at the field path .status.conditions. For example, 89 | \n type FooStatus struct{ // Represents the observations of a 90 | foo's current state. // Known .status.conditions.type are: \"Available\", 91 | \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge 92 | // +listType=map // +listMapKey=type Conditions []metav1.Condition 93 | `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" 94 | protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" 95 | properties: 96 | lastTransitionTime: 97 | description: 98 | lastTransitionTime is the last time the condition 99 | transitioned from one status to another. This should be when 100 | the underlying condition changed. If that is not known, then 101 | using the time when the API field changed is acceptable. 102 | format: date-time 103 | type: string 104 | message: 105 | description: 106 | message is a human readable message indicating 107 | details about the transition. This may be an empty string. 108 | maxLength: 32768 109 | type: string 110 | observedGeneration: 111 | description: 112 | observedGeneration represents the .metadata.generation 113 | that the condition was set based upon. For instance, if .metadata.generation 114 | is currently 12, but the .status.conditions[x].observedGeneration 115 | is 9, the condition is out of date with respect to the current 116 | state of the instance. 117 | format: int64 118 | minimum: 0 119 | type: integer 120 | reason: 121 | description: 122 | reason contains a programmatic identifier indicating 123 | the reason for the condition's last transition. Producers 124 | of specific condition types may define expected values and 125 | meanings for this field, and whether the values are considered 126 | a guaranteed API. The value should be a CamelCase string. 127 | This field may not be empty. 128 | maxLength: 1024 129 | minLength: 1 130 | pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ 131 | type: string 132 | status: 133 | description: status of the condition, one of True, False, Unknown. 134 | enum: 135 | - "True" 136 | - "False" 137 | - Unknown 138 | type: string 139 | type: 140 | description: 141 | type of condition in CamelCase or in foo.example.com/CamelCase. 142 | --- Many .condition.type values are consistent across resources 143 | like Available, but because arbitrary conditions can be useful 144 | (see .node.status.conditions), the ability to deconflict is 145 | important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) 146 | maxLength: 316 147 | 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])$ 148 | type: string 149 | required: 150 | - lastTransitionTime 151 | - message 152 | - reason 153 | - status 154 | - type 155 | type: object 156 | type: array 157 | ownerReferences: 158 | description: 159 | OwnerReference contains enough information to let you 160 | identify an owning object. An owning object must be in the same 161 | namespace as the dependent, or be cluster-scoped, so there is no 162 | namespace field. 163 | properties: 164 | apiVersion: 165 | description: API version of the referent. 166 | type: string 167 | blockOwnerDeletion: 168 | description: 169 | If true, AND if the owner has the "foregroundDeletion" 170 | finalizer, then the owner cannot be deleted from the key-value 171 | store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion 172 | for how the garbage collector interacts with this field and 173 | enforces the foreground deletion. Defaults to false. To set 174 | this field, a user needs "delete" permission of the owner, otherwise 175 | 422 (Unprocessable Entity) will be returned. 176 | type: boolean 177 | controller: 178 | description: If true, this reference points to the managing controller. 179 | type: boolean 180 | kind: 181 | description: "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" 182 | type: string 183 | name: 184 | description: "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names" 185 | type: string 186 | uid: 187 | description: "UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids" 188 | type: string 189 | required: 190 | - apiVersion 191 | - kind 192 | - name 193 | - uid 194 | type: object 195 | x-kubernetes-map-type: atomic 196 | type: object 197 | type: object 198 | served: true 199 | storage: true 200 | subresources: 201 | status: {} 202 | --------------------------------------------------------------------------------