From 97fdce4f03c4bb2009711a806561f14fb6fca0dc Mon Sep 17 00:00:00 2001 From: Chris Werner Rau Date: Mon, 5 Aug 2024 16:36:17 +0200 Subject: [PATCH 1/3] feat(anynines-klutch): initial commit --- charts/anynines-klutch/.helmignore | 25 + charts/anynines-klutch/Chart.lock | 6 + charts/anynines-klutch/Chart.yaml | 20 + charts/anynines-klutch/README.md.gotmpl | 313 +++++ .../ci/backupmanager-values.yaml | 4 + charts/anynines-klutch/ci/basic-values.yaml | 20 + .../ci/dataservices-values.yaml | 6 + ...klutch.anynines.com_apiservicebinding.yaml | 429 +++++++ .../klutch.anynines.com_apiserviceexport.yaml | 686 +++++++++++ ....anynines.com_apiserviceexportrequest.yaml | 450 +++++++ ...anynines.com_apiserviceexporttemplate.yaml | 358 ++++++ ...utch.anynines.com_apiservicenamespace.yaml | 60 + .../klutch.anynines.com_clusterbinding.yaml | 163 +++ charts/anynines-klutch/dashboards/loki.json | 1044 +++++++++++++++++ .../anynines-klutch/helmrelease.schema.json | 20 + charts/anynines-klutch/templates/NOTES.txt | 0 .../apiServiceExportTemplate.yaml | 59 + .../anynines-kube-bind/clusterRole.yaml | 76 ++ .../clusterRoleBinding.yaml | 14 + .../anynines-kube-bind/deployment.yaml | 68 ++ .../templates/anynines-kube-bind/ingress.yaml | 24 + .../anynines-kube-bind/secret-cookies.yaml | 17 + .../templates/anynines-kube-bind/service.yaml | 13 + .../anynines-kube-bind/serviceAccount.yaml | 6 + .../templates/crossplane/providerConfig.yaml | 76 ++ .../anynines-klutch/templates/oidc/dex.yaml | 72 ++ .../templates/oidc/keycloak-secret.yaml | 10 + .../templates/oidc/keycloak.yaml | 112 ++ .../templates/oidc/persistentVolumeClaim.yaml | 12 + .../templates/oidc/secret.yaml | 11 + charts/anynines-klutch/values.schema.json | 174 +++ charts/anynines-klutch/values.yaml | 16 + 32 files changed, 4364 insertions(+) create mode 100644 charts/anynines-klutch/.helmignore create mode 100644 charts/anynines-klutch/Chart.lock create mode 100644 charts/anynines-klutch/Chart.yaml create mode 100644 charts/anynines-klutch/README.md.gotmpl create mode 100644 charts/anynines-klutch/ci/backupmanager-values.yaml create mode 100644 charts/anynines-klutch/ci/basic-values.yaml create mode 100644 charts/anynines-klutch/ci/dataservices-values.yaml create mode 100644 charts/anynines-klutch/crds/klutch.anynines.com_apiservicebinding.yaml create mode 100644 charts/anynines-klutch/crds/klutch.anynines.com_apiserviceexport.yaml create mode 100644 charts/anynines-klutch/crds/klutch.anynines.com_apiserviceexportrequest.yaml create mode 100644 charts/anynines-klutch/crds/klutch.anynines.com_apiserviceexporttemplate.yaml create mode 100644 charts/anynines-klutch/crds/klutch.anynines.com_apiservicenamespace.yaml create mode 100644 charts/anynines-klutch/crds/klutch.anynines.com_clusterbinding.yaml create mode 100644 charts/anynines-klutch/dashboards/loki.json create mode 100644 charts/anynines-klutch/helmrelease.schema.json create mode 100644 charts/anynines-klutch/templates/NOTES.txt create mode 100644 charts/anynines-klutch/templates/anynines-kube-bind/apiServiceExportTemplate.yaml create mode 100644 charts/anynines-klutch/templates/anynines-kube-bind/clusterRole.yaml create mode 100644 charts/anynines-klutch/templates/anynines-kube-bind/clusterRoleBinding.yaml create mode 100644 charts/anynines-klutch/templates/anynines-kube-bind/deployment.yaml create mode 100644 charts/anynines-klutch/templates/anynines-kube-bind/ingress.yaml create mode 100644 charts/anynines-klutch/templates/anynines-kube-bind/secret-cookies.yaml create mode 100644 charts/anynines-klutch/templates/anynines-kube-bind/service.yaml create mode 100644 charts/anynines-klutch/templates/anynines-kube-bind/serviceAccount.yaml create mode 100644 charts/anynines-klutch/templates/crossplane/providerConfig.yaml create mode 100644 charts/anynines-klutch/templates/oidc/persistentVolumeClaim.yaml create mode 100644 charts/anynines-klutch/templates/oidc/secret.yaml create mode 100644 charts/anynines-klutch/values.schema.json create mode 100644 charts/anynines-klutch/values.yaml diff --git a/charts/anynines-klutch/.helmignore b/charts/anynines-klutch/.helmignore new file mode 100644 index 000000000..d46db97df --- /dev/null +++ b/charts/anynines-klutch/.helmignore @@ -0,0 +1,25 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ +README.md.gotmpl +.helmignore diff --git a/charts/anynines-klutch/Chart.lock b/charts/anynines-klutch/Chart.lock new file mode 100644 index 000000000..a7a2dadfa --- /dev/null +++ b/charts/anynines-klutch/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common + repository: oci://ghcr.io/teutonet/teutonet-helm-charts + version: 1.2.0 +digest: sha256:62ef92fb03b60b1bf481b96b8b856f3b3156c10cc50a50e3604c8b679ef71497 +generated: "2024-07-29T11:41:05.632726065+02:00" diff --git a/charts/anynines-klutch/Chart.yaml b/charts/anynines-klutch/Chart.yaml new file mode 100644 index 000000000..124332897 --- /dev/null +++ b/charts/anynines-klutch/Chart.yaml @@ -0,0 +1,20 @@ +apiVersion: v2 +name: anynines-klutch +type: application +version: 0.1.0 +icon: https://docs.k8s.anynines.com/img/favicon.ico +maintainers: + - name: cwrau + email: cwr@teuto.net + - name: marvinWolff + email: mw@teuto.net + - name: tasches + email: st@teuto.net +sources: + - https://docs.k8s.anynines.com/docs/platform-operator/central-management-cluster-setup +home: https://teuto.net +description: Installs the anynines klutch platform +dependencies: + - name: common + version: 1.2.0 + repository: oci://ghcr.io/teutonet/teutonet-helm-charts diff --git a/charts/anynines-klutch/README.md.gotmpl b/charts/anynines-klutch/README.md.gotmpl new file mode 100644 index 000000000..81097edb8 --- /dev/null +++ b/charts/anynines-klutch/README.md.gotmpl @@ -0,0 +1,313 @@ +[modeline]: # ( vim: set ft=markdown: ) +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.maintainersSection" . }} + +## Cluster bootstrap + +```sh +# always be git 😁 +git init + +# create empty cluster HelmRelease; +flux create helmrelease --export base-cluster -n flux-system --source HelmRepository/teuto-net.flux-system --chart base-cluster --chart-version 5.x.x > cluster.yaml + +# maybe use the following name for your cluster; +kubectl get node -o jsonpath='{.items[0].metadata.annotations.cluster\.x-k8s\.io/cluster-name}' + +# configure according to your needs, at least `.global.clusterName` is needed +# additionally, you should add your git repo to `.flux.gitRepositories`, see [the documentation](https://github.com/teutonet/teutonet-helm-charts/tree/main/charts/base-cluster#81--property-base-cluster-configuration--flux--gitrepositories) +# make sure to use the correct url format, see [the documentation](https://github.com/teutonet/teutonet-helm-charts/tree/main/charts/base-cluster#81112-property-base-cluster-configuration--flux--gitrepositories--additionalproperties--allof--item-0--oneof--item-1) +vi cluster.yaml + +# create HelmRelease for flux to manage itself +kubectl create namespace flux-system --dry-run=client -o yaml > flux.yaml +flux create source helm --url https://fluxcd-community.github.io/helm-charts flux -n flux-system --export >> flux.yaml +flux create helmrelease --export flux -n flux-system --source HelmRepository/flux.flux-system --chart flux2 --chart-version 2.x.x >> flux.yaml + +# add, commit and push resources +git add cluster.yaml flux.yaml +git commit cluster.yaml flux.yaml +git push + +# after this you should be on the KUBECONFIG for the cluster +# we explicitly do not use `flux bootstrap` or `flux install` as this creates kustomization stuff and installs flux manually +kubectl apply --server-side -f flux.yaml # ignore the errors about missing CRDs +helm install -n flux-system flux flux2 --repo https://fluxcd-community.github.io/helm-charts --version 2.x.x --atomic + +# manual initial installation of the chart, afterwards the chart takes over +# after the installation finished, follow the on-screen instructions to configure your flux, distribute KUBECONFIGs, ... +helm install -n flux-system base-cluster oci://ghcr.io/teutonet/teutonet-helm-charts/base-cluster --version 4.x.x --atomic --values <(cat cluster.yaml | yq -y .spec.values) + +# you can use this command to get the instructions again +# e.g. when adding users, gitRepositories, ... +helm -n flux-system get notes base-cluster +``` + +> ⚠️ Due to various reasons, it's not possible to cleanly uninstall this +via a normal `kubectl delete`, `helm uninstall` or via flux deletion. +[See the corresponding issue](https://github.com/teutonet/teutonet-helm-charts/issues/28) + +## Cluster components + +### Component [backup](#backup) + +[velero](https://velero.io) takes care of backing up your PVCs. + +### Component [cert-manager](#certManager) + +[cert-manager](https://cert-manager.io) takes care of creating SSL certificates +for your Ingresses (and [other needs](https://cert-manager.io/docs/usage)) + +1. set `.certManager.email` to your email for the Let's Encrypt account to enable + certificates + +To create wildcard certificates, you need to enable a [DNS Provider](#component-dns) + +Then you can just create a [`Certificate`](https://cert-manager.io/docs/usage/certificate) +resource. + +### Component [descheduler](#descheduler) + +The [descheduler](https://github.com/kubernetes-sigs/descheduler) runs periodically +and tries to average the load across the nodes by deleting pods on fuller nodes +so the kube-scheduler can, hopefully, schedule them on nodes with more space. + +Additionally, the descheduler also tries to reconcile `topologySpreadConstraints` +and affinities. + +If the cluster is _semi_ underspecced or the individual applications have unperfect +resource requests, the descheduler might lead to period restarting of random pods. + +In that case you should disable the descheduler. + +### Component [dns](#dns) + +The [external-dns](https://github.com/kubernetes-sigs/external-dns) creates, updates, +deletes and syncs DNS records for your Ingresses. + +1. set `.dns.provider.` to your implementation: + - cloudflare: `.dns.provider.cloudflare.apiToken` + +If you need a different provider than cloudflare, please open a ticket for one of +the [supported ones](https://github.com/kubernetes-sigs/external-dns#status-of-providers) +which is also supported by [cert-manager](https://cert-manager.io/docs/configuration/acme/dns01/#supported-dns01-providers) + +### Component [ingress](#ingress) + +The included [`nginx` ingress-controller](https://docs.nginx.com/nginx-ingress-controller) +only works for the `IngressClassName: nginx`. + +#### TLS + +1. add `kubernetes.io/tls-acme: "true"` to your Ingress's annotations + - additionally, although not advised unless you know what you're doing, + you can explicitly choose the Issuer by using these annotations: + - `cert-manager.io/cluster-issuer: letsencrypt-staging` + - `cert-manager.io/cluster-issuer: letsencrypt-production` + +#### IP Address + +If you want to make sure that, in the event of a catastrophic failure, you keep the +same IP address, you should roll this out, get the assigned IP +(`kubectl -n ingress-nginx get svc ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress}'`) +and set `.ingress.IP=` in the values. This makes sure the IP is kept in your +project (may incur cost!), which means you can reuse it later or after recovery. + +### Component [flux](#flux) + +[Flux](https://fluxcd.io) is used to deploy resources to your cluster and to +keep them in sync. + +Flux can also auto-update images and HelmReleases. + +You can create any number of gitRepository connections, with SSH(recommended) +or https checkout, with or without SOPS, ... . + +### Component [kyverno](#kyverno) + +You can optionally enable [kyverno](https://kyverno.io), which is a policy +system, allowing you to specify in-depth policies to prevent or force certain +things in your cluster. + +### Component [monitoring](#monitoring) + +#### Sub-Component [prometheus](#monitoring_prometheus) + +[Prometheus](https://prometheus.io) takes care of scraping metrics and alerting. + +#### Sub-Component [grafana](#monitoring_grafana) + +[Grafana](https://grafana.com) is used to create dashboards to visualize your +metrics and the health of your cluster and applications. + +#### Sub-Component [loki](#monitoring_loki) + +[Loki](https://grafana.com/oss/loki) collects logs from across the cluster and +allows to have a centralized, non-CLI, view of the logs and to create alerting +based on them. + +#### Sub-Component [metrics-server](#monitoring_metricsServer) + +[Metrics Server](https://github.com/kubernetes-sigs/metrics-server) implements +the [kubernetes Metrics API](https://github.com/kubernetes/metrics) to allow +for [Horizontal Autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale), +[Vertical Autoscaling](https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler) +and to allow `kubectl top` and tools like [k9s](https://k9scli.io) to show +resource usage for your pods and nodes. + +#### Sub-Component [securityScanning](#monitoring_securityScanning) + +The included [trivy](https://aquasecurity.github.io/trivy-operator) scans the +running workload in your cluster for CVEs and creates +[Custom Resources](https://aquasecurity.github.io/trivy-operator/v0.12.1/docs/crds) +to present the results. + +#### Sub-Component [tracing](#monitoring_tracing) + +The included [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) +collects traces via otlp-grpc on every node via the `open-telemetry-collector-opentelemetry-collector.monitoring` service. +These traces are then sent to [Grafana Tempo](https://grafana.com/oss/tempo/), +which is included as a datasource in Grafana by default. + +##### Usage Example + +In your deployment/statefulset/daemonset/... add the following config; + +```yaml +spec: + template: + spec: + containers: + - env: + - name: OTEL_HOST <- change this to your framework's environment variable + value: open-telemetry-collector-opentelemetry-collector.monitoring + - name: OTEL_PORT + value: "4317" +``` + +The supported protocols are; + +- jaeger + - grpc: 14250 + - thrift_http: 14268 + - thrift_compact: 6831 +- otlp + - grpc: 4317 + - http: 4318 +- zipkin: 9411 + +### Component [storage](#storage) + +The included [NFS Ganesha server and external provisioner](https://github.com/kubernetes-sigs/nfs-ganesha-server-and-external-provisioner) +provides rudimentary support for RWM volumes if needed. + +> ⚠️ This is _not_ highly available, and the software itself _does not_ support +it. You should only use this if there is no other choice and make sure you're +cloud provider knows about this, because a node rotation _will_ result in a +downtime for all attached applications! + +### Component [rbac](#rbac) + +This chart gives you the ability to create serviceAccounts, roles, roleBindings, +[namespaces](#miscellaneous) and KUBECONFIG files with a, hopefully easy to +understand, DSL. + +After configuring your stuff you can fetch the KUBECONFIGs with the help of the +output of `helm -n flux-system get notes base-cluster` + +### Miscellaneous + +- You can create [`HelmRepositoy`s](#global); `.global.helmRepositories..url=` +- You can create [cluster-wide certificates](#global_certificates); `.global.certificates..dnsNames=[]` +- You can create [namespaces](#global_namespaces); `.global.namespaces.={}` +- You can create [cluster-wide imageCredentials](#global); `.global.imageCredentials..{host,username,password}` + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +This helm chart requires [flux v2 to be installed](https://fluxcd.io/docs/installation), +see [bootstrap](#cluster-bootstrap) + +The various components are automatically updated to the latest minor and patch version. + +This excludes: + +- descheduler, its version is bound to the k8s version and they have not released + 1.0.0 + +## Migration + +### 0.x.x -> 1.0.0 + +- The field `.dns.email` moves to `.certManager.email`. +- The field `.dns.provider.cloudflare.email` is removed, as only `apiToken`s are + supported anyways. + +### 1.x.x -> 2.0.0 + +⚠️ Skip this migration! + +- Flux is now a direct dependency + - You should add the following labels to all resources of flux; + - .metadata.labels["app.kubernetes.io/managed-by"]="Helm" + - .metadata.annotations["meta.helm.sh/release-name"]="base-cluster" + - .metadata.annotations["meta.helm.sh/release-namespace"]="flux-system" + - If you have problems when applying / `helm upgrade`ing the new CRDs, like + `cannot patch "alerts.notification.toolkit.fluxcd.io" with kind + CustomResourceDefinition: CustomResourceDefinition.apiextensions.k8s.io + "alerts.notification.toolkit.fluxcd.io" is invalid: status.storedVersions[1]: + Invalid value: "v1beta2": must appear in spec.versions`, you can replace + those CRDs. (kubectl replace --force -f -) + - ⚠️ make sure to only replace CRDs you're not actively using!!, this is + a destructive operation. If all your resources are in flux you can also + try to turn off flux before the replacement and flux _should_ resync and + reconcile all resources. + - remove your manually managed flux resources + +### 2.x.x -> 3.0.0 + +- Flux is removed as a direct dependency + + The flux chart is way too unstable, cannot be used for an installation, ... + +We be sorry 😥 + +You're gonna have to install flux yourself again + +### 3.x.x -> 4.0.0 + +The storageClasses are going to be removed from this chart, this is prepared by +leaving them in the cluster on upgrade. + +The new [t8s-cluster](../t8s-cluster) is going to provide these, the enduser can +ignore this change. + +### 4.x.x -> 5.0.0 + +The condition if velero gets deployed changed. Velero will not be deployed if you +have not configured its backupstoragelocation. This change is necessary, because +in the current version of velero this value is mandatory. Please move +your existing backupstoragelocation configuration to the base-cluster chart if you +haven't already. + +### 5.x.x -> 6.0.0 + +The kyverno 2.x.x -> 3.x.x upgrade cannot be done without manual intervention, see +https://artifacthub.io/packages/helm/kyverno/kyverno#option-1---uninstallation-and-reinstallation + +So you have to backup your resources and delete the kyverno HelmReleases before the +upgrade, they will be recreated in version 6. + +This also makes kyverno HA, so be aware that kyverno will need more resources in +you cluster. + +{{ .Files.Get "values.md" }} diff --git a/charts/anynines-klutch/ci/backupmanager-values.yaml b/charts/anynines-klutch/ci/backupmanager-values.yaml new file mode 100644 index 000000000..7e5bc6701 --- /dev/null +++ b/charts/anynines-klutch/ci/backupmanager-values.yaml @@ -0,0 +1,4 @@ +backupManager: + url: https://sb.test.com:3000 + username: admin + password: admin diff --git a/charts/anynines-klutch/ci/basic-values.yaml b/charts/anynines-klutch/ci/basic-values.yaml new file mode 100644 index 000000000..51cf558ea --- /dev/null +++ b/charts/anynines-klutch/ci/basic-values.yaml @@ -0,0 +1,20 @@ +dataservices: + postgresql: + url: https://pg.sb.a9s.cwrau.wtf + username: admin + password: fvGeQ8AIclWj1tN8ViiHghtLROQX9c + +backupManager: + url: https://backups.a9s.cwrau.wtf + username: admin + password: FkVFcrX8Fow4ELuwkVhjeURKeocX8I + +oidc: + ingress: + host: dex.a9s.cwrau.wtf + +ingress: + host: klutch.a9s.cwrau.wtf + +kubernetes: + externalAddress: https://212.15.214.137:6443 diff --git a/charts/anynines-klutch/ci/dataservices-values.yaml b/charts/anynines-klutch/ci/dataservices-values.yaml new file mode 100644 index 000000000..c6cff1d54 --- /dev/null +++ b/charts/anynines-klutch/ci/dataservices-values.yaml @@ -0,0 +1,6 @@ +dataservices: + postgresql: &svc + url: https://sb.test.com:3000 + username: admin + password: admin + redis: *svc diff --git a/charts/anynines-klutch/crds/klutch.anynines.com_apiservicebinding.yaml b/charts/anynines-klutch/crds/klutch.anynines.com_apiservicebinding.yaml new file mode 100644 index 000000000..5d1ddb3de --- /dev/null +++ b/charts/anynines-klutch/crds/klutch.anynines.com_apiservicebinding.yaml @@ -0,0 +1,429 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + name: apiservicebindings.klutch.anynines.com +spec: + group: klutch.anynines.com + names: + categories: + - kube-bindings + kind: APIServiceBinding + listKind: APIServiceBindingList + plural: apiservicebindings + shortNames: + - sb + singular: apiservicebinding + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.providerPrettyName + name: Provider + type: string + - jsonPath: .metadata.annotations.bind\.klutch.\anynines\.com/resources + name: Resources + priority: 1 + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Message + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: APIServiceBinding binds an API service represented by a APIServiceExport + in a service provider cluster into a consumer cluster. This object lives + in the consumer cluster. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: spec specifies how an API service from a service provider + should be bound in the local consumer cluster. + properties: + kubeconfigSecretRef: + description: kubeconfigSecretName is the secret ref that contains + the kubeconfig of the service cluster. + properties: + key: + description: The key of the secret to select from. Must be "kubeconfig". + enum: + - kubeconfig + type: string + name: + description: Name of the referent. + minLength: 1 + type: string + namespace: + description: Namespace of the referent. + minLength: 1 + type: string + required: + - key + - name + - namespace + type: object + x-kubernetes-validations: + - message: kubeconfigSecretRef is immutable + rule: self == oldSelf + permissionClaims: + description: permissionClaims records decisions about permission claims + requested by the API service provider. Individual claims can be + accepted or rejected. If accepted, the API service provider gets + the requested access to the specified resources in this workspace. + Access is granted per GroupResource and other properties like selectors. + items: + description: acceptablePermissionClaim is a permission claim that + stores the users acceptance in the field state. Only accepted + permission claims are reconciled. + properties: + autoAdopt: + description: autoAdopt set to true means that objects created + by the consumer are adopted by the provider. i.e. the provider + will become the owner. Mutually exclusive with autoDonate. + type: boolean + autoDonate: + description: autoDonate set to true means that a newly created + object by the provider is immediately owned by the consumer. + If false, the object stays in ownership of the provider. Mutually + exclusive with autoDonate. + type: boolean + create: + description: create determines whether the kube-bind konnector + will sync matching objects from the provider cluster down + to the consumer cluster. only for owner Provider + properties: + replaceExisting: + description: "replaceExisting means that an existing object + owned by the consumer will be replaced by the provider + object. \n If set to false, and a conflicting consumer + object exists, it is not touched." + type: boolean + type: object + group: + default: "" + description: group is the name of an API group. For core groups + this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + onConflict: + description: onConflict determines how the conflicts between + objects on the consumer cluster will be resolved. + properties: + recreateWhenConsumerSideDeleted: + default: true + description: "recreateWhenConsumerSideDeleted set to true + (the default) means the provider will recreate the object + in case the object is missing on the consumer cluster, + but has been synchronized before. \n If set to false, + deleted provider-owned objects get deleted on the provider + cluster as well. \n Even if the consumer mistakenly or + intentionally deletes the object, the provider will recreate + it. If the field is set as false, the provider will not + recreate the object in case the object is deleted on the + consumer cluster." + type: boolean + type: object + read: + description: read claims read access to matching objects for + the provider. reading of the claimed object(s) is always claimed. + By default no labels and annotations are read. Reading of + labels and annotations can be claimed optionally by adding + labels and annotations items. If labels on consumer owned + objects that are set by the consumer are read, labelsOnProviderOwnedObjects + and annotationsOnProviderOwnedObjects can be set. + properties: + annotations: + description: annotations is a list of claimed annotation + key wildcard patterns that are synchronized from the consumer + cluster to the provider on objects that are owned by the + consumer. + items: + properties: + pattern: + type: string + type: object + type: array + labels: + description: labels is a list of claimed label key wildcard + patterns that are synchronized from the consumer cluster + to the provider on objects that are owned by the consumer. + items: + properties: + pattern: + type: string + type: object + type: array + labelsOnProviderOwnedObjects: + description: labelsOnProviderOwnedObjects is a list of claimed + label key wildcard patterns that are synchronized from + the consumer cluster to the provider on objects owned + by the provider. + items: + properties: + pattern: + type: string + type: object + type: array + overrideAnnotations: + description: overrideAnnotations is a list of claimed annotation + key wildcard patterns that are synchronized from the consumer + cluster to the provider on objects owned by the provider. + items: + properties: + pattern: + type: string + type: object + type: array + type: object + required: + description: required indicates whether the APIServiceBinding + will work if this claim is not accepted. If a required claim + is denied, the binding is aborted. + type: boolean + resource: + description: 'resource is the name of the resource. Note: it + is worth noting that you can not ask for permissions for resource + provided by a CRD not provided by an service binding export.' + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + selector: + description: selector selects which resources are being claimed. + If unset, all resources across all namespaces are being claimed. + properties: + fieldSelectors: + description: fieldSelectors is a list of field selectors + matching selected resources, see https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/. + items: + type: string + type: array + labelSelectors: + description: labelSelectors is a list of label selectors + matching selected resources. label selectors follow the + same rules as kubernetes label selectors, see https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/. + items: + additionalProperties: + type: string + type: object + type: array + names: + default: + - '*' + description: "names is a list of specific resource names + to select. Names matches the metadata.name field of the + underlying object. An entry of \"*\" anywhere in the list + means all object names of the group/resource within the + \"namespaces\" field are claimed. Wildcard entries other + than \"*\" and regular expressions are currently unsupported. + If a resources name matches any value in names, the resource + name is considered matching. \n // +kubebuilder:validation:XValidation:rule=\"self.all(n, + n.matches('^[A-z-]+|[*]$'))\",message=\"only names or + * are allowed\"" + items: + type: string + type: array + namespaces: + default: + - '*' + description: "namespaces represents namespaces where an + object of the given group/resource may be managed. Namespaces + matches against the metadata.namespace field. A value + of \"*\" matches namespaced objects across all namespaces. + If a resources namespace matches any value in namespaces, + the resource namespace is considered matching. If the + claim is for a cluster-scoped resource, namespaces has + to explicitly be set to an empty array to prevent defaulting + to \"*\". If the \"names\" field is unset, all objects + of the group/resource within the listed namespaces (or + cluster) will be claimed. \n // +kubebuilder:validation:XValidation:rule=\"self.all(n, + n.matches('^[A-z-]+|[*]$'))\",message=\"only names or + * are allowed\"" + items: + type: string + type: array + owner: + description: owner matches the resource's owner. If an owner + selector is set, resources owned by other owners will + not be claimed. Resources without a present owner will + be considered, if configured owner could be the owner + of the object. For example, if the consumer creates a + resource that is claimed by the provider for reading. + In this case the resource will be marked as owned by the + consumer, and handled as such in further reconciliations. + An unset owner selector means objects from both sides + are considered. + enum: + - Provider + - Consumer + type: string + type: object + state: + description: state indicates if the claim is accepted or rejected. + enum: + - Accepted + - Rejected + type: string + update: + description: update lists which updates to objects on the consumer + cluster are claimed. By default, the whole object is synced, + but metadata is not. + properties: + alwaysRecreate: + description: "alwaysRecreate, when true will delete the + old object and create new ones instead of updating. Useful + for immutable objects. \n This does not apply to metadata + field updates." + type: boolean + annotations: + description: "annotations is a list of claimed annotation + keys or annotation wildcard patterns that are synchronized + from the provider to the consumer for objects owned by + the provider. \n By default, no annotations are synced." + items: + properties: + pattern: + type: string + type: object + type: array + annotationsOnConsumerOwnedObjects: + description: "annotationsOnConsumerOwnedObjects is a list + of claimed annotation key wildcard patterns that are synchronized + from the provider to the consumer for objects owned by + the consumer. \n By default, no annotations are synced." + items: + properties: + pattern: + type: string + type: object + type: array + fields: + description: "fields are a list of JSON Paths describing + which parts of an object the provider wants to control. + \n This field is ignored if the owner in the claim selector + is set to \"Provider\"." + items: + type: string + type: array + labels: + description: "labels is a list of claimed label keys or + label wildcard patterns that are synchronized from the + provider to the consumer for objects owned by the provider. + \n By default, no labels are synced." + items: + properties: + pattern: + type: string + type: object + type: array + labelsOnConsumerOwnedObjects: + description: "labelsOnConsumerOwnedObjects is a list of + claimed label key wildcard patterns that are synchronized + from the provider to the consumer for objects owned by + the consumer. \n By default, no labels are synced." + items: + properties: + pattern: + type: string + type: object + type: array + preserving: + description: "preserving is a list of JSON Paths describing + which parts of an object owned by the provider the consumer + keeps controlling. \n This field is ignored if the owner + in the claim selector is set to \"Consumer\"." + items: + type: string + type: array + type: object + version: + description: version is the version of the claimed resource. + minLength: 1 + type: string + required: + - resource + - state + - version + type: object + x-kubernetes-validations: + - message: donate and adopt are mutually exclusive + rule: '!(has(self.autoDonate) && self.autoDonate && has(self.autoAdopt) + && self.autoAdopt)' + type: array + required: + - kubeconfigSecretRef + type: object + status: + description: status contains reconciliation information for a service + binding. + properties: + conditions: + description: conditions is a list of conditions that apply to the + APIServiceBinding. + items: + description: Condition defines an observation of a object operational + state. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. This should be when the underlying condition changed. + If that is not known, then using the time when the API field + changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. This field may be empty. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. The specific API may choose whether or not this + field is considered a guaranteed API. This field may not be + empty. + type: string + severity: + description: Severity provides an explicit classification of + Reason code, so the users or machines can immediately understand + the current situation and act accordingly. The Severity field + MUST be set only when Status=False. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase or in foo.example.com/CamelCase. + Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + providerPrettyName: + description: providerPrettyName is the pretty name of the service + provider cluster. This can be shared among different APIServiceBindings. + type: string + type: object + type: object + served: true + storage: true + subresources: diff --git a/charts/anynines-klutch/crds/klutch.anynines.com_apiserviceexport.yaml b/charts/anynines-klutch/crds/klutch.anynines.com_apiserviceexport.yaml new file mode 100644 index 000000000..7d223625a --- /dev/null +++ b/charts/anynines-klutch/crds/klutch.anynines.com_apiserviceexport.yaml @@ -0,0 +1,686 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + name: apiserviceexports.klutch.anynines.com +spec: + group: klutch.anynines.com + names: + categories: + - kube-bindings + kind: APIServiceExport + listKind: APIServiceExportList + plural: apiserviceexports + singular: apiserviceexport + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Established")].status + name: Established + priority: 5 + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: 'APIServiceExport specifies the resource to be exported. It is + mostly a CRD: - the spec is a CRD spec, but without webhooks - the status + reflects that on the consumer cluster' + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: spec specifies the resource. + properties: + clusterScopedIsolation: + description: ClusterScopedIsolation specifies how cluster scoped service + objects are isolated between multiple consumers on the provider + side. It can be "Prefixed", "Namespaced", or "None". + enum: + - Prefixed + - Namespaced + - None + type: string + group: + description: "group is the API group of the defined custom resource. + Empty string means the core API group. \tThe resources are served + under `/apis//...` or `/api` for the core group." + type: string + informerScope: + description: "informerScope is the scope of the APIServiceExport. + It can be either Cluster or Namespace. \n Cluster: The konnector + has permission to watch all namespaces at once and cluster-scoped + resources. This is more efficient than watching each namespace individually. + Namespaced: The konnector has permission to watch only single namespaces. + This is more resource intensive. And it means cluster-scoped resources + cannot be exported." + enum: + - Cluster + - Namespaced + type: string + x-kubernetes-validations: + - message: informerScope is immutable + rule: self == oldSelf + names: + description: names specify the resource and kind names for the custom + resource. + properties: + categories: + description: categories is a list of grouped resources this custom + resource belongs to (e.g. 'all'). This is published in API discovery + documents, and used by clients to support invocations like `kubectl + get all`. + items: + type: string + type: array + kind: + description: kind is the serialized kind of the resource. It is + normally CamelCase and singular. Custom resource instances will + use this value as the `kind` attribute in API calls. + type: string + listKind: + description: listKind is the serialized kind of the list for this + resource. Defaults to "`kind`List". + type: string + plural: + description: plural is the plural name of the resource to serve. + The custom resources are served under `/apis///.../`. + Must match the name of the CustomResourceDefinition (in the + form `.`). Must be all lowercase. + type: string + shortNames: + description: shortNames are short names for the resource, exposed + in API discovery documents, and used by clients to support invocations + like `kubectl get `. It must be all lowercase. + items: + type: string + type: array + singular: + description: singular is the singular name of the resource. It + must be all lowercase. Defaults to lowercased `kind`. + type: string + required: + - kind + - plural + type: object + permissionClaims: + items: + description: permissionClaim selects objects of a GVR that a service + provider may request and that a consumer may accept and allow + the service provider access to. + properties: + autoAdopt: + description: autoAdopt set to true means that objects created + by the consumer are adopted by the provider. i.e. the provider + will become the owner. Mutually exclusive with autoDonate. + type: boolean + autoDonate: + description: autoDonate set to true means that a newly created + object by the provider is immediately owned by the consumer. + If false, the object stays in ownership of the provider. Mutually + exclusive with autoDonate. + type: boolean + create: + description: create determines whether the kube-bind konnector + will sync matching objects from the provider cluster down + to the consumer cluster. only for owner Provider + properties: + replaceExisting: + description: "replaceExisting means that an existing object + owned by the consumer will be replaced by the provider + object. \n If set to false, and a conflicting consumer + object exists, it is not touched." + type: boolean + type: object + group: + default: "" + description: group is the name of an API group. For core groups + this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + onConflict: + description: onConflict determines how the conflicts between + objects on the consumer cluster will be resolved. + properties: + recreateWhenConsumerSideDeleted: + default: true + description: "recreateWhenConsumerSideDeleted set to true + (the default) means the provider will recreate the object + in case the object is missing on the consumer cluster, + but has been synchronized before. \n If set to false, + deleted provider-owned objects get deleted on the provider + cluster as well. \n Even if the consumer mistakenly or + intentionally deletes the object, the provider will recreate + it. If the field is set as false, the provider will not + recreate the object in case the object is deleted on the + consumer cluster." + type: boolean + type: object + read: + description: read claims read access to matching objects for + the provider. reading of the claimed object(s) is always claimed. + By default no labels and annotations are read. Reading of + labels and annotations can be claimed optionally by adding + labels and annotations items. If labels on consumer owned + objects that are set by the consumer are read, labelsOnProviderOwnedObjects + and annotationsOnProviderOwnedObjects can be set. + properties: + annotations: + description: annotations is a list of claimed annotation + key wildcard patterns that are synchronized from the consumer + cluster to the provider on objects that are owned by the + consumer. + items: + properties: + pattern: + type: string + type: object + type: array + labels: + description: labels is a list of claimed label key wildcard + patterns that are synchronized from the consumer cluster + to the provider on objects that are owned by the consumer. + items: + properties: + pattern: + type: string + type: object + type: array + labelsOnProviderOwnedObjects: + description: labelsOnProviderOwnedObjects is a list of claimed + label key wildcard patterns that are synchronized from + the consumer cluster to the provider on objects owned + by the provider. + items: + properties: + pattern: + type: string + type: object + type: array + overrideAnnotations: + description: overrideAnnotations is a list of claimed annotation + key wildcard patterns that are synchronized from the consumer + cluster to the provider on objects owned by the provider. + items: + properties: + pattern: + type: string + type: object + type: array + type: object + required: + description: required indicates whether the APIServiceBinding + will work if this claim is not accepted. If a required claim + is denied, the binding is aborted. + type: boolean + resource: + description: 'resource is the name of the resource. Note: it + is worth noting that you can not ask for permissions for resource + provided by a CRD not provided by an service binding export.' + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + selector: + description: selector selects which resources are being claimed. + If unset, all resources across all namespaces are being claimed. + properties: + fieldSelectors: + description: fieldSelectors is a list of field selectors + matching selected resources, see https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/. + items: + type: string + type: array + labelSelectors: + description: labelSelectors is a list of label selectors + matching selected resources. label selectors follow the + same rules as kubernetes label selectors, see https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/. + items: + additionalProperties: + type: string + type: object + type: array + names: + default: + - '*' + description: "names is a list of specific resource names + to select. Names matches the metadata.name field of the + underlying object. An entry of \"*\" anywhere in the list + means all object names of the group/resource within the + \"namespaces\" field are claimed. Wildcard entries other + than \"*\" and regular expressions are currently unsupported. + If a resources name matches any value in names, the resource + name is considered matching. \n // +kubebuilder:validation:XValidation:rule=\"self.all(n, + n.matches('^[A-z-]+|[*]$'))\",message=\"only names or + * are allowed\"" + items: + type: string + type: array + namespaces: + default: + - '*' + description: "namespaces represents namespaces where an + object of the given group/resource may be managed. Namespaces + matches against the metadata.namespace field. A value + of \"*\" matches namespaced objects across all namespaces. + If a resources namespace matches any value in namespaces, + the resource namespace is considered matching. If the + claim is for a cluster-scoped resource, namespaces has + to explicitly be set to an empty array to prevent defaulting + to \"*\". If the \"names\" field is unset, all objects + of the group/resource within the listed namespaces (or + cluster) will be claimed. \n // +kubebuilder:validation:XValidation:rule=\"self.all(n, + n.matches('^[A-z-]+|[*]$'))\",message=\"only names or + * are allowed\"" + items: + type: string + type: array + owner: + description: owner matches the resource's owner. If an owner + selector is set, resources owned by other owners will + not be claimed. Resources without a present owner will + be considered, if configured owner could be the owner + of the object. For example, if the consumer creates a + resource that is claimed by the provider for reading. + In this case the resource will be marked as owned by the + consumer, and handled as such in further reconciliations. + An unset owner selector means objects from both sides + are considered. + enum: + - Provider + - Consumer + type: string + type: object + update: + description: update lists which updates to objects on the consumer + cluster are claimed. By default, the whole object is synced, + but metadata is not. + properties: + alwaysRecreate: + description: "alwaysRecreate, when true will delete the + old object and create new ones instead of updating. Useful + for immutable objects. \n This does not apply to metadata + field updates." + type: boolean + annotations: + description: "annotations is a list of claimed annotation + keys or annotation wildcard patterns that are synchronized + from the provider to the consumer for objects owned by + the provider. \n By default, no annotations are synced." + items: + properties: + pattern: + type: string + type: object + type: array + annotationsOnConsumerOwnedObjects: + description: "annotationsOnConsumerOwnedObjects is a list + of claimed annotation key wildcard patterns that are synchronized + from the provider to the consumer for objects owned by + the consumer. \n By default, no annotations are synced." + items: + properties: + pattern: + type: string + type: object + type: array + fields: + description: "fields are a list of JSON Paths describing + which parts of an object the provider wants to control. + \n This field is ignored if the owner in the claim selector + is set to \"Provider\"." + items: + type: string + type: array + labels: + description: "labels is a list of claimed label keys or + label wildcard patterns that are synchronized from the + provider to the consumer for objects owned by the provider. + \n By default, no labels are synced." + items: + properties: + pattern: + type: string + type: object + type: array + labelsOnConsumerOwnedObjects: + description: "labelsOnConsumerOwnedObjects is a list of + claimed label key wildcard patterns that are synchronized + from the provider to the consumer for objects owned by + the consumer. \n By default, no labels are synced." + items: + properties: + pattern: + type: string + type: object + type: array + preserving: + description: "preserving is a list of JSON Paths describing + which parts of an object owned by the provider the consumer + keeps controlling. \n This field is ignored if the owner + in the claim selector is set to \"Consumer\"." + items: + type: string + type: array + type: object + version: + description: version is the version of the claimed resource. + minLength: 1 + type: string + required: + - resource + - version + type: object + x-kubernetes-validations: + - message: donate and adopt are mutually exclusive + rule: '!(has(self.autoDonate) && self.autoDonate && has(self.autoAdopt) + && self.autoAdopt)' + type: array + scope: + description: scope indicates whether the defined custom resource is + cluster- or namespace-scoped. Allowed values are `Cluster` and `Namespaced`. + enum: + - Cluster + - Namespaced + type: string + versions: + description: "versions is the API version of the defined custom resource. + \n Note: the OpenAPI v3 schemas must be equal for all versions until + CEL version migration is supported." + items: + description: APIServiceExportVersion describes one API version of + a resource. + properties: + additionalPrinterColumns: + description: additionalPrinterColumns specifies additional columns + returned in Table output. See https://kubernetes.io/docs/reference/using-api/api-concepts/#receiving-resources-as-tables + for details. If no columns are specified, a single column + displaying the age of the custom resource is used. + items: + description: CustomResourceColumnDefinition specifies a column + for server side printing. + properties: + description: + description: description is a human readable description + of this column. + type: string + format: + description: format is an optional OpenAPI type definition + for this column. The 'name' format is applied to the + primary identifier column to assist in clients identifying + column is the resource name. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types + for details. + type: string + jsonPath: + description: jsonPath is a simple JSON path (i.e. with + array notation) which is evaluated against each custom + resource to produce the value for this column. + type: string + name: + description: name is a human readable name for the column. + type: string + priority: + description: priority is an integer defining the relative + importance of this column compared to others. Lower + numbers are considered higher priority. Columns that + may be omitted in limited space scenarios should be + given a priority greater than 0. + format: int32 + type: integer + type: + description: type is an OpenAPI type definition for this + column. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types + for details. + type: string + required: + - jsonPath + - name + - type + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + deprecated: + description: deprecated indicates this version of the custom + resource API is deprecated. When set to true, API requests + to this version receive a warning header in the server response. + Defaults to false. + type: boolean + deprecationWarning: + description: deprecationWarning overrides the default warning + returned to API clients. May only be set when `deprecated` + is true. The default warning indicates this version is deprecated + and recommends use of the newest served version of equal or + greater stability, if one exists. + type: string + name: + description: name is the version name, e.g. “v1”, “v2beta1”, + etc. The custom resources are served under this version at + `/apis///...` if `served` is true. + minLength: 1 + pattern: ^v[1-9][0-9]*([a-z]+[1-9][0-9]*)?$ + type: string + schema: + description: schema describes the structural schema used for + validation, pruning, and defaulting of this version of the + custom resource. + properties: + openAPIV3Schema: + description: openAPIV3Schema is the OpenAPI v3 schema to + use for validation and pruning. + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + required: + - openAPIV3Schema + type: object + served: + default: true + description: served is a flag enabling/disabling this version + from being served via REST APIs + type: boolean + storage: + description: storage indicates this version should be used when + persisting custom resources to storage. There must be exactly + one version with storage=true. + type: boolean + subresources: + description: subresources specify what subresources this version + of the defined custom resource have. + properties: + scale: + description: scale indicates the custom resource should + serve a `/scale` subresource that returns an `autoscaling/v1` + Scale object. + properties: + labelSelectorPath: + description: 'labelSelectorPath defines the JSON path + inside of a custom resource that corresponds to Scale + `status.selector`. Only JSON paths without the array + notation are allowed. Must be a JSON Path under `.status` + or `.spec`. Must be set to work with HorizontalPodAutoscaler. + The field pointed by this JSON path must be a string + field (not a complex selector struct) which contains + a serialized label selector in string form. More info: + https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions#scale-subresource + If there is no value under the given path in the custom + resource, the `status.selector` value in the `/scale` + subresource will default to the empty string.' + type: string + specReplicasPath: + description: specReplicasPath defines the JSON path + inside of a custom resource that corresponds to Scale + `spec.replicas`. Only JSON paths without the array + notation are allowed. Must be a JSON Path under `.spec`. + If there is no value under the given path in the custom + resource, the `/scale` subresource will return an + error on GET. + type: string + statusReplicasPath: + description: statusReplicasPath defines the JSON path + inside of a custom resource that corresponds to Scale + `status.replicas`. Only JSON paths without the array + notation are allowed. Must be a JSON Path under `.status`. + If there is no value under the given path in the custom + resource, the `status.replicas` value in the `/scale` + subresource will default to 0. + type: string + required: + - specReplicasPath + - statusReplicasPath + type: object + status: + description: 'status indicates the custom resource should + serve a `/status` subresource. When enabled: 1. requests + to the custom resource primary endpoint ignore changes + to the `status` stanza of the object. 2. requests to the + custom resource `/status` subresource ignore changes to + anything other than the `status` stanza of the object.' + type: object + type: object + required: + - name + - schema + - served + - storage + type: object + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - informerScope + type: object + x-kubernetes-validations: + - message: informerScope must be Cluster for cluster-scoped resources + rule: self.scope == "Namespaced" || self.informerScope == "Cluster" + - message: clusterScopedIsolation must be defined for cluster-scoped resources + rule: self.scope == "Namespaced" || has(self.clusterScopedIsolation) + - message: clusterScopedIsolation is not relevant for namespaced resources + rule: self.scope == "Cluster" || !has(self.clusterScopedIsolation) + status: + description: status contains reconciliation information for the resource. + properties: + acceptedNames: + description: acceptedNames are the names that are actually being used + to serve discovery. They may be different than the names in spec. + properties: + categories: + description: categories is a list of grouped resources this custom + resource belongs to (e.g. 'all'). This is published in API discovery + documents, and used by clients to support invocations like `kubectl + get all`. + items: + type: string + type: array + kind: + description: kind is the serialized kind of the resource. It is + normally CamelCase and singular. Custom resource instances will + use this value as the `kind` attribute in API calls. + type: string + listKind: + description: listKind is the serialized kind of the list for this + resource. Defaults to "`kind`List". + type: string + plural: + description: plural is the plural name of the resource to serve. + The custom resources are served under `/apis///.../`. + Must match the name of the CustomResourceDefinition (in the + form `.`). Must be all lowercase. + type: string + shortNames: + description: shortNames are short names for the resource, exposed + in API discovery documents, and used by clients to support invocations + like `kubectl get `. It must be all lowercase. + items: + type: string + type: array + singular: + description: singular is the singular name of the resource. It + must be all lowercase. Defaults to lowercased `kind`. + type: string + required: + - kind + - plural + type: object + conditions: + description: conditions is a list of conditions that apply to the + APIServiceExport. It is updated by the konnector on the consumer + cluster. + items: + description: Condition defines an observation of a object operational + state. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. This should be when the underlying condition changed. + If that is not known, then using the time when the API field + changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. This field may be empty. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. The specific API may choose whether or not this + field is considered a guaranteed API. This field may not be + empty. + type: string + severity: + description: Severity provides an explicit classification of + Reason code, so the users or machines can immediately understand + the current situation and act accordingly. The Severity field + MUST be set only when Status=False. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase or in foo.example.com/CamelCase. + Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + storedVersions: + description: storedVersions lists all versions of CustomResources + that were ever persisted. Tracking these versions allows a migration + path for stored versions in etcd. The field is mutable so a migration + controller can finish a migration to another version (ensuring no + old objects are left in storage), and then remove the rest of the + versions from this list. Versions may not be removed from `spec.versions` + while they exist in this list. + items: + type: string + type: array + type: object + required: + - spec + type: object + x-kubernetes-validations: + - message: informerScope is immutable + rule: self.metadata.name == self.spec.names.plural+"."+self.spec.group + served: true + storage: true + subresources: diff --git a/charts/anynines-klutch/crds/klutch.anynines.com_apiserviceexportrequest.yaml b/charts/anynines-klutch/crds/klutch.anynines.com_apiserviceexportrequest.yaml new file mode 100644 index 000000000..baf7c46ff --- /dev/null +++ b/charts/anynines-klutch/crds/klutch.anynines.com_apiserviceexportrequest.yaml @@ -0,0 +1,450 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + name: apiserviceexportrequests.klutch.anynines.com +spec: + group: klutch.anynines.com + names: + categories: + - kube-bindings + kind: APIServiceExportRequest + listKind: APIServiceExportRequestList + plural: apiserviceexportrequests + singular: apiserviceexportrequest + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: "APIServiceExportRequest is represents a request session of kubectl-bind-apiservice. + \n The service provider can prune these objects after some time." + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: spec specifies how an API service from a service provider + should be bound in the local consumer cluster. + properties: + parameters: + description: parameters holds service provider specific parameters + for this binding request. + type: object + x-kubernetes-preserve-unknown-fields: true + x-kubernetes-validations: + - message: parameters are immutable + rule: self == oldSelf + resources: + description: resources is a list of resources that should be exported. + items: + properties: + group: + default: "" + description: group is the name of an API group. For core groups + this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + permissionClaims: + description: permissionClaims records decisions about permission + claims requested by the service provider. Individual claims + can be accepted or rejected. If accepted, the API service + provider gets the requested access to the specified resources + in this workspace. Access is granted per GroupResource, identity, + and other properties. + items: + description: permissionClaim selects objects of a GVR that + a service provider may request and that a consumer may accept + and allow the service provider access to. + properties: + autoAdopt: + description: autoAdopt set to true means that objects + created by the consumer are adopted by the provider. + i.e. the provider will become the owner. Mutually exclusive + with autoDonate. + type: boolean + autoDonate: + description: autoDonate set to true means that a newly + created object by the provider is immediately owned + by the consumer. If false, the object stays in ownership + of the provider. Mutually exclusive with autoDonate. + type: boolean + create: + description: create determines whether the kube-bind konnector + will sync matching objects from the provider cluster + down to the consumer cluster. only for owner Provider + properties: + replaceExisting: + description: "replaceExisting means that an existing + object owned by the consumer will be replaced by + the provider object. \n If set to false, and a conflicting + consumer object exists, it is not touched." + type: boolean + type: object + group: + default: "" + description: group is the name of an API group. For core + groups this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + onConflict: + description: onConflict determines how the conflicts between + objects on the consumer cluster will be resolved. + properties: + recreateWhenConsumerSideDeleted: + default: true + description: "recreateWhenConsumerSideDeleted set + to true (the default) means the provider will recreate + the object in case the object is missing on the + consumer cluster, but has been synchronized before. + \n If set to false, deleted provider-owned objects + get deleted on the provider cluster as well. \n + Even if the consumer mistakenly or intentionally + deletes the object, the provider will recreate it. + If the field is set as false, the provider will + not recreate the object in case the object is deleted + on the consumer cluster." + type: boolean + type: object + read: + description: read claims read access to matching objects + for the provider. reading of the claimed object(s) is + always claimed. By default no labels and annotations + are read. Reading of labels and annotations can be claimed + optionally by adding labels and annotations items. If + labels on consumer owned objects that are set by the + consumer are read, labelsOnProviderOwnedObjects and + annotationsOnProviderOwnedObjects can be set. + properties: + annotations: + description: annotations is a list of claimed annotation + key wildcard patterns that are synchronized from + the consumer cluster to the provider on objects + that are owned by the consumer. + items: + properties: + pattern: + type: string + type: object + type: array + labels: + description: labels is a list of claimed label key + wildcard patterns that are synchronized from the + consumer cluster to the provider on objects that + are owned by the consumer. + items: + properties: + pattern: + type: string + type: object + type: array + labelsOnProviderOwnedObjects: + description: labelsOnProviderOwnedObjects is a list + of claimed label key wildcard patterns that are + synchronized from the consumer cluster to the provider + on objects owned by the provider. + items: + properties: + pattern: + type: string + type: object + type: array + overrideAnnotations: + description: overrideAnnotations is a list of claimed + annotation key wildcard patterns that are synchronized + from the consumer cluster to the provider on objects + owned by the provider. + items: + properties: + pattern: + type: string + type: object + type: array + type: object + required: + description: required indicates whether the APIServiceBinding + will work if this claim is not accepted. If a required + claim is denied, the binding is aborted. + type: boolean + resource: + description: 'resource is the name of the resource. Note: + it is worth noting that you can not ask for permissions + for resource provided by a CRD not provided by an service + binding export.' + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + selector: + description: selector selects which resources are being + claimed. If unset, all resources across all namespaces + are being claimed. + properties: + fieldSelectors: + description: fieldSelectors is a list of field selectors + matching selected resources, see https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/. + items: + type: string + type: array + labelSelectors: + description: labelSelectors is a list of label selectors + matching selected resources. label selectors follow + the same rules as kubernetes label selectors, see + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/. + items: + additionalProperties: + type: string + type: object + type: array + names: + default: + - '*' + description: "names is a list of specific resource + names to select. Names matches the metadata.name + field of the underlying object. An entry of \"*\" + anywhere in the list means all object names of the + group/resource within the \"namespaces\" field are + claimed. Wildcard entries other than \"*\" and regular + expressions are currently unsupported. If a resources + name matches any value in names, the resource name + is considered matching. \n // +kubebuilder:validation:XValidation:rule=\"self.all(n, + n.matches('^[A-z-]+|[*]$'))\",message=\"only names + or * are allowed\"" + items: + type: string + type: array + namespaces: + default: + - '*' + description: "namespaces represents namespaces where + an object of the given group/resource may be managed. + Namespaces matches against the metadata.namespace + field. A value of \"*\" matches namespaced objects + across all namespaces. If a resources namespace + matches any value in namespaces, the resource namespace + is considered matching. If the claim is for a cluster-scoped + resource, namespaces has to explicitly be set to + an empty array to prevent defaulting to \"*\". If + the \"names\" field is unset, all objects of the + group/resource within the listed namespaces (or + cluster) will be claimed. \n // +kubebuilder:validation:XValidation:rule=\"self.all(n, + n.matches('^[A-z-]+|[*]$'))\",message=\"only names + or * are allowed\"" + items: + type: string + type: array + owner: + description: owner matches the resource's owner. If + an owner selector is set, resources owned by other + owners will not be claimed. Resources without a + present owner will be considered, if configured + owner could be the owner of the object. For example, + if the consumer creates a resource that is claimed + by the provider for reading. In this case the resource + will be marked as owned by the consumer, and handled + as such in further reconciliations. An unset owner + selector means objects from both sides are considered. + enum: + - Provider + - Consumer + type: string + type: object + update: + description: update lists which updates to objects on + the consumer cluster are claimed. By default, the whole + object is synced, but metadata is not. + properties: + alwaysRecreate: + description: "alwaysRecreate, when true will delete + the old object and create new ones instead of updating. + Useful for immutable objects. \n This does not apply + to metadata field updates." + type: boolean + annotations: + description: "annotations is a list of claimed annotation + keys or annotation wildcard patterns that are synchronized + from the provider to the consumer for objects owned + by the provider. \n By default, no annotations are + synced." + items: + properties: + pattern: + type: string + type: object + type: array + annotationsOnConsumerOwnedObjects: + description: "annotationsOnConsumerOwnedObjects is + a list of claimed annotation key wildcard patterns + that are synchronized from the provider to the consumer + for objects owned by the consumer. \n By default, + no annotations are synced." + items: + properties: + pattern: + type: string + type: object + type: array + fields: + description: "fields are a list of JSON Paths describing + which parts of an object the provider wants to control. + \n This field is ignored if the owner in the claim + selector is set to \"Provider\"." + items: + type: string + type: array + labels: + description: "labels is a list of claimed label keys + or label wildcard patterns that are synchronized + from the provider to the consumer for objects owned + by the provider. \n By default, no labels are synced." + items: + properties: + pattern: + type: string + type: object + type: array + labelsOnConsumerOwnedObjects: + description: "labelsOnConsumerOwnedObjects is a list + of claimed label key wildcard patterns that are + synchronized from the provider to the consumer for + objects owned by the consumer. \n By default, no + labels are synced." + items: + properties: + pattern: + type: string + type: object + type: array + preserving: + description: "preserving is a list of JSON Paths describing + which parts of an object owned by the provider the + consumer keeps controlling. \n This field is ignored + if the owner in the claim selector is set to \"Consumer\"." + items: + type: string + type: array + type: object + version: + description: version is the version of the claimed resource. + minLength: 1 + type: string + required: + - resource + - version + type: object + x-kubernetes-validations: + - message: donate and adopt are mutually exclusive + rule: '!(has(self.autoDonate) && self.autoDonate && has(self.autoAdopt) + && self.autoAdopt)' + type: array + resource: + description: 'resource is the name of the resource. Note: it + is worth noting that you can not ask for permissions for resource + provided by a CRD not provided by an service binding export.' + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + versions: + description: versions is a list of versions that should be exported. + If this is empty a sensible default is chosen by the service + provider. + items: + type: string + type: array + required: + - resource + type: object + minItems: 1 + type: array + x-kubernetes-validations: + - message: resources are immutable + rule: self == oldSelf + required: + - resources + type: object + status: + default: {} + description: status contains reconciliation information for a service + binding. + properties: + conditions: + description: conditions is a list of conditions that apply to the + ClusterBinding. It is updated by the konnector and the service provider. + items: + description: Condition defines an observation of a object operational + state. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. This should be when the underlying condition changed. + If that is not known, then using the time when the API field + changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. This field may be empty. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. The specific API may choose whether or not this + field is considered a guaranteed API. This field may not be + empty. + type: string + severity: + description: Severity provides an explicit classification of + Reason code, so the users or machines can immediately understand + the current situation and act accordingly. The Severity field + MUST be set only when Status=False. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase or in foo.example.com/CamelCase. + Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + phase: + default: Pending + description: phase is the current phase of the binding request. It + starts in Pending and transitions to Succeeded or Failed. See the + condition for detailed information. + enum: + - Pending + - Failed + - Succeeded + type: string + terminalMessage: + description: terminalMessage is a human readable message that describes + the reason for the current phase. + type: string + type: object + required: + - spec + type: object + served: true + storage: true + subresources: diff --git a/charts/anynines-klutch/crds/klutch.anynines.com_apiserviceexporttemplate.yaml b/charts/anynines-klutch/crds/klutch.anynines.com_apiserviceexporttemplate.yaml new file mode 100644 index 000000000..fbf46cf0f --- /dev/null +++ b/charts/anynines-klutch/crds/klutch.anynines.com_apiserviceexporttemplate.yaml @@ -0,0 +1,358 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: apiserviceexporttemplates.bind.anynines.com +spec: + group: bind.anynines.com + names: + categories: + - kube-bindings + kind: APIServiceExportTemplate + listKind: APIServiceExportTemplateList + plural: apiserviceexporttemplates + singular: apiserviceexporttemplate + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Established")].status + name: Established + priority: 5 + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + APIServiceExportTemplate specifies the resource to be exported. + It references the CRD to be exported along with additional resources that + are synchronized from and to the consumer cluster. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: spec specifies the resource. + properties: + APIServiceSelector: + properties: + group: + type: string + resource: + type: string + versions: + items: + type: string + type: array + type: object + permissionClaims: + items: + description: |- + permissionClaim selects objects of a GVR that a service provider may + request and that a consumer may accept and allow the service provider access to. + properties: + autoAdopt: + description: |- + autoAdopt set to true means that objects created by the consumer are adopted by the provider. + i.e. the provider will become the owner. + Mutually exclusive with autoDonate. + type: boolean + autoDonate: + description: |- + autoDonate set to true means that a newly created object by the provider is immediately owned by the consumer. + If false, the object stays in ownership of the provider. + Mutually exclusive with autoDonate. + type: boolean + create: + description: |- + create determines whether the kube-bind konnector will sync matching objects from the + provider cluster down to the consumer cluster. + only for owner Provider + properties: + replaceExisting: + description: |- + replaceExisting means that an existing object owned by the consumer will be replaced by the provider object. + + + If set to false, and a conflicting consumer object exists, it is not touched. + type: boolean + type: object + group: + default: "" + description: |- + group is the name of an API group. + For core groups this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + onConflict: + default: {} + description: onConflict determines how the conflicts between + objects on the consumer cluster will be resolved. + properties: + recreateWhenConsumerSideDeleted: + default: true + description: |- + recreateWhenConsumerSideDeleted set to true (the default) means the provider will recreate the object + in case the object is missing on the consumer cluster, but has been synchronized before. + + + If set to false, deleted provider-owned objects get deleted on the provider cluster as well. + + + Even if the consumer mistakenly or intentionally + deletes the object, the provider will recreate it. If the field is set as false, + the provider will not recreate the object in case the object is deleted on the consumer cluster. + type: boolean + type: object + read: + default: {} + description: |- + read claims read access to matching objects for the provider. + reading of the claimed object(s) is always claimed. + By default no labels and annotations are read. Reading of labels and annotations can be claimed + optionally by adding labels and annotations items. + If labels on consumer owned objects that are set by the consumer are read, labelsOnProviderOwnedObjects and + annotationsOnProviderOwnedObjects can be set. + properties: + annotations: + description: |- + annotations is a list of claimed annotation key wildcard patterns + that are synchronized from the consumer cluster to the provider on + objects that are owned by the consumer. + items: + properties: + pattern: + type: string + type: object + type: array + labels: + description: |- + labels is a list of claimed label key wildcard patterns + that are synchronized from the consumer cluster to the provider on + objects that are owned by the consumer. + items: + properties: + pattern: + type: string + type: object + type: array + labelsOnProviderOwnedObjects: + description: |- + labelsOnProviderOwnedObjects is a list of claimed label key wildcard + patterns that are synchronized from the consumer cluster + to the provider on objects owned by the provider. + items: + properties: + pattern: + type: string + type: object + type: array + overrideAnnotations: + description: |- + overrideAnnotations is a list of claimed annotation key wildcard + patterns that are synchronized from the consumer cluster + to the provider on objects owned by the provider. + items: + properties: + pattern: + type: string + type: object + type: array + type: object + required: + description: required indicates whether the APIServiceBinding + will work if this claim is not accepted. If a required claim + is denied, the binding is aborted. + type: boolean + resource: + description: |- + resource is the name of the resource. + Note: it is worth noting that you can not ask for permissions for resource provided by a CRD + not provided by an service binding export. + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + selector: + default: {} + description: |- + selector selects which resources are being claimed. + If unset, all resources across all namespaces are being claimed. + properties: + fieldSelectors: + description: |- + fieldSelectors is a list of field selectors matching selected resources, + see https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/. + items: + type: string + type: array + labelSelectors: + description: |- + labelSelectors is a list of label selectors matching selected resources. label selectors follow the same rules as kubernetes label selectors, + see https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/. + items: + additionalProperties: + type: string + type: object + type: array + names: + default: + - '*' + description: |- + names is a list of specific resource names to select. + Names matches the metadata.name field of the underlying object. + An entry of "*" anywhere in the list means all object names of the group/resource within the "namespaces" field are claimed. + Wildcard entries other than "*" and regular expressions are currently unsupported. + If a resources name matches any value in names, the resource name is considered matching. + + + // +kubebuilder:validation:XValidation:rule="self.all(n, n.matches('^[A-z-]+|[*]$'))",message="only names or * are allowed" + items: + type: string + type: array + namespaces: + default: + - '*' + description: |- + namespaces represents namespaces where an object of the given group/resource may be managed. + Namespaces matches against the metadata.namespace field. A value of "*" matches namespaced objects across all namespaces. + If a resources namespace matches any value in namespaces, the resource namespace is considered matching. + If the claim is for a cluster-scoped resource, namespaces has to explicitly be set to an empty array to prevent defaulting to "*". + If the "names" field is unset, all objects of the group/resource within the listed namespaces (or cluster) will be claimed. + + + // +kubebuilder:validation:XValidation:rule="self.all(n, n.matches('^[A-z-]+|[*]$'))",message="only names or * are allowed" + items: + type: string + type: array + owner: + description: |- + owner matches the resource's owner. If an owner selector is set, resources owned by other owners will not be claimed. + Resources without a present owner will be considered, if configured owner could be the owner of the object. + For example, if the consumer creates a resource that is claimed by the provider for reading. In this case the resource + will be marked as owned by the consumer, and handled as such in further reconciliations. + An unset owner selector means objects from both sides are considered. + enum: + - Provider + - Consumer + type: string + type: object + update: + description: |- + update lists which updates to objects on the consumer cluster are claimed. + By default, the whole object is synced, but metadata is not. + properties: + alwaysRecreate: + description: |- + alwaysRecreate, when true will delete the old object and create new ones + instead of updating. Useful for immutable objects. + + + This does not apply to metadata field updates. + type: boolean + annotations: + description: |- + annotations is a list of claimed annotation keys or annotation wildcard patterns that are synchronized from the provider to the consumer for objects owned by the provider. + + + By default, no annotations are synced. + items: + properties: + pattern: + type: string + type: object + type: array + annotationsOnConsumerOwnedObjects: + description: |- + annotationsOnConsumerOwnedObjects is a list of claimed annotation key wildcard patterns that are synchronized from the provider to the consumer for objects owned by the consumer. + + + By default, no annotations are synced. + items: + properties: + pattern: + type: string + type: object + type: array + fields: + description: |- + fields are a list of JSON Paths describing which parts of an object the provider wants to control. + + + This field is ignored if the owner in the claim selector is set to "Provider". + items: + type: string + type: array + labels: + description: |- + labels is a list of claimed label keys or label wildcard patterns that are synchronized from the provider to the consumer for objects owned by the provider. + + + By default, no labels are synced. + items: + properties: + pattern: + type: string + type: object + type: array + labelsOnConsumerOwnedObjects: + description: |- + labelsOnConsumerOwnedObjects is a list of claimed label key wildcard patterns that are synchronized from the provider to the consumer for objects owned by the consumer. + + + By default, no labels are synced. + items: + properties: + pattern: + type: string + type: object + type: array + preserving: + description: |- + preserving is a list of JSON Paths describing which parts of an object owned by the provider the consumer keeps controlling. + + + This field is ignored if the owner in the claim selector is set to "Consumer". + items: + type: string + type: array + type: object + version: + description: version is the version of the claimed resource. + minLength: 1 + type: string + required: + - resource + - version + type: object + x-kubernetes-validations: + - message: donate and adopt are mutually exclusive + rule: '!(has(self.autoDonate) && self.autoDonate && has(self.autoAdopt) + && self.autoAdopt)' + type: array + type: object + status: + description: status contains reconciliation information for the resource. + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + diff --git a/charts/anynines-klutch/crds/klutch.anynines.com_apiservicenamespace.yaml b/charts/anynines-klutch/crds/klutch.anynines.com_apiservicenamespace.yaml new file mode 100644 index 000000000..d86b0eadb --- /dev/null +++ b/charts/anynines-klutch/crds/klutch.anynines.com_apiservicenamespace.yaml @@ -0,0 +1,60 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + name: apiservicenamespaces.klutch.anynines.com +spec: + group: klutch.anynines.com + names: + categories: + - kube-bindings + kind: APIServiceNamespace + listKind: APIServiceNamespaceList + plural: apiservicenamespaces + singular: apiservicenamespace + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.namespace + name: Namespace + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: "APIServiceNamespace defines how consumer namespaces map to service + namespaces. These objects are created by the konnector, and a service namespace + is then created by the service provider. \n The name of the APIServiceNamespace + equals the namespace name in the consumer cluster." + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: spec specifies a service namespace. + type: object + status: + description: status contains reconciliation information for a service + namespace + properties: + namespace: + description: namespace is the service provider namespace name that + will be bound to the consumer namespace named like this object. + type: string + type: object + type: object + served: true + storage: true + subresources: diff --git a/charts/anynines-klutch/crds/klutch.anynines.com_clusterbinding.yaml b/charts/anynines-klutch/crds/klutch.anynines.com_clusterbinding.yaml new file mode 100644 index 000000000..d477ec715 --- /dev/null +++ b/charts/anynines-klutch/crds/klutch.anynines.com_clusterbinding.yaml @@ -0,0 +1,163 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + name: clusterbindings.klutch.anynines.com +spec: + group: klutch.anynines.com + names: + categories: + - kube-bindings + kind: ClusterBinding + listKind: ClusterBindingList + plural: clusterbindings + singular: clusterbinding + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.konnectorVersion + name: Konnector Version + type: string + - jsonPath: .status.lastHeartbeatTime + name: Last Heartbeat + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterBinding represents a bound consumer class. It lives in + a service provider cluster and is a singleton named "cluster". + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: spec represents the data in the newly created ClusterBinding. + properties: + kubeconfigSecretRef: + description: kubeconfigSecretName is the secret ref that contains + the kubeconfig of the service cluster. + properties: + key: + description: The key of the secret to select from. Must be "kubeconfig". + enum: + - kubeconfig + type: string + name: + description: Name of the referent. + minLength: 1 + type: string + required: + - key + - name + type: object + x-kubernetes-validations: + - message: kubeconfigSecretRef is immutable + rule: self == oldSelf + providerPrettyName: + description: providerPrettyName is the pretty name of the service + provider cluster. This can be shared among different ServiceBindings. + minLength: 1 + type: string + serviceProviderSpec: + description: serviceProviderSpec contains all the data and information + about the service which has been bound to the service binding request. + The service providers decide what they need and what to configure + based on what then include in this field, such as service region, + type, tiers, etc... + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - kubeconfigSecretRef + - providerPrettyName + type: object + status: + description: status contains reconciliation information for the service + binding. + properties: + conditions: + description: conditions is a list of conditions that apply to the + ClusterBinding. It is updated by the konnector and the service provider. + items: + description: Condition defines an observation of a object operational + state. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. This should be when the underlying condition changed. + If that is not known, then using the time when the API field + changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. This field may be empty. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. The specific API may choose whether or not this + field is considered a guaranteed API. This field may not be + empty. + type: string + severity: + description: Severity provides an explicit classification of + Reason code, so the users or machines can immediately understand + the current situation and act accordingly. The Severity field + MUST be set only when Status=False. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase or in foo.example.com/CamelCase. + Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + heartbeatInterval: + description: heartbeatInterval is the maximal interval between heartbeats + that the konnector promises to send. The service provider can assume + that the konnector is not unhealthy if it does not receive a heartbeat + within this time. + type: string + konnectorVersion: + description: konnectorVersion is the version of the konnector that + is running on the consumer cluster. + type: string + lastHeartbeatTime: + description: lastHeartbeatTime is the last time the konnector updated + the status. + format: date-time + type: string + type: object + required: + - spec + type: object + x-kubernetes-validations: + - message: cluster binding name should be cluster + rule: self.metadata.name == "cluster" + served: true + storage: true + subresources: diff --git a/charts/anynines-klutch/dashboards/loki.json b/charts/anynines-klutch/dashboards/loki.json new file mode 100644 index 000000000..ca8bb07fb --- /dev/null +++ b/charts/anynines-klutch/dashboards/loki.json @@ -0,0 +1,1044 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Universal and flexible dashboard for logging", + "editable": true, + "gnetId": 12611, + "graphTooltip": 0, + "id": 25, + "iteration": 1603358789722, + "links": [], + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorPostfix": true, + "colorValue": true, + "colors": [ + "rgb(31, 255, 7)", + "rgb(31, 255, 7)", + "rgb(31, 255, 7)" + ], + "datasource": "Loki", + "description": "Total Count of log lines in the specified time range", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "format": "short", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 3, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 11, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "pluginVersion": "6.4.3", + "postfix": " lines", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgb(0, 82, 0)", + "full": false, + "lineColor": "rgb(31, 255, 7)", + "show": true, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(count_over_time(({container=\"$container\", stream=~\"$stream\", pod=~\"$pod\"})[$__interval]))", + "hide": false, + "refId": "A" + } + ], + "thresholds": "10,50", + "timeFrom": null, + "timeShift": null, + "title": "Total Count of logs", + "type": "singlestat", + "valueFontSize": "70%", + "valueMaps": [ + { + "op": "=", + "text": "0", + "value": "null" + } + ], + "valueName": "total" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorPostfix": true, + "colorPrefix": false, + "colorValue": true, + "colors": [ + "rgb(222, 15, 43)", + "rgb(222, 15, 43)", + "rgb(222, 15, 43)" + ], + "datasource": "Loki", + "description": "Total Count: of $searchable_pattern in the specified time range", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "format": "short", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 3, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 6, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "pluginVersion": "6.4.3", + "postfix": " \"$searchable_pattern\"", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgb(105, 34, 43)", + "full": false, + "lineColor": "#C4162A", + "show": true, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(count_over_time(({container=\"$container\", stream=~\"$stream\", pod=~\"$pod\"} |~ \"(?i)$searchable_pattern\")[$__interval]))", + "hide": false, + "refId": "A" + } + ], + "thresholds": "10,50", + "timeFrom": null, + "timeShift": null, + "title": "Total Count: of $searchable_pattern", + "type": "singlestat", + "valueFontSize": "70%", + "valueMaps": [ + { + "op": "=", + "text": "0", + "value": "null" + } + ], + "valueName": "total" + }, + { + "datasource": "Loki", + "description": "Live logs is a like 'tail -f' in a real time", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 3 + }, + "id": 2, + "options": { + "showLabels": true, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "expr": "{container=\"$container\", pod=~\"$pod\", stream=~\"$stream\"} |~ \"(?i)$searchable_pattern\" ", + "hide": false, + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Live logs", + "type": "logs" + }, + { + "content": "", + "datasource": "${DS_NY-ALERTING2}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 24, + "x": 0, + "y": 12 + }, + "id": 15, + "mode": "html", + "options": {}, + "targets": [ + { + "refId": "A", + "target": "" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "", + "type": "text" + }, + { + "aliasColors": { + "stderr": "#C4162A", + "stdout": "#37872D" + }, + "breakPoint": "50%", + "cacheTimeout": null, + "combine": { + "label": "Others", + "threshold": 0 + }, + "datasource": "Loki", + "decimals": null, + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [ + { + "id": 0, + "op": "=", + "text": "0", + "type": 1, + "value": "null" + } + ], + "nullValueMode": "connected", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#299c46", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 10 + }, + { + "color": "#C4162A", + "value": 50 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "fontSize": "80%", + "format": "short", + "gridPos": { + "h": 7, + "w": 7, + "x": 0, + "y": 14 + }, + "id": 19, + "interval": null, + "legend": { + "header": "Total Count", + "percentage": true, + "percentageDecimals": 2, + "show": true, + "sort": "total", + "sortDesc": true, + "values": true + }, + "legendType": "Under graph", + "links": [], + "maxDataPoints": 100, + "nullPointMode": "connected", + "options": {}, + "pieType": "pie", + "pluginVersion": "7.0.4", + "strokeWidth": "1.5", + "targets": [ + { + "expr": "sum(count_over_time(({container=\"$container\", pod=~\"$pod\"})[$__interval])) by (stream)", + "hide": false, + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total count of stderr / stdout pie", + "type": "grafana-piechart-panel", + "valueName": "total" + }, + { + "aliasColors": {}, + "breakPoint": "50%", + "cacheTimeout": null, + "combine": { + "label": "Others", + "threshold": 0 + }, + "datasource": "Loki", + "decimals": 0, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fontSize": "80%", + "format": "short", + "gridPos": { + "h": 7, + "w": 12, + "x": 7, + "y": 14 + }, + "id": 20, + "interval": "1m", + "legend": { + "header": "total count", + "percentage": true, + "show": true, + "sort": "total", + "sortDesc": true, + "values": true + }, + "legendType": "Right side", + "links": [], + "maxDataPoints": "", + "nullPointMode": "connected", + "options": {}, + "pieType": "donut", + "pluginVersion": "6.4.3", + "strokeWidth": "0.4", + "targets": [ + { + "expr": "sum(count_over_time(({container=\"$container\", pod=~\"$pod\", stream=~\"$stream\"} |~ \"(?i)$searchable_pattern\")[$__interval])) by (pod)", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Matched word: \"$searchable_pattern\" donut", + "type": "grafana-piechart-panel", + "valueName": "total" + }, + { + "cacheTimeout": null, + "datasource": "Loki", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [ + { + "id": 0, + "op": "=", + "text": "0", + "type": 1, + "value": "null" + } + ], + "max": 100, + "min": 0, + "noValue": "0", + "nullValueMode": "connected", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#299c46", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 10 + }, + { + "color": "#C4162A", + "value": 50 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 19, + "y": 14 + }, + "id": 9, + "interval": null, + "links": [], + "maxDataPoints": 100, + "options": { + "fieldOptions": { + "calcs": [ + "mean" + ], + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [], + "values": false + }, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": false + }, + "pluginVersion": "6.6.0", + "targets": [ + { + "expr": "sum(count_over_time(({container=\"$container\", stream=~\"$stream\", pod=~\"$pod\"} |~ \"(?i)$searchable_pattern\")[$__interval])) * 100 / sum(count_over_time(({container=\"$container\", stream=~\"$stream\", pod=~\"$pod\"})[$__interval]))", + "hide": false, + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "\"$searchable_pattern\" Percentage for specified time", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": true, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "Loki", + "decimals": 0, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 1, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 21 + }, + "hiddenSeries": false, + "id": 18, + "interval": "1m", + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "hideZero": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": false, + "linewidth": 3, + "links": [], + "maxDataPoints": "", + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.4.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(count_over_time(({container=\"$container\", pod=~\"$pod\", stream=~\"$stream\"} |~ \"(?i)$searchable_pattern\")[$__interval])) by (pod)", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Matched word: \"$searchable_pattern\" historical", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": "Count", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "Loki", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 10, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 21 + }, + "hiddenSeries": false, + "id": 10, + "interval": null, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.4.3", + "pointradius": 0.5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(({container=\"$container\", stream=~\"$stream\", pod=~\"$pod\"} |~ \"(?i)$searchable_pattern\")[30s])) by (pod)", + "hide": false, + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "\"$searchable_pattern\" Rate per Pod", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 10, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "Loki", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [ + { + "id": 0, + "op": "=", + "text": "0", + "type": 1, + "value": "null" + } + ], + "nullValueMode": "connected", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#299c46", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 10 + }, + { + "color": "#C4162A", + "value": 50 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "fill": 2, + "fillGradient": 4, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 30 + }, + "hiddenSeries": false, + "id": 7, + "interval": null, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "7.0.4", + "pointradius": 2, + "points": true, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "{stream=\"stderr\"} stderr", + "color": "#C4162A", + "legend": false, + "linewidth": 2 + }, + { + "alias": "{stream=\"stdout\"} stdout", + "color": "#56A64B", + "legend": false, + "linewidth": 2 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(count_over_time(({container=\"$container\", pod=~\"$pod\"})[$__interval])) by (stream)", + "hide": false, + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Count of stderr / stdout historical", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "columns": [], + "datasource": "Loki", + "fontSize": "100%", + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 38 + }, + "id": 22, + "options": {}, + "pageSize": null, + "showHeader": true, + "sort": { + "col": 0, + "desc": true + }, + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "align": "right", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "{container=\"$container\"} |~ \"(?i)$searchable_pattern\"", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Panel Title", + "transform": "table", + "type": "table" + } + ], + "refresh": "5s", + "schemaVersion": 22, + "style": "dark", + "tags": [ + "Loki", + "logging" + ], + "templating": { + "list": [ + { + "allValue": null, + "current": { + "isNone": true, + "selected": false, + "text": "None", + "value": "" + }, + "datasource": "Loki", + "definition": "label_values({container=~\".+\"}, container)", + "hide": 0, + "includeAll": false, + "label": "Service", + "multi": false, + "name": "container", + "options": [], + "query": "label_values({container=~\".+\"}, container)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": "Loki", + "definition": "label_values({container=\"$container\"}, pod)", + "hide": 0, + "includeAll": true, + "label": "Pod", + "multi": false, + "name": "pod", + "options": [], + "query": "label_values({container=\"$container\"}, pod)", + "refresh": 2, + "regex": "$container.*", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": "Loki", + "definition": "label_values({container=\"$container\"}, stream)", + "hide": 0, + "includeAll": true, + "label": "Stream", + "multi": false, + "name": "stream", + "options": [], + "query": "label_values({container=\"$container\"}, stream)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": { + "selected": false, + "text": "error", + "value": "error" + }, + "hide": 0, + "label": "Search (case insensitive)", + "name": "searchable_pattern", + "options": [ + { + "selected": true, + "text": "", + "value": "" + } + ], + "query": "", + "skipUrlSync": false, + "type": "textbox" + } + ] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Logging Dashboard via Loki", + "uid": "fRIvzUZMz", + "version": 3 +} diff --git a/charts/anynines-klutch/helmrelease.schema.json b/charts/anynines-klutch/helmrelease.schema.json new file mode 100644 index 000000000..8dbdfe600 --- /dev/null +++ b/charts/anynines-klutch/helmrelease.schema.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "allOf": [ + { + "$ref": "https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2beta2.json" + }, + { + "properties": { + "spec": { + "properties": { + "values": { + "$ref": "./values.schema.json" + } + } + } + } + } + ] +} diff --git a/charts/anynines-klutch/templates/NOTES.txt b/charts/anynines-klutch/templates/NOTES.txt new file mode 100644 index 000000000..e69de29bb diff --git a/charts/anynines-klutch/templates/anynines-kube-bind/apiServiceExportTemplate.yaml b/charts/anynines-klutch/templates/anynines-kube-bind/apiServiceExportTemplate.yaml new file mode 100644 index 000000000..b84d67e4f --- /dev/null +++ b/charts/anynines-klutch/templates/anynines-kube-bind/apiServiceExportTemplate.yaml @@ -0,0 +1,59 @@ +{{- range $name := .Values.dataservices | keys -}} +--- +kind: APIServiceExportTemplate +apiVersion: bind.anynines.com/v1alpha1 +metadata: + name: {{ printf "%sinstances" $name }} + namespace: {{ $.Release.Namespace }} + labels: {{- include "common.labels.standard" $ | nindent 4 }} +spec: + APIServiceSelector: + resource: {{ printf "%sinstances" $name }} + group: anynines.com +{{- end }} +--- +kind: APIServiceExportTemplate +apiVersion: bind.anynines.com/v1alpha1 +metadata: + name: servicebindings + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +spec: + APIServiceSelector: + resource: servicebindings + group: anynines.com + permissionClaims: + - group: "" + resource: secrets + version: v1 + selector: + owner: Provider + - group: "" + resource: configmaps + version: v1 + selector: + owner: Provider +{{- if .Values.backupManager }} +--- +kind: APIServiceExportTemplate +apiVersion: bind.anynines.com/v1alpha1 +metadata: + name: backups + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +spec: + APIServiceSelector: + resource: backups + group: anynines.com +--- +kind: APIServiceExportTemplate +apiVersion: bind.anynines.com/v1alpha1 +metadata: + name: restores + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +spec: + APIServiceSelector: + resource: restores + group: anynines.com +{{- end }} diff --git a/charts/anynines-klutch/templates/anynines-kube-bind/clusterRole.yaml b/charts/anynines-klutch/templates/anynines-kube-bind/clusterRole.yaml new file mode 100644 index 000000000..45804a30f --- /dev/null +++ b/charts/anynines-klutch/templates/anynines-kube-bind/clusterRole.yaml @@ -0,0 +1,76 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kube-binder + labels: {{- include "common.labels.standard" . | nindent 4 }} +rules: + - apiGroups: + - kube-bind.io + resources: + - apiserviceexportrequests + verbs: + - create + - delete + - patch + - update + - get + - list + - watch + - apiGroups: + - '' + resources: + - namespaces + verbs: + - get + - apiGroups: + - '' + resources: + - secrets + verbs: + - get + - watch + - list + - apiGroups: + - kube-bind.io + resources: + - clusterbindings + verbs: + - get + - watch + - list + - apiGroups: + - kube-bind.io + resources: + - clusterbindings/status + verbs: + - get + - patch + - update + - apiGroups: + - kube-bind.io + resources: + - apiserviceexports + verbs: + - get + - watch + - list + - apiGroups: + - kube-bind.io + resources: + - apiserviceexports/status + verbs: + - get + - patch + - update + - apiGroups: + - kube-bind.io + resources: + - apiservicenamespaces + verbs: + - create + - delete + - patch + - update + - get + - list + - watch diff --git a/charts/anynines-klutch/templates/anynines-kube-bind/clusterRoleBinding.yaml b/charts/anynines-klutch/templates/anynines-kube-bind/clusterRoleBinding.yaml new file mode 100644 index 000000000..71800c4f0 --- /dev/null +++ b/charts/anynines-klutch/templates/anynines-kube-bind/clusterRoleBinding.yaml @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace }} diff --git a/charts/anynines-klutch/templates/anynines-kube-bind/deployment.yaml b/charts/anynines-klutch/templates/anynines-kube-bind/deployment.yaml new file mode 100644 index 000000000..6cafbdd03 --- /dev/null +++ b/charts/anynines-klutch/templates/anynines-kube-bind/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +spec: + replicas: 1 + selector: + matchLabels: {{ include "common.labels.matchLabels" . | nindent 6 }} + strategy: + type: Recreate + template: + metadata: + annotations: + checksum/cookies-secret: {{ include "common.utils.checksumTemplate" (dict "path" "/anynines-kube-bind/secret-cookies.yaml" "context" $) | sha256sum }} + checksum/oidc-secret: {{ include "common.utils.checksumTemplate" (dict "path" "/oidc/secret.yaml" "context" $) | sha256sum }} + labels: {{ include "common.labels.standard" . | nindent 8 }} + spec: + serviceAccountName: {{ include "common.names.fullname" . }} + automountServiceAccountToken: true + containers: + - name: anynines-backend + image: {{ include "common.images.image" (dict "imageRoot" .Values.global.kubebind.image "global" .Values.global) }} + args: + - --namespace-prefix=cluster + - --pretty-name=anynines + - --consumer-scope=Namespaced + - --oidc-issuer-client-id=$(OIDC-ISSUER-CLIENT-ID) + - --oidc-issuer-client-secret=$(OIDC-ISSUER-CLIENT-SECRET) + - --oidc-issuer-url=$(OIDC-ISSUER-URL) + - --oidc-callback-url=$(OIDC-CALLBACK-URL) + - --listen-address=0.0.0.0:9443 + - --cookie-signing-key=$(COOKIE-SIGNING-KEY) + - --cookie-encryption-key=$(COOKIE-ENCRYPTION-KEY) + - --external-address={{ .Values.kubernetes.externalAddress }} + ports: + - containerPort: 9443 + name: https + env: + - name: OIDC-ISSUER-CLIENT-ID + value: kube-bind + - name: OIDC-ISSUER-CLIENT-SECRET + valueFrom: + secretKeyRef: + name: {{ printf "%s-oidc" (include "common.names.fullname" .) }} + key: kube-bind-client-secret + - name: OIDC-ISSUER-URL + value: {{ printf "https://%s" .Values.oidc.ingress.host }} + - name: OIDC-CALLBACK-URL + value: {{ printf "https://%s/callback" .Values.ingress.host }} + - name: COOKIE-SIGNING-KEY + valueFrom: + secretKeyRef: + name: {{ printf "%s-kube-bind-cookies" (include "common.names.fullname" .) }} + key: signing-key + - name: COOKIE-ENCRYPTION-KEY + valueFrom: + secretKeyRef: + name: {{ printf "%s-kube-bind-cookies" (include "common.names.fullname" .) }} + key: encryption-key + resources: + limits: + cpu: "2" + memory: 2Gi + requests: + cpu: "100m" + memory: 256Mi diff --git a/charts/anynines-klutch/templates/anynines-kube-bind/ingress.yaml b/charts/anynines-klutch/templates/anynines-kube-bind/ingress.yaml new file mode 100644 index 000000000..8dafd2087 --- /dev/null +++ b/charts/anynines-klutch/templates/anynines-kube-bind/ingress.yaml @@ -0,0 +1,24 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + annotations: + kubernetes.io/tls-acme: "true" +spec: + rules: + - host: &host {{ .Values.ingress.host }} + http: + paths: + - pathType: Prefix + path: "/" + backend: + service: + name: {{ include "common.names.fullname" . }} + port: + name: https + tls: + - secretName: {{ printf "%s-tls" (include "common.names.fullname" .) }} + hosts: + - *host diff --git a/charts/anynines-klutch/templates/anynines-kube-bind/secret-cookies.yaml b/charts/anynines-klutch/templates/anynines-kube-bind/secret-cookies.yaml new file mode 100644 index 000000000..fd6e82097 --- /dev/null +++ b/charts/anynines-klutch/templates/anynines-kube-bind/secret-cookies.yaml @@ -0,0 +1,17 @@ +{{- $secretName := printf "%s-kube-bind-cookies" (include "common.names.fullname" .) -}} +{{- $secret := lookup "v1" "Secret" .Release.Namespace $secretName -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +type: Opaque +{{- if $secret }} +data: +{{- else }} +stringData: +{{- end }} + # This might change on every `template` call, this can be ignored + signing-key: {{ include "common.secrets.passwords.manage" (dict "secret" $secretName "length" 32 "strong" false "key" "signing-key" "failOnNew" false "context" $ "providedValues" (list)) }} + encryption-key: {{ include "common.secrets.passwords.manage" (dict "secret" $secretName "length" 32 "strong" false "key" "encryption-key" "failOnNew" false "context" $ "providedValues" (list)) }} diff --git a/charts/anynines-klutch/templates/anynines-kube-bind/service.yaml b/charts/anynines-klutch/templates/anynines-kube-bind/service.yaml new file mode 100644 index 000000000..6b5e152ab --- /dev/null +++ b/charts/anynines-klutch/templates/anynines-kube-bind/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +spec: + ports: + - protocol: TCP + name: https + port: 443 + targetPort: https + selector: {{ include "common.labels.matchLabels" . | nindent 4}} diff --git a/charts/anynines-klutch/templates/anynines-kube-bind/serviceAccount.yaml b/charts/anynines-klutch/templates/anynines-kube-bind/serviceAccount.yaml new file mode 100644 index 000000000..c264c1988 --- /dev/null +++ b/charts/anynines-klutch/templates/anynines-kube-bind/serviceAccount.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} diff --git a/charts/anynines-klutch/templates/crossplane/providerConfig.yaml b/charts/anynines-klutch/templates/crossplane/providerConfig.yaml new file mode 100644 index 000000000..1d0298bde --- /dev/null +++ b/charts/anynines-klutch/templates/crossplane/providerConfig.yaml @@ -0,0 +1,76 @@ +{{- range $name, $dataservice := .Values.dataservices -}} + {{- $resourceName := printf "%s-service-broker" $name -}} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $resourceName }} + namespace: {{ $.Release.Namespace }} + labels: {{- include "common.labels.standard" $ | nindent 4 }} + app.kubernetes.io/component: {{ $name }} +type: Opaque +stringData: + username: {{ $dataservice.username | quote }} + password: {{ $dataservice.password | quote }} +--- +apiVersion: dataservices.anynines.com/v1 +kind: ProviderConfig +metadata: + name: {{ $resourceName }} + namespace: {{ $.Release.Namespace }} + labels: {{- include "common.labels.standard" $ | nindent 4 }} + app.kubernetes.io/component: {{ $name }} +spec: + url: {{ $dataservice.url | quote }} + providerCredentials: + source: Secret + username: + secretRef: + namespace: crossplane-system + name: {{ $resourceName }} + key: username + password: + secretRef: + namespace: crossplane-system + name: {{ $resourceName }} + key: password + {{- if $.Values.backupManager -}} + {{- $backupManagerResourceName := printf "%s-backup-manager" $name }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $backupManagerResourceName }} + namespace: {{ $.Release.Namespace }} + labels: {{- include "common.labels.standard" $ | nindent 4 }} + app.kubernetes.io/component: backup-manager + app.kubernetes.io/part-of: {{ $name }} +type: Opaque +stringData: + username: {{ $.Values.backupManager.username | quote }} + password: {{ $.Values.backupManager.password | quote }} +--- +apiVersion: dataservices.anynines.com/v1 +kind: ProviderConfig +metadata: + name: {{ $backupManagerResourceName }} + namespace: {{ $.Release.Namespace }} + labels: {{- include "common.labels.standard" $ | nindent 4 }} + app.kubernetes.io/component: backup-manager + app.kubernetes.io/part-of: {{ $name }} +spec: + url: {{ $.Values.backupManager.url | quote }} + providerCredentials: + source: Secret + username: + secretRef: + namespace: crossplane-system + name: {{ $backupManagerResourceName }} + key: username + password: + secretRef: + namespace: crossplane-system + name: {{ $backupManagerResourceName }} + key: password + {{- end }} +{{- end -}} diff --git a/charts/anynines-klutch/templates/oidc/dex.yaml b/charts/anynines-klutch/templates/oidc/dex.yaml index e69de29bb..9b37a8af7 100644 --- a/charts/anynines-klutch/templates/oidc/dex.yaml +++ b/charts/anynines-klutch/templates/oidc/dex.yaml @@ -0,0 +1,72 @@ +{{- if false -}} +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: {{ printf "%s-dex" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +spec: + url: https://charts.dexidp.io +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ printf "%s-dex" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +spec: + chart: + spec: + chart: dex + sourceRef: + name: {{ printf "%s-dex" (include "common.names.fullname" .) }} + kind: HelmRepository + version: 0.18.0 + interval: 1h + values: + ingress: + enabled: true + annotations: + kubernetes.io/tls-acme: "true" + hosts: + - host: &host {{ .Values.oidc.ingress.host }} + paths: + - path: / + pathType: Prefix + tls: + - hosts: + - *host + secretName: {{ printf "%s-dex-tls" (include "common.names.fullname" .) }} + envVars: + - name: HELPER_CLI_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: &secretName {{ printf "%s-oidc" (include "common.names.fullname" .)}} + key: helper-cli-client-secret + - name: KUBE_BIND_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: *secretName + key: kube-bind-client-secret + config: + issuer: {{ printf "https://%s" .Values.oidc.ingress.host }} + storage: + type: memory + # *some* connector has to be enabled... + enablePasswordDB: true + oauth2: + skipApprovalScreen: true + staticClients: + - id: helper-cli + name: Help CLI for automated cluster binding + secret: $HELPER_CLI_CLIENT_SECRET + public: true + - id: kube-bind + redirectURIs: + - {{ printf "https://%s/callback" .Values.ingress.host }} + name: Tenant Cluster Binding + secret: $KUBE_BIND_CLIENT_SECRET + public: true + trustedPeers: + - helper-cli +{{- end -}} diff --git a/charts/anynines-klutch/templates/oidc/keycloak-secret.yaml b/charts/anynines-klutch/templates/oidc/keycloak-secret.yaml index e69de29bb..22b28859d 100644 --- a/charts/anynines-klutch/templates/oidc/keycloak-secret.yaml +++ b/charts/anynines-klutch/templates/oidc/keycloak-secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-stupid-keycloak" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +type: Opaque +stringData: + db-password: "" + unused: "" diff --git a/charts/anynines-klutch/templates/oidc/keycloak.yaml b/charts/anynines-klutch/templates/oidc/keycloak.yaml index e69de29bb..f647ad2ef 100644 --- a/charts/anynines-klutch/templates/oidc/keycloak.yaml +++ b/charts/anynines-klutch/templates/oidc/keycloak.yaml @@ -0,0 +1,112 @@ +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ printf "%s-keycloak" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +spec: + chart: + spec: + chart: keycloak + sourceRef: + name: bitnami + namespace: flux-system + kind: HelmRepository + version: 22.x.x + interval: 1h + values: + postgresql: + enabled: false + extraEnvVars: +{{/* - name: KC_DB*/}} +{{/* value: dev-mem*/}} + - name: KEYCLOAK_DATABASE_VENDOR + value: dev-file + externalDatabase: + existingSecret: {{ printf "%s-stupid-keycloak" (include "common.names.fullname" .) }} + existingSecretHostKey: unused + existingSecretPortKey: unused + existingSecretDatabaseKey: unused + existingSecretUserKey: unused + extraVolumes: + - name: h2 + persistentVolumeClaim: + claimName: {{ printf "%s-keycloak-db" (include "common.names.fullname" .) }} + extraVolumeMounts: + - name: h2 + mountPath: /opt/bitnami/keycloak/data/h2 + proxy: edge + auth: + adminUser: admin + adminIngress: + tls: true + ingress: + enabled: true + annotations: + kubernetes.io/tls-acme: "true" + hostname: {{ .Values.oidc.ingress.host }} + tls: true + keycloakConfigCli: + enabled: true + # TODO: workaround, see: https://github.com/bitnami/charts/issues/14279 + command: + - java + - -jar + - /opt/bitnami/keycloak-config-cli/keycloak-config-cli.jar + extraEnvVars: + - name: HELPER_CLI_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: &secretName {{ printf "%s-oidc" (include "common.names.fullname" .)}} + key: helper-cli-client-secret + - name: KUBE_BIND_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: *secretName + key: kube-bind-client-secret + configuration: + a9s.yaml: | + enabled: true + registrationAllowed: false + realm: a9s + displayName: a9s platform + clients: + - clientId: kube-bind + enabled: true + secret: ${env:KUBE_BIND_CLIENT_SECRET} + consentRequired: false + authorizationServicesEnabled: true + serviceAccountsEnabled: true + standardFlowEnabled: true + redirectUris: + - {{ printf "https://%s/callback" .Values.ingress.host }} + - clientId: helper-cli + enabled: true + secret: ${env:HELPER_CLI_CLIENT_SECRET} + consentRequired: false + standardFlowEnabled: false + serviceAccountsEnabled: true + protocolMappers: + - name: kube-bind + protocol: openid-connect + protocolMapper: oidc-audience-mapper + config: + included.client.audience: kube-bind + id.token.claim: true + access.token.claim: true + introspection.token.claim: true + lightweight.claim: false + config: + staticClients: + - id: helper-cli + name: Help CLI for automated cluster binding + secret: $HELPER_CLI_CLIENT_SECRET + public: true + - id: kube-bind + redirectURIs: + - {{ printf "https://%s/callback" .Values.ingress.host }} + name: Tenant Cluster Binding + secret: $KUBE_BIND_CLIENT_SECRET + public: true + trustedPeers: + - helper-cli diff --git a/charts/anynines-klutch/templates/oidc/persistentVolumeClaim.yaml b/charts/anynines-klutch/templates/oidc/persistentVolumeClaim.yaml new file mode 100644 index 000000000..da6b05a5f --- /dev/null +++ b/charts/anynines-klutch/templates/oidc/persistentVolumeClaim.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ printf "%s-keycloak-db" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi diff --git a/charts/anynines-klutch/templates/oidc/secret.yaml b/charts/anynines-klutch/templates/oidc/secret.yaml new file mode 100644 index 000000000..b53d026ad --- /dev/null +++ b/charts/anynines-klutch/templates/oidc/secret.yaml @@ -0,0 +1,11 @@ +{{- $secretName := printf "%s-oidc" (include "common.names.fullname" .) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +type: Opaque +data: + kube-bind-client-secret: {{ include "common.secrets.passwords.manage" (dict "secret" $secretName "length" 32 "strong" false "key" "kube-bind-client-secret" "failOnNew" false "context" $ "providedValues" (list)) }} + helper-cli-client-secret: {{ include "common.secrets.passwords.manage" (dict "secret" $secretName "length" 32 "strong" false "key" "helper-cli-client-secret" "failOnNew" false "context" $ "providedValues" (list)) }} diff --git a/charts/anynines-klutch/values.schema.json b/charts/anynines-klutch/values.schema.json new file mode 100644 index 000000000..94983adeb --- /dev/null +++ b/charts/anynines-klutch/values.schema.json @@ -0,0 +1,174 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema", + "type": "object", + "title": "base cluster configuration", + "properties": { + "global": { + "type": "object", + "properties": { + "provider": { + "properties": { + "image": { + "$ref": "#/$defs/image" + } + } + }, + "configuration": { + "properties": { + "image": { + "$ref": "#/$defs/image" + } + } + } + } + }, + "dataservices": { + "type": "object", + "description": "See https://docs.k8s.anynines.com/docs/platform-operator/central-management-cluster-setup/#install-providerconfig for the valid values, using the values from the `Data-service Value` column", + "additionalProperties": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "username": { + "type": "string" + }, + "password": { + "type": "string" + } + }, + "required": [ + "url", + "username", + "password" + ], + "additionalProperties": false + }, + "minProperties": 1 + }, + "backupManager": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "username": { + "type": "string" + }, + "password": { + "type": "string" + } + }, + "required": [ + "url", + "username", + "password" + ], + "additionalProperties": false + }, + "oidc": { + "type": "object", + "properties": { + "ingress": { + "type": "object", + "properties": { + "host": { + "type": "string" + } + }, + "required": [ + "host" + ], + "additionalProperties": false + } + }, + "required": [ + "ingress" + ], + "additionalProperties": false + }, + "ingress": { + "type": "object", + "properties": { + "host": { + "type": "string" + } + }, + "required": [ + "host" + ], + "additionalProperties": false + }, + "kubernetes": { + "type": "object", + "properties": { + "externalAddress": { + "type": "string" + } + }, + "required": [ + "externalAddress" + ], + "additionalProperties": false + }, + "crossplane": { + "type": "object", + "description": "Values for sub-chart" + }, + "common": { + "type": "object", + "description": "Values for sub-chart" + } + }, + "required": [ + "dataservices", + "ingress", + "kubernetes", + "oidc" + ], + "additionalProperties": false, + "$defs": { + "resourceRequirements": { + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/master-standalone-strict/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements" + }, + "resourcesPreset": { + "$ref": "https://raw.githubusercontent.com/teutonet/teutonet-helm-charts/main/charts/common/values.schema.json#/$defs/resourcesPreset" + }, + "quantity": { + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/master-standalone-strict/_definitions.json#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "tolerations": { + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/master-standalone-strict/_definitions.json#/definitions/io.k8s.api.core.v1.Toleration" + }, + "image": { + "type": "object", + "properties": { + "registry": { + "type": "string", + "description": "The host of the registry", + "examples": [ + "docker.io" + ] + }, + "repository": { + "type": "string", + "description": "The image path in the registry", + "examples": [ + "bitnami/kubectl" + ] + }, + "tag": { + "type": "string" + }, + "digest": { + "type": "string" + } + }, + "additionalProperties": false + }, + "email": { + "pattern": "(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])" + } + } +} diff --git a/charts/anynines-klutch/values.yaml b/charts/anynines-klutch/values.yaml new file mode 100644 index 000000000..a933d5c94 --- /dev/null +++ b/charts/anynines-klutch/values.yaml @@ -0,0 +1,16 @@ +global: + provider: + image: + registry: public.ecr.aws + repository: w5n9a2g2/anynines/provider-anynines + tag: v1.2.0 + configuration: + image: + registry: public.ecr.aws + repository: w5n9a2g2/anynines/dataservices + tag: v1.2.0 + kubebind: + image: + registry: public.ecr.aws + repository: w5n9a2g2/anynines/kubebind-backend + tag: v1.3.0 From e8c384f96e6dd875fb3efbc22c71bcdd5a3ce8a7 Mon Sep 17 00:00:00 2001 From: Chris Werner Rau Date: Wed, 7 Aug 2024 10:53:45 +0200 Subject: [PATCH 2/3] fix(anynines-klutch/keycloak): use correct form of var interpolation --- charts/anynines-klutch/templates/oidc/keycloak.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/charts/anynines-klutch/templates/oidc/keycloak.yaml b/charts/anynines-klutch/templates/oidc/keycloak.yaml index f647ad2ef..988c7a98e 100644 --- a/charts/anynines-klutch/templates/oidc/keycloak.yaml +++ b/charts/anynines-klutch/templates/oidc/keycloak.yaml @@ -53,6 +53,7 @@ spec: - java - -jar - /opt/bitnami/keycloak-config-cli/keycloak-config-cli.jar + - --import.var-substitution.enabled=true extraEnvVars: - name: HELPER_CLI_CLIENT_SECRET valueFrom: @@ -73,7 +74,7 @@ spec: clients: - clientId: kube-bind enabled: true - secret: ${env:KUBE_BIND_CLIENT_SECRET} + secret: $(env:KUBE_BIND_CLIENT_SECRET) consentRequired: false authorizationServicesEnabled: true serviceAccountsEnabled: true @@ -82,7 +83,7 @@ spec: - {{ printf "https://%s/callback" .Values.ingress.host }} - clientId: helper-cli enabled: true - secret: ${env:HELPER_CLI_CLIENT_SECRET} + secret: $(env:HELPER_CLI_CLIENT_SECRET) consentRequired: false standardFlowEnabled: false serviceAccountsEnabled: true From 65aa8c00bfd9b125e64551c105fb88c6534e368d Mon Sep 17 00:00:00 2001 From: Chris Werner Rau Date: Fri, 9 Aug 2024 12:31:20 +0200 Subject: [PATCH 3/3] chore: add external ca certificate --- charts/anynines-klutch/ci/basic-values.yaml | 2 +- .../templates/anynines-kube-bind/deployment.yaml | 12 +++++++++++- .../anynines-kube-bind/secret-kubernetes-ca.yaml | 9 +++++++++ charts/anynines-klutch/templates/oidc/_helpers.tpl | 3 +++ charts/anynines-klutch/templates/oidc/keycloak.yaml | 2 +- charts/anynines-klutch/values.schema.json | 3 +++ 6 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 charts/anynines-klutch/templates/anynines-kube-bind/secret-kubernetes-ca.yaml create mode 100644 charts/anynines-klutch/templates/oidc/_helpers.tpl diff --git a/charts/anynines-klutch/ci/basic-values.yaml b/charts/anynines-klutch/ci/basic-values.yaml index 51cf558ea..9ed998270 100644 --- a/charts/anynines-klutch/ci/basic-values.yaml +++ b/charts/anynines-klutch/ci/basic-values.yaml @@ -11,7 +11,7 @@ backupManager: oidc: ingress: - host: dex.a9s.cwrau.wtf + host: oidc.a9s.cwrau.wtf ingress: host: klutch.a9s.cwrau.wtf diff --git a/charts/anynines-klutch/templates/anynines-kube-bind/deployment.yaml b/charts/anynines-klutch/templates/anynines-kube-bind/deployment.yaml index 6cafbdd03..fe57d2f71 100644 --- a/charts/anynines-klutch/templates/anynines-kube-bind/deployment.yaml +++ b/charts/anynines-klutch/templates/anynines-kube-bind/deployment.yaml @@ -19,6 +19,7 @@ spec: spec: serviceAccountName: {{ include "common.names.fullname" . }} automountServiceAccountToken: true + enableServiceLinks: false containers: - name: anynines-backend image: {{ include "common.images.image" (dict "imageRoot" .Values.global.kubebind.image "global" .Values.global) }} @@ -33,7 +34,9 @@ spec: - --listen-address=0.0.0.0:9443 - --cookie-signing-key=$(COOKIE-SIGNING-KEY) - --cookie-encryption-key=$(COOKIE-ENCRYPTION-KEY) + # TODO: a9s will look into just talking to klutch instead of the k8s API - --external-address={{ .Values.kubernetes.externalAddress }} + - --external-ca-file=/cert/ca.crt ports: - containerPort: 9443 name: https @@ -46,7 +49,7 @@ spec: name: {{ printf "%s-oidc" (include "common.names.fullname" .) }} key: kube-bind-client-secret - name: OIDC-ISSUER-URL - value: {{ printf "https://%s" .Values.oidc.ingress.host }} + value: {{ printf "https://%s/realms/%s" .Values.oidc.ingress.host (include "anynines-klutch.oidc.realm" (dict)) }} - name: OIDC-CALLBACK-URL value: {{ printf "https://%s/callback" .Values.ingress.host }} - name: COOKIE-SIGNING-KEY @@ -66,3 +69,10 @@ spec: requests: cpu: "100m" memory: 256Mi + volumeMounts: + - mountPath: /cert + name: certificate + volumes: + - name: certificate + secret: + secretName: {{ printf "%s-kubernetes-ca-certificate" (include "common.names.fullname" .) }} diff --git a/charts/anynines-klutch/templates/anynines-kube-bind/secret-kubernetes-ca.yaml b/charts/anynines-klutch/templates/anynines-kube-bind/secret-kubernetes-ca.yaml new file mode 100644 index 000000000..70c47558f --- /dev/null +++ b/charts/anynines-klutch/templates/anynines-kube-bind/secret-kubernetes-ca.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-kubernetes-ca-certificate" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} +type: Generic +stringData: + ca.crt: {{ .Values.kubernetes.caCertificate | quote }} diff --git a/charts/anynines-klutch/templates/oidc/_helpers.tpl b/charts/anynines-klutch/templates/oidc/_helpers.tpl new file mode 100644 index 000000000..20187bb55 --- /dev/null +++ b/charts/anynines-klutch/templates/oidc/_helpers.tpl @@ -0,0 +1,3 @@ +{{- define "anynines-klutch.oidc.realm" -}} +a9s +{{- end -}} diff --git a/charts/anynines-klutch/templates/oidc/keycloak.yaml b/charts/anynines-klutch/templates/oidc/keycloak.yaml index 988c7a98e..667ebe048 100644 --- a/charts/anynines-klutch/templates/oidc/keycloak.yaml +++ b/charts/anynines-klutch/templates/oidc/keycloak.yaml @@ -69,7 +69,7 @@ spec: a9s.yaml: | enabled: true registrationAllowed: false - realm: a9s + realm: {{ include "anynines-klutch.oidc.realm" (dict) }} displayName: a9s platform clients: - clientId: kube-bind diff --git a/charts/anynines-klutch/values.schema.json b/charts/anynines-klutch/values.schema.json index 94983adeb..5cde1eeb7 100644 --- a/charts/anynines-klutch/values.schema.json +++ b/charts/anynines-klutch/values.schema.json @@ -105,6 +105,9 @@ "properties": { "externalAddress": { "type": "string" + }, + "caCertificate": { + "type": "string" } }, "required": [