From ab877093c95818a21a4516c4475456338b6612f2 Mon Sep 17 00:00:00 2001 From: Malte Poll Date: Tue, 2 May 2023 09:33:55 +0200 Subject: [PATCH 1/2] cli: add yawol helm charts --- .../charts/yawol-config/.helmignore | 23 + .../charts/yawol-config/Chart.yaml | 5 + .../charts/yawol-config/templates/secret.yaml | 7 + .../charts/yawol-config/values.schema.json | 17 + .../charts/yawol-config/values.yaml | 0 .../charts/yawol-controller/Chart.yaml | 7 + .../charts/yawol-controller/README.md | 55 +++ ...ol.stackit.cloud_loadbalancermachines.yaml | 319 ++++++++++++ .../yawol.stackit.cloud_loadbalancers.yaml | 454 ++++++++++++++++++ .../yawol.stackit.cloud_loadbalancersets.yaml | 306 ++++++++++++ .../yawol-controller/templates/_helpers.tpl | 3 + .../rbac-yawol-cloud-controller.yaml | 113 +++++ .../templates/rbac-yawol-controller.yaml | 84 ++++ .../templates/sa-yawol-cloud-controller.yaml | 5 + .../templates/sa-yawol-controller.yaml | 5 + .../yawol-controller/templates/vpa.yaml | 27 ++ .../templates/yawol-cloud-controller.yaml | 96 ++++ .../templates/yawol-controller.yaml | 131 +++++ .../templates/yawol-gardener-monitoring.yaml | 140 ++++++ .../charts/yawol-controller/values.yaml | 100 ++++ 20 files changed, 1897 insertions(+) create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/.helmignore create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/Chart.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/templates/secret.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/values.schema.json create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/values.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/Chart.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/README.md create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancermachines.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancers.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancersets.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/_helpers.tpl create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/rbac-yawol-cloud-controller.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/rbac-yawol-controller.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/sa-yawol-cloud-controller.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/sa-yawol-controller.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/vpa.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-cloud-controller.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-controller.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-gardener-monitoring.yaml create mode 100644 cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/values.yaml diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/.helmignore b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/.helmignore @@ -0,0 +1,23 @@ +# 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/ diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/Chart.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/Chart.yaml new file mode 100644 index 0000000000..f625ef3eca --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: yawol-config +description: A Helm chart for Kubernetes +type: application +version: 0.0.0 diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/templates/secret.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/templates/secret.yaml new file mode 100644 index 0000000000..1a7025ba3c --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/templates/secret.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: yawolkey + namespace: {{ .Release.Namespace }} +data: + cloudprovider.conf: {{ .Values.secretData | b64enc }} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/values.schema.json b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/values.schema.json new file mode 100644 index 0000000000..9e71fb5f6a --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/values.schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "properties": { + "secretData": { + "description": "Cloud INI secret used for yawol.", + "type": "string", + "examples": [ + "[Global]\nauth-url = ..." + ] + } + }, + "required": [ + "secretData" + ], + "title": "Values", + "type": "object" +} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/values.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-config/values.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/Chart.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/Chart.yaml new file mode 100644 index 0000000000..cccefe85b9 --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +description: Helm chart for yawol-controller +name: yawol-controller +sources: + - https://github.com/stackitcloud/yawol +version: 0.14.0 +appVersion: v0.14.0 diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/README.md b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/README.md new file mode 100644 index 0000000000..43a44e8414 --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/README.md @@ -0,0 +1,55 @@ +# yawol-controller + +![Version: 0.12.0](https://img.shields.io/badge/Version-0.12.0-informational?style=flat-square) ![AppVersion: v0.12.0](https://img.shields.io/badge/AppVersion-v0.12.0-informational?style=flat-square) + +Helm chart for yawol-controller + +## Source Code + +* + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| featureGates | object | `{}` | | +| namespace | string | `"kube-system"` | | +| podAnnotations | object | `{}` | | +| podLabels | object | `{}` | | +| proxy | object | `{}` | | +| replicas | int | `1` | | +| resources.yawolCloudController.limits.cpu | string | `"500m"` | | +| resources.yawolCloudController.limits.memory | string | `"512Mi"` | | +| resources.yawolCloudController.requests.cpu | string | `"100m"` | | +| resources.yawolCloudController.requests.memory | string | `"64Mi"` | | +| resources.yawolControllerLoadbalancer.limits.cpu | string | `"500m"` | | +| resources.yawolControllerLoadbalancer.limits.memory | string | `"512Mi"` | | +| resources.yawolControllerLoadbalancer.requests.cpu | string | `"100m"` | | +| resources.yawolControllerLoadbalancer.requests.memory | string | `"64Mi"` | | +| resources.yawolControllerLoadbalancermachine.limits.cpu | string | `"500m"` | | +| resources.yawolControllerLoadbalancermachine.limits.memory | string | `"512Mi"` | | +| resources.yawolControllerLoadbalancermachine.requests.cpu | string | `"100m"` | | +| resources.yawolControllerLoadbalancermachine.requests.memory | string | `"64Mi"` | | +| resources.yawolControllerLoadbalancerset.limits.cpu | string | `"500m"` | | +| resources.yawolControllerLoadbalancerset.limits.memory | string | `"512Mi"` | | +| resources.yawolControllerLoadbalancerset.requests.cpu | string | `"100m"` | | +| resources.yawolControllerLoadbalancerset.requests.memory | string | `"64Mi"` | | +| vpa.enabled | bool | `false` | | +| vpa.yawolCloudController.mode | string | `"Auto"` | | +| vpa.yawolController.mode | string | `"Auto"` | | +| yawolAPIHost | string | `nil` | | +| yawolAvailabilityZone | string | `""` | | +| yawolCloudController.clusterRoleEnabled | bool | `true` | | +| yawolCloudController.enabled | bool | `true` | | +| yawolCloudController.gardenerMonitoringEnabled | bool | `false` | | +| yawolCloudController.image.repository | string | `"ghcr.io/stackitcloud/yawol/yawol-cloud-controller"` | | +| yawolCloudController.image.tag | string | `""` | Allows you to override the yawol version in this chart. Use at your own risk. | +| yawolController.gardenerMonitoringEnabled | bool | `false` | | +| yawolController.image.repository | string | `"ghcr.io/stackitcloud/yawol/yawol-controller"` | | +| yawolController.image.tag | string | `""` | Allows you to override the yawol version in this chart. Use at your own risk. | +| yawolFlavorID | string | `nil` | | +| yawolFloatingID | string | `nil` | | +| yawolImageID | string | `nil` | | +| yawolNetworkID | string | `nil` | | +| yawolOSSecretName | string | `nil` | | + diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancermachines.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancermachines.yaml new file mode 100644 index 0000000000..4c2ef948ee --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancermachines.yaml @@ -0,0 +1,319 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.3 + creationTimestamp: null + name: loadbalancermachines.yawol.stackit.cloud +spec: + group: yawol.stackit.cloud + names: + kind: LoadBalancerMachine + listKind: LoadBalancerMachineList + plural: loadbalancermachines + shortNames: + - lbm + singular: loadbalancermachine + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="EnvoyUpToDate")].status + name: EnvoyUpToDate + type: string + - jsonPath: .status.conditions[?(@.type=="KeepalivedMaster")].status + name: KeepalivedMaster + type: string + - jsonPath: .status.metrics[?(@.type=="load1")].value + name: Load1 + type: string + - jsonPath: .status.creationTimestamp + name: creationTimestamp + type: string + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: LoadBalancerMachine is the Schema for the LoadBalancerMachine's + API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: LoadBalancerMachineSpec defines the desired state of LoadBalancerMachine + properties: + infrastructure: + description: Infrastructure defines parameters for the Infrastructure. + properties: + additionalNetworks: + description: AdditionalNetworks defines additional networks that + will be added to the LoadBalancerMachines. + items: + description: LoadBalancerAdditionalNetwork defines additional + networks for the LoadBalancer + properties: + networkID: + description: NetworkID defines an openstack ID for the network. + type: string + required: + - networkID + type: object + type: array + authSecretRef: + description: AuthSecretRef defines a secretRef for the openstack + secret. + properties: + name: + description: name is unique within a namespace to reference + a secret resource. + type: string + namespace: + description: namespace defines the space within which the + secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + availabilityZone: + description: AvailabilityZone defines the openstack availability + zone for the LoadBalancer. + type: string + defaultNetwork: + description: DefaultNetwork defines the default/listener network + for the Loadbalancer. TODO Remove optional when Deprecations + are removed + properties: + floatingNetID: + description: FloatingNetID defines an openstack ID for the + floatingNet. + type: string + networkID: + description: NetworkID defines an openstack ID for the network. + type: string + required: + - networkID + type: object + flavor: + description: Flavor defines openstack flavor for the LoadBalancer. + properties: + flavor_id: + description: 'Deprecated: use flavorID instead.' + type: string + flavor_name: + description: 'Deprecated: use flavorName instead.' + type: string + flavor_search: + description: 'Deprecated: use flavorSearch instead.' + type: string + flavorID: + description: FlavorID is the flavor ID used for requesting + virtual machines. + type: string + flavorName: + description: NOT IMPLEMENTED ONLY FlavorID is supported. FlavorName + is the name of the flavor used for requesting virtual machines. + FlavorName is only used if FlavorID is not defined. + type: string + flavorSearch: + description: NOT IMPLEMENTED ONLY FlavorID is supported. FlavorSearch + is a search string to find the flavor used for requesting + virtual machines. Search will be performed in metadata of + the flavors. FlavorSearch is only used if FlavorName and + FlavorID are not defined. + type: string + type: object + floatingNetID: + description: 'Deprecated: use defaultNetwork instead FloatingNetID + defines a openstack ID for the floatingNet.' + type: string + image: + description: Image defines openstack image for the LoadBalancer. + properties: + image_id: + description: 'Deprecated: use imageID instead.' + type: string + image_name: + description: 'Deprecated: use imageName instead.' + type: string + image_search: + description: 'Deprecated: use imageSearch instead.' + type: string + imageID: + description: ImageID is the image ID used for requesting virtual + machines. + type: string + imageName: + description: NOT IMPLEMENTED ONLY ImageID is supported. ImageName + is the name of the image used for requesting virtual machines. + ImageName is only used if ImageID is not defined. + type: string + imageSearch: + description: NOT IMPLEMENTED ONLY ImageID is supported. ImageSearch + is a search string to find the image used for requesting + virtual machines. Search will be performed in metadata of + the images. ImageSearch is only used if ImageName and ImageID + are not defined. + type: string + type: object + networkID: + description: 'Deprecated: use defaultNetwork instead NetworkID + defines a openstack ID for the network.' + type: string + projectID: + description: ProjectID defines an openstack project ID which will + be used instead of the project from the secret ref. If not set + the project from the secret ref will be used. + type: string + required: + - authSecretRef + - flavor + - image + type: object + loadBalancerRef: + description: LoadBalancerRef defines a reference to the LoadBalancer + Object. + properties: + name: + description: Name is unique within a namespace to reference a + LoadBalancer resource. + type: string + namespace: + description: Namespace defines the space within which the LoadBalancer + name must be unique. + type: string + required: + - name + - namespace + type: object + portID: + description: PortID defines the openstack ID of the port attached + to the FloatingIP. + type: string + serverGroupID: + description: ServerGroupID defines the openstack ID of the openstack + server group. + type: string + required: + - infrastructure + - loadBalancerRef + - portID + type: object + status: + description: LoadBalancerMachineStatus defines the observed state of LoadBalancerMachine. + properties: + conditions: + description: Conditions contains condition information for a LoadBalancerMachine. + items: + description: NodeCondition contains condition information for a + node. + properties: + lastHeartbeatTime: + description: Last time we got an update on a given condition. + format: date-time + type: string + lastTransitionTime: + description: Last time the condition transit from one status + to another. + format: date-time + type: string + message: + description: Human readable message indicating details about + last transition. + type: string + reason: + description: (brief) reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of node condition. + type: string + required: + - status + - type + type: object + type: array + creationTimestamp: + description: CreationTimestamp contains the creation timestamp a LoadBalancerMachine. + format: date-time + type: string + defaultPortID: + description: DefaultPortID contains the default openstack port ID + for a LoadBalancerMachine. + type: string + defaultPortIP: + description: DefaultPortIP contains the default openstack port IP + for a LoadBalancerMachine. + type: string + defaultPortName: + description: DefaultPortName contains the default openstack port Name + for a LoadBalancerMachine. + type: string + lastOpenstackReconcile: + description: LastOpenstackReconcile contains the timestamp of the + last openstack reconciliation. + format: date-time + type: string + metrics: + description: Metrics contains metrics for a LoadBalancerMachine. + items: + description: LoadBalancerMachineMetric describes a metric of the + LoadBalancerMachine + properties: + timestamp: + description: Time is the timestamp if the metric + format: date-time + type: string + type: + description: Type is the type of the metric + type: string + value: + description: Value is the value of a metric + type: string + required: + - timestamp + - type + - value + type: object + type: array + portID: + description: 'Deprecated: use defaultPortID instead PortID contains + the openstack port ID for a LoadBalancerMachine.' + type: string + roleBindingName: + description: RoleBindingName contains the namespacedName from the + RoleBinding for a LoadBalancerMachine. + type: string + roleName: + description: RoleName contains the namespacedName from the Role for + a LoadBalancerMachine. + type: string + serverID: + description: ServerID contains the openstack server ID for a LoadBalancerMachine. + type: string + serviceAccountName: + description: ServiceAccountName contains the namespacedName from the + ServiceAccount for a LoadBalancerMachine. + type: string + serviceAccountSecretName: + description: SecretName contains the namespacedName from the Secret + which belongs to the Serviceaccount. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancers.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancers.yaml new file mode 100644 index 0000000000..5b8b8a3226 --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancers.yaml @@ -0,0 +1,454 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.3 + creationTimestamp: null + name: loadbalancers.yawol.stackit.cloud +spec: + group: yawol.stackit.cloud + names: + kind: LoadBalancer + listKind: LoadBalancerList + plural: loadbalancers + shortNames: + - lb + singular: loadbalancer + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.replicas + name: DESIRED + type: string + - jsonPath: .status.replicas + name: CURRENT + type: string + - jsonPath: .status.readyReplicas + name: READY + type: string + - jsonPath: .status.externalIP + name: externalIP + type: string + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: LoadBalancer is the Schema for the YAWOL LoadBalancer API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: LoadBalancerSpec defines the desired state of LoadBalancer + properties: + debugSettings: + description: Debug are settings for debugging an loadbalancer. + properties: + enabled: + description: Enabled defines if debugging is enabled + type: boolean + sshkeyName: + description: SshKey is a openstack sshkey name for debugging + type: string + type: object + endpoints: + description: Endpoints defines the Endpoints for the LoadBalancer. + items: + description: LoadBalancerEndpoint defines a Endpoint for the LoadBalancer + properties: + addresses: + description: Addresses is a list of addresses for the endpoint, + they can contain IPv4 and IPv6 addresses. + items: + type: string + type: array + name: + description: 'Name defines a name for the Endpoint (example: + node name).' + type: string + required: + - name + type: object + type: array + existingFloatingIP: + description: ExistingFloatingIP uses a existing Floating IP as FIP + type: string + infrastructure: + description: Infrastructure defines parameters for the Infrastructure + properties: + additionalNetworks: + description: AdditionalNetworks defines additional networks that + will be added to the LoadBalancerMachines. + items: + description: LoadBalancerAdditionalNetwork defines additional + networks for the LoadBalancer + properties: + networkID: + description: NetworkID defines an openstack ID for the network. + type: string + required: + - networkID + type: object + type: array + authSecretRef: + description: AuthSecretRef defines a secretRef for the openstack + secret. + properties: + name: + description: name is unique within a namespace to reference + a secret resource. + type: string + namespace: + description: namespace defines the space within which the + secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + availabilityZone: + description: AvailabilityZone defines the openstack availability + zone for the LoadBalancer. + type: string + defaultNetwork: + description: DefaultNetwork defines the default/listener network + for the Loadbalancer. TODO Remove optional when Deprecations + are removed + properties: + floatingNetID: + description: FloatingNetID defines an openstack ID for the + floatingNet. + type: string + networkID: + description: NetworkID defines an openstack ID for the network. + type: string + required: + - networkID + type: object + flavor: + description: Flavor defines openstack flavor for the LoadBalancer. + properties: + flavor_id: + description: 'Deprecated: use flavorID instead.' + type: string + flavor_name: + description: 'Deprecated: use flavorName instead.' + type: string + flavor_search: + description: 'Deprecated: use flavorSearch instead.' + type: string + flavorID: + description: FlavorID is the flavor ID used for requesting + virtual machines. + type: string + flavorName: + description: NOT IMPLEMENTED ONLY FlavorID is supported. FlavorName + is the name of the flavor used for requesting virtual machines. + FlavorName is only used if FlavorID is not defined. + type: string + flavorSearch: + description: NOT IMPLEMENTED ONLY FlavorID is supported. FlavorSearch + is a search string to find the flavor used for requesting + virtual machines. Search will be performed in metadata of + the flavors. FlavorSearch is only used if FlavorName and + FlavorID are not defined. + type: string + type: object + floatingNetID: + description: 'Deprecated: use defaultNetwork instead FloatingNetID + defines a openstack ID for the floatingNet.' + type: string + image: + description: Image defines openstack image for the LoadBalancer. + properties: + image_id: + description: 'Deprecated: use imageID instead.' + type: string + image_name: + description: 'Deprecated: use imageName instead.' + type: string + image_search: + description: 'Deprecated: use imageSearch instead.' + type: string + imageID: + description: ImageID is the image ID used for requesting virtual + machines. + type: string + imageName: + description: NOT IMPLEMENTED ONLY ImageID is supported. ImageName + is the name of the image used for requesting virtual machines. + ImageName is only used if ImageID is not defined. + type: string + imageSearch: + description: NOT IMPLEMENTED ONLY ImageID is supported. ImageSearch + is a search string to find the image used for requesting + virtual machines. Search will be performed in metadata of + the images. ImageSearch is only used if ImageName and ImageID + are not defined. + type: string + type: object + networkID: + description: 'Deprecated: use defaultNetwork instead NetworkID + defines a openstack ID for the network.' + type: string + projectID: + description: ProjectID defines an openstack project ID which will + be used instead of the project from the secret ref. If not set + the project from the secret ref will be used. + type: string + required: + - authSecretRef + - flavor + - image + type: object + options: + description: Options for additional LoadBalancer settings + properties: + internalLB: + default: false + description: InternalLB is a bool for internal LoadBalancer. If + set to false a FloatingIP will be assigned to the LB. Defaults + to false. + type: boolean + loadBalancerSourceRanges: + description: LoadBalancerSourceRanges restrict traffic to IP ranges + for the LoadBalancer (copy from service) + items: + type: string + type: array + logForward: + description: LogForward enables log forward to a loki instance + properties: + enabled: + description: Enabled defines if log forward is enabled + type: boolean + lokiUrl: + description: 'LokiUrl defines the loki push url (Example: + http://example.com:3100/loki/api/v1/push).' + type: string + type: object + serverGroupPolicy: + description: ServerGroupPolicy creates a server group with that + policy. Can be 'affinity', 'anti-affinity' 'soft-affinity', + 'soft-anti-affinity' depending on the OpenStack Infrastructure. + If empty Openstack server group will not be used. Default is + disabled + type: string + tcpIdleTimeout: + description: TCPIdleTimeout sets TCP idle Timeout for all TCP + connections from this LoadBalancer. Value is in Seconds. With + 0 you disable the idle timeout, be careful this can lead to + side effects. Default is 1h. + type: string + tcpProxyProtocol: + description: TCPProxyProtocol enables HAProxy TCP Proxy Protocol + type: boolean + tcpProxyProtocolPortFilter: + description: TCPProxyProtocolPortList enables HAProxy TCP Proxy + Protocol for specified ports. If empty it is enabled for all + ports. Only has an affect if TCPProxyProtocol is enabled. + items: + format: int32 + type: integer + type: array + udpIdleTimeout: + description: UDPIdleTimeout sets UDP idle Timeout for all UDP + connections from this LoadBalancer. Value is in Seconds. With + 0 you disable the idle timeout, be careful this can lead to + side effects. Default is 1m. + type: string + type: object + ports: + description: Ports defines the Ports for the LoadBalancer (copy from + service) + items: + description: ServicePort contains information on service's port. + properties: + appProtocol: + description: The application protocol for this port. This field + follows standard Kubernetes label syntax. Un-prefixed names + are reserved for IANA standard service names (as per RFC-6335 + and https://www.iana.org/assignments/service-names). Non-standard + protocols should use prefixed names such as mycompany.com/my-custom-protocol. + type: string + name: + description: The name of this port within the service. This + must be a DNS_LABEL. All ports within a ServiceSpec must have + unique names. When considering the endpoints for a Service, + this must match the 'name' field in the EndpointPort. Optional + if only one ServicePort is defined on this service. + type: string + nodePort: + description: 'The port on each node on which this service is + exposed when type is NodePort or LoadBalancer. Usually assigned + by the system. If a value is specified, in-range, and not + in use it will be used, otherwise the operation will fail. If + not specified, a port will be allocated if this Service requires + one. If this field is specified when creating a Service which + does not need it, creation will fail. This field will be wiped + when updating a Service to no longer need it (e.g. changing + type from NodePort to ClusterIP). More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + default: TCP + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on the pods + targeted by the service. Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. If this is a string, + it will be looked up as a named port in the target Pod''s + container ports. If this is not specified, the value of the + ''port'' field is used (an identity map). This field is ignored + for services with clusterIP=None, and should be omitted or + set equal to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + replicas: + default: 1 + description: Replicas defines the number of LoadBalancers that should + run. + minimum: 0 + type: integer + selector: + description: This label selector matches the load balancer sets deriving + from the load balancer + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - infrastructure + - selector + type: object + status: + description: LoadBalancerStatus defines the observed state of LoadBalancer. + properties: + externalIP: + description: ExternalIP is the current externalIP (FIP or private). + If not defined, no ExternalIP is bound yet. + type: string + floatingID: + description: FloatingID is the current openstack ID from the FloatingIP. + type: string + floatingName: + description: FloatingName is the current openstack name from the FloatingIP. + type: string + lastOpenstackReconcile: + description: LastOpenstackReconcile contains the timestamp of the + last openstack reconciliation. + format: date-time + type: string + openstackReconcileHash: + description: OpenstackReconcileHash contains a hash of openstack related + settings to reset the LastOpenstackReconcile timer if needed. + type: string + portID: + description: PortID is the current openstack ID from the virtual Port. + type: string + portIP: + description: PortIP is the IP from the openstack virtual Port. + type: string + portName: + description: PortName is the current openstack name from the virtual + Port. + type: string + readyReplicas: + description: ReadyReplicas are the current running replicas. + type: integer + replicas: + description: Replicas displays the running lb replicas under this + deployment + type: integer + security_group_id: + description: 'Deprecated: use securityGroupID instead.' + type: string + security_group_name: + description: 'Deprecated: use securityGroupName instead.' + type: string + securityGroupID: + description: SecurityGroupID is the current security group ID mapped + to the port + type: string + securityGroupName: + description: SecurityGroupName is the current security group name + mapped to the port + type: string + serverGroupID: + description: ServerGroupID is the current sever group ID + type: string + serverGroupName: + description: ServerGroupName is the current sever group name + type: string + type: object + required: + - metadata + type: object + served: true + storage: true + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancersets.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancersets.yaml new file mode 100644 index 0000000000..6dd2ac392f --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancersets.yaml @@ -0,0 +1,306 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.3 + creationTimestamp: null + name: loadbalancersets.yawol.stackit.cloud +spec: + group: yawol.stackit.cloud + names: + kind: LoadBalancerSet + listKind: LoadBalancerSetList + plural: loadbalancersets + shortNames: + - lbs + singular: loadbalancerset + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.replicas + name: DESIRED + type: string + - jsonPath: .status.replicas + name: CURRENT + type: string + - jsonPath: .status.readyReplicas + name: READY + type: string + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: LoadBalancerSet is the Schema for the LoadBalancerSet's API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: LoadBalancerSetSpec defines the desired state of LoadBalancerSet. + properties: + replicas: + default: 1 + description: Replicas defines the number of LoadBalancer that should + run. Defaults to 1. + minimum: 0 + type: integer + selector: + description: Selector is a label query over pods that should match + the replica count. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + template: + description: Template defines a template for the LoadBalancerMachine. + This is used to instantiate LoadBalancerMachine. + properties: + labels: + additionalProperties: + type: string + description: Labels for the LoadBalancerMachine + type: object + spec: + description: Spec is the spec for the LoadBalancerMachine. + properties: + infrastructure: + description: Infrastructure defines parameters for the Infrastructure. + properties: + additionalNetworks: + description: AdditionalNetworks defines additional networks + that will be added to the LoadBalancerMachines. + items: + description: LoadBalancerAdditionalNetwork defines additional + networks for the LoadBalancer + properties: + networkID: + description: NetworkID defines an openstack ID for + the network. + type: string + required: + - networkID + type: object + type: array + authSecretRef: + description: AuthSecretRef defines a secretRef for the + openstack secret. + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + availabilityZone: + description: AvailabilityZone defines the openstack availability + zone for the LoadBalancer. + type: string + defaultNetwork: + description: DefaultNetwork defines the default/listener + network for the Loadbalancer. TODO Remove optional when + Deprecations are removed + properties: + floatingNetID: + description: FloatingNetID defines an openstack ID + for the floatingNet. + type: string + networkID: + description: NetworkID defines an openstack ID for + the network. + type: string + required: + - networkID + type: object + flavor: + description: Flavor defines openstack flavor for the LoadBalancer. + properties: + flavor_id: + description: 'Deprecated: use flavorID instead.' + type: string + flavor_name: + description: 'Deprecated: use flavorName instead.' + type: string + flavor_search: + description: 'Deprecated: use flavorSearch instead.' + type: string + flavorID: + description: FlavorID is the flavor ID used for requesting + virtual machines. + type: string + flavorName: + description: NOT IMPLEMENTED ONLY FlavorID is supported. + FlavorName is the name of the flavor used for requesting + virtual machines. FlavorName is only used if FlavorID + is not defined. + type: string + flavorSearch: + description: NOT IMPLEMENTED ONLY FlavorID is supported. + FlavorSearch is a search string to find the flavor + used for requesting virtual machines. Search will + be performed in metadata of the flavors. FlavorSearch + is only used if FlavorName and FlavorID are not + defined. + type: string + type: object + floatingNetID: + description: 'Deprecated: use defaultNetwork instead FloatingNetID + defines a openstack ID for the floatingNet.' + type: string + image: + description: Image defines openstack image for the LoadBalancer. + properties: + image_id: + description: 'Deprecated: use imageID instead.' + type: string + image_name: + description: 'Deprecated: use imageName instead.' + type: string + image_search: + description: 'Deprecated: use imageSearch instead.' + type: string + imageID: + description: ImageID is the image ID used for requesting + virtual machines. + type: string + imageName: + description: NOT IMPLEMENTED ONLY ImageID is supported. + ImageName is the name of the image used for requesting + virtual machines. ImageName is only used if ImageID + is not defined. + type: string + imageSearch: + description: NOT IMPLEMENTED ONLY ImageID is supported. + ImageSearch is a search string to find the image + used for requesting virtual machines. Search will + be performed in metadata of the images. ImageSearch + is only used if ImageName and ImageID are not defined. + type: string + type: object + networkID: + description: 'Deprecated: use defaultNetwork instead NetworkID + defines a openstack ID for the network.' + type: string + projectID: + description: ProjectID defines an openstack project ID + which will be used instead of the project from the secret + ref. If not set the project from the secret ref will + be used. + type: string + required: + - authSecretRef + - flavor + - image + type: object + loadBalancerRef: + description: LoadBalancerRef defines a reference to the LoadBalancer + Object. + properties: + name: + description: Name is unique within a namespace to reference + a LoadBalancer resource. + type: string + namespace: + description: Namespace defines the space within which + the LoadBalancer name must be unique. + type: string + required: + - name + - namespace + type: object + portID: + description: PortID defines the openstack ID of the port attached + to the FloatingIP. + type: string + serverGroupID: + description: ServerGroupID defines the openstack ID of the + openstack server group. + type: string + required: + - infrastructure + - loadBalancerRef + - portID + type: object + required: + - labels + - spec + type: object + required: + - selector + - template + type: object + status: + description: LoadBalancerSetStatus defines the observed state of LoadBalancerSet. + properties: + availableReplicas: + description: AvailableReplicas are the current running replicas. + type: integer + readyReplicas: + description: ReadyReplicas are the current ready replicas. + type: integer + replicas: + description: Replicas are the desired replicas. + type: integer + type: object + type: object + served: true + storage: true + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/_helpers.tpl b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/_helpers.tpl new file mode 100644 index 0000000000..f31adaf43f --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/_helpers.tpl @@ -0,0 +1,3 @@ +{{- define "deploymentversion" -}} +apps/v1 +{{- end -}} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/rbac-yawol-cloud-controller.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/rbac-yawol-cloud-controller.yaml new file mode 100644 index 0000000000..ed7cb38640 --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/rbac-yawol-cloud-controller.yaml @@ -0,0 +1,113 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: yawol-cloud-controller + namespace: {{ .Values.namespace }} +rules: + - apiGroups: ["yawol.stackit.cloud"] + resources: + - "loadbalancers" + - "loadbalancers/status" + verbs: ["*"] + - apiGroups: [""] + resources: + - "events" + verbs: + - create + - get + - list + - watch + - patch + - apiGroups: [""] + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: ["coordination.k8s.io"] + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: [""] + resources: + - configmaps/status + - services/status + verbs: + - get + - update + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: yawol-cloud-controller + namespace: {{ .Values.namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: yawol-cloud-controller +subjects: + - kind: ServiceAccount + name: yawol-cloud-controller + namespace: {{ .Values.namespace }} +{{- if .Values.yawolCloudController.clusterRoleEnabled }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: yawol-cloud-controller +rules: + - apiGroups: [""] + resources: + - "events" + verbs: + - create + - get + - list + - watch + - patch + - apiGroups: [""] + resources: + - services + - services/status + verbs: + - get + - list + - watch + - create + - update + - patch + - apiGroups: [""] + resources: + - nodes + - nodes/status + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: yawol-cloud-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: yawol-cloud-controller +subjects: + - kind: ServiceAccount + name: yawol-cloud-controller + namespace: {{ .Values.namespace }} +{{- end }} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/rbac-yawol-controller.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/rbac-yawol-controller.yaml new file mode 100644 index 0000000000..1e0cc08ec3 --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/rbac-yawol-controller.yaml @@ -0,0 +1,84 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: yawol-controller + namespace: {{ .Values.namespace }} +rules: + - apiGroups: ["yawol.stackit.cloud"] + resources: + - "loadbalancersets" + - "loadbalancersets/status" + - "loadbalancermachines" + - "loadbalancermachines/status" + - "loadbalancers" + - "loadbalancers/status" + verbs: ["*"] + - apiGroups: [""] + resources: + - "secrets" + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: [""] + resources: + - "serviceaccounts" + verbs: ["*"] + - apiGroups: ["rbac.authorization.k8s.io"] + resources: + - "roles" + - "rolebindings" + verbs: ["*"] + - apiGroups: [""] + resources: + - "events" + verbs: + - create + - patch + - apiGroups: [""] + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: ["coordination.k8s.io"] + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: [""] + resources: + - configmaps/status + verbs: + - get + - update + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: yawol-controller + namespace: {{ .Values.namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: yawol-controller +subjects: + - kind: ServiceAccount + name: yawol-controller + namespace: {{ .Values.namespace }} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/sa-yawol-cloud-controller.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/sa-yawol-cloud-controller.yaml new file mode 100644 index 0000000000..c396cb4a75 --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/sa-yawol-cloud-controller.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: yawol-cloud-controller + namespace: {{ .Values.namespace }} \ No newline at end of file diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/sa-yawol-controller.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/sa-yawol-controller.yaml new file mode 100644 index 0000000000..d6d77eb905 --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/sa-yawol-controller.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: yawol-controller + namespace: {{ .Values.namespace }} \ No newline at end of file diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/vpa.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/vpa.yaml new file mode 100644 index 0000000000..44455aacc0 --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/vpa.yaml @@ -0,0 +1,27 @@ +{{- if .Values.vpa.enabled }} +apiVersion: autoscaling.k8s.io/v1 +kind: VerticalPodAutoscaler +metadata: + name: yawol-cloud-controller-vpa + namespace: {{ .Values.namespace }} +spec: + targetRef: + apiVersion: {{ include "deploymentversion" . }} + kind: Deployment + name: yawol-cloud-controller + updatePolicy: + updateMode: {{ .Values.vpa.yawolCloudController.mode }} +--- +apiVersion: autoscaling.k8s.io/v1 +kind: VerticalPodAutoscaler +metadata: + name: yawol-controller-vpa + namespace: {{ .Values.namespace }} +spec: + targetRef: + apiVersion: {{ include "deploymentversion" . }} + kind: Deployment + name: yawol-controller + updatePolicy: + updateMode: {{ .Values.vpa.yawolController.mode }} +{{- end }} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-cloud-controller.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-cloud-controller.yaml new file mode 100644 index 0000000000..303a77ac89 --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-cloud-controller.yaml @@ -0,0 +1,96 @@ +{{- if .Values.yawolCloudController.enabled }} +apiVersion: {{ include "deploymentversion" . }} +kind: Deployment +metadata: + name: yawol-cloud-controller + namespace: {{ .Values.namespace }} + labels: + garden.sapcloud.io/role: controlplane + app: kubernetes + role: yawol-cloud-controller +spec: + revisionHistoryLimit: 0 + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: kubernetes + role: yawol-cloud-controller + template: + metadata: +{{- if .Values.podAnnotations }} + annotations: +{{ toYaml .Values.podAnnotations | indent 8 }} +{{- end }} + labels: + app: kubernetes + role: yawol-cloud-controller +{{- if .Values.podLabels }} +{{ toYaml .Values.podLabels | indent 8 }} +{{- end }} + spec: + serviceAccountName: yawol-cloud-controller + containers: + - name: yawol-cloud-controller + image: "{{ .Values.yawolCloudController.image.repository }}:{{ default .Chart.AppVersion .Values.yawolCloudController.image.tag }}" + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 + name: metrics + args: +{{- if .Values.yawolCloudController.additionalArguments }} +{{ toYaml .Values.yawolCloudController.additionalArguments | indent 8 }} +{{- end }} + - -leader-elect + {{- if .Values.yawolClassName }} + - -classname={{ .Values.yawolClassName }} + {{- end }} + env: + {{- if .Values.namespace }} + - name: CLUSTER_NAMESPACE + value: {{ .Values.namespace }} + {{- end }} + {{- if .Values.yawolOSSecretName }} + - name: SECRET_NAME + value: {{ .Values.yawolOSSecretName }} + {{- end }} + {{- if .Values.yawolFloatingID }} + - name: FLOATING_NET_ID + value: {{ .Values.yawolFloatingID }} + {{- end }} + {{- if .Values.yawolNetworkID }} + - name: NETWORK_ID + value: {{ .Values.yawolNetworkID }} + {{- end }} + {{- if .Values.yawolFlavorID }} + - name: FLAVOR_ID + value: {{ .Values.yawolFlavorID }} + {{- end }} + {{- if .Values.yawolImageID }} + - name: IMAGE_ID + value: {{ .Values.yawolImageID }} + {{- end }} + {{- if .Values.yawolAvailabilityZone }} + - name: AVAILABILITY_ZONE + value: {{ .Values.yawolAvailabilityZone }} + {{- end }} + {{- if .Values.resources.yawolCloudController }} + resources: +{{ toYaml .Values.resources.yawolCloudController | indent 10 }} + {{- end }} +{{- if .Values.yawolCloudController.additionalVolumeMounts }} + volumeMounts: +{{ toYaml .Values.yawolCloudController.additionalVolumeMounts | indent 8 }} +{{- end }} + securityContext: + runAsNonRoot: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + dnsPolicy: ClusterFirst + restartPolicy: Always +{{- if .Values.yawolCloudController.additionalVolumes }} + volumes: +{{ toYaml .Values.yawolCloudController.additionalVolumes | indent 6 }} +{{- end }} +{{- end }} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-controller.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-controller.yaml new file mode 100644 index 0000000000..55cfd36947 --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-controller.yaml @@ -0,0 +1,131 @@ +apiVersion: {{ include "deploymentversion" . }} +kind: Deployment +metadata: + name: yawol-controller + namespace: {{ .Values.namespace }} + labels: + app: kubernetes + role: yawol-controller +spec: + revisionHistoryLimit: 0 + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: kubernetes + role: yawol-controller + template: + metadata: +{{- if .Values.podAnnotations }} + annotations: +{{ toYaml .Values.podAnnotations | indent 8 }} +{{- end }} + labels: + app: kubernetes + role: yawol-controller +{{- if .Values.podLabels }} +{{ toYaml .Values.podLabels | indent 8 }} +{{- end }} + spec: + serviceAccountName: yawol-controller + containers: + - name: yawol-controller-loadbalancer + image: "{{ .Values.yawolController.image.repository }}:{{ default .Chart.AppVersion .Values.yawolController.image.tag }}" + imagePullPolicy: Always + ports: + - containerPort: 8080 + name: metrics + args: + - -leader-elect + - -enable-loadbalancer-controller + {{- if .Values.openstackTimeout }} + - -openstack-timeout={{ .Values.openstackTimeout }} + {{- end }} + {{- if .Values.yawolController.errorBackoffBaseDelay }} + - -error-backoff-base-delay={{ .Values.yawolController.errorBackoffBaseDelay }} + {{- end }} + {{- if .Values.yawolController.errorBackoffMaxDelay }} + - -error-backoff-max-delay={{ .Values.yawolController.errorBackoffMaxDelay }} + {{- end }} + env: + {{- if .Values.namespace }} + - name: CLUSTER_NAMESPACE + value: {{ .Values.namespace }} + {{- end }} + {{- if .Values.resources.yawolControllerLoadbalancer }} + resources: +{{ toYaml .Values.resources.yawolControllerLoadbalancer | indent 10 }} + {{- end }} + securityContext: + runAsNonRoot: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + - name: yawol-controller-loadbalancerset + image: "{{ .Values.yawolController.image.repository }}:{{ default .Chart.AppVersion .Values.yawolController.image.tag }}" + imagePullPolicy: Always + ports: + - containerPort: 8081 + name: metrics + args: + - -leader-elect + - -enable-loadbalancerset-controller + {{- if .Values.yawolController.errorBackoffBaseDelay }} + - -error-backoff-base-delay={{ .Values.yawolController.errorBackoffBaseDelay }} + {{- end }} + {{- if .Values.yawolController.errorBackoffMaxDelay }} + - -error-backoff-max-delay={{ .Values.yawolController.errorBackoffMaxDelay }} + {{- end }} + env: + {{- if .Values.namespace }} + - name: CLUSTER_NAMESPACE + value: {{ .Values.namespace }} + {{- end }} + {{- if .Values.resources.yawolControllerLoadbalancerset }} + resources: +{{ toYaml .Values.resources.yawolControllerLoadbalancerset | indent 10 }} + {{- end }} + securityContext: + runAsNonRoot: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + - name: yawol-controller-loadbalancermachine + image: "{{ .Values.yawolController.image.repository }}:{{ default .Chart.AppVersion .Values.yawolController.image.tag }}" + imagePullPolicy: Always + ports: + - containerPort: 8082 + name: metrics + args: + - -leader-elect + - -enable-loadbalancermachine-controller + {{- if .Values.openstackTimeout }} + - -openstack-timeout={{ .Values.openstackTimeout }} + {{- end }} + {{- if .Values.yawolController.errorBackoffBaseDelay }} + - -error-backoff-base-delay={{ .Values.yawolController.errorBackoffBaseDelay }} + {{- end }} + {{- if .Values.yawolController.errorBackoffMaxDelay }} + - -error-backoff-max-delay={{ .Values.yawolController.errorBackoffMaxDelay }} + {{- end }} + env: + {{- if .Values.namespace }} + - name: CLUSTER_NAMESPACE + value: {{ .Values.namespace }} + {{- end }} + {{- if .Values.yawolAPIHost }} + - name: API_ENDPOINT + value: {{ .Values.yawolAPIHost }} + {{- end }} + {{- if .Values.resources.yawolControllerLoadbalancermachine }} + resources: +{{ toYaml .Values.resources.yawolControllerLoadbalancermachine | indent 10 }} + {{- end }} + securityContext: + runAsNonRoot: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + restartPolicy: Always diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-gardener-monitoring.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-gardener-monitoring.yaml new file mode 100644 index 0000000000..47c5b58d15 --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-gardener-monitoring.yaml @@ -0,0 +1,140 @@ +{{- if .Values.yawolCloudController.gardenerMonitoringEnabled }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: yawol-cloud-controller-monitoring-config + namespace: {{ .Release.Namespace }} + labels: + extensions.gardener.cloud/configuration: monitoring +data: + scrape_config: | + - job_name: yawol-cloud-controller + scheme: http + honor_labels: false + kubernetes_sd_configs: + - role: endpoints + namespaces: + names: [{{ .Release.Namespace }}] + relabel_configs: + - source_labels: + - __meta_kubernetes_service_name + - __meta_kubernetes_endpoint_port_name + action: keep + regex: yawol-cloud-controller;metrics + # common metrics + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: [ __meta_kubernetes_pod_name ] + target_label: pod +--- +apiVersion: v1 +kind: Service +metadata: + name: yawol-cloud-controller + namespace: {{ .Release.Namespace }} + labels: + app: kubernetes + role: yawol-cloud-controller +spec: + type: ClusterIP + clusterIP: None + ports: + - name: metrics + port: 8080 + protocol: TCP + selector: + app: kubernetes + role: yawol-cloud-controller +{{- end }} +{{- if .Values.yawolController.gardenerMonitoringEnabled }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: yawol-controller-monitoring-config + namespace: {{ .Release.Namespace }} + labels: + extensions.gardener.cloud/configuration: monitoring +data: + scrape_config: | + - job_name: yawol-loadbalancer-controller-metrics + scheme: http + honor_labels: false + kubernetes_sd_configs: + - role: endpoints + namespaces: + names: [{{ .Release.Namespace }}] + relabel_configs: + - source_labels: + - __meta_kubernetes_service_name + - __meta_kubernetes_endpoint_port_name + action: keep + regex: yawol-controller;metrics-lb + # common metrics + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: [ __meta_kubernetes_pod_name ] + target_label: pod + - job_name: yawol-loadbalancerset-controller-metrics + scheme: http + honor_labels: false + kubernetes_sd_configs: + - role: endpoints + namespaces: + names: [{{ .Release.Namespace }}] + relabel_configs: + - source_labels: + - __meta_kubernetes_service_name + - __meta_kubernetes_endpoint_port_name + action: keep + regex: yawol-controller;metrics-lbs + # common metrics + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: [ __meta_kubernetes_pod_name ] + target_label: pod + - job_name: yawol-loadbalancermachine-controller-metrics + scheme: http + honor_labels: false + kubernetes_sd_configs: + - role: endpoints + namespaces: + names: [{{ .Release.Namespace }}] + relabel_configs: + - source_labels: + - __meta_kubernetes_service_name + - __meta_kubernetes_endpoint_port_name + action: keep + regex: yawol-controller;metrics-lbm + # common metrics + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: [ __meta_kubernetes_pod_name ] + target_label: pod +--- +apiVersion: v1 +kind: Service +metadata: + name: yawol-controller + namespace: {{ .Release.Namespace }} + labels: + app: kubernetes + role: yawol-controller +spec: + type: ClusterIP + clusterIP: None + ports: + - name: metrics-lb + port: 8080 + protocol: TCP + - name: metrics-lbs + port: 8081 + protocol: TCP + - name: metrics-lbm + port: 8082 + protocol: TCP + selector: + app: kubernetes + role: yawol-controller +{{- end }} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/values.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/values.yaml new file mode 100644 index 0000000000..f7f44f554d --- /dev/null +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/yawol-controller/values.yaml @@ -0,0 +1,100 @@ +replicas: 1 +podAnnotations: {} +podLabels: {} +featureGates: {} +proxy: {} +namespace: kube-system +vpa: + enabled: false + yawolCloudController: + mode: Auto + yawolController: + mode: Auto + +yawolCloudController: + enabled: true + gardenerMonitoringEnabled: false + clusterRoleEnabled: true + image: + repository: ghcr.io/stackitcloud/yawol/yawol-cloud-controller + # -- Allows you to override the yawol version in this chart. Use at your own risk. + tag: "" + +yawolController: + gardenerMonitoringEnabled: false + errorBackoffBaseDelay: 5ms + errorBackoffMaxDelay: 1000s + image: + repository: ghcr.io/stackitcloud/yawol/yawol-controller + # -- Allows you to override the yawol version in this chart. Use at your own risk. + tag: "" + +resources: + yawolCloudController: + requests: + cpu: 100m + memory: 64Mi + limits: + cpu: 500m + memory: 512Mi + yawolControllerLoadbalancer: + requests: + cpu: 100m + memory: 64Mi + limits: + cpu: 500m + memory: 512Mi + yawolControllerLoadbalancerset: + requests: + cpu: 100m + memory: 64Mi + limits: + cpu: 500m + memory: 512Mi + yawolControllerLoadbalancermachine: + requests: + cpu: 100m + memory: 64Mi + limits: + cpu: 500m + memory: 512Mi + +#yawolClassName: debug +#openstackTimeout: 20s + +# the name of the Kubernetes secret that contains the .openrc file contents +# with the correct permissions to connect to the OpenStack API +# +# Placed in LoadBalancer.spec.infrastructure.authSecretRef.name +yawolOSSecretName: + +# floating IP ID of the IP pool that yawol uses to request IPs +# +# Placed in LoadBalancer.spec.infrastructure.floatingNetID +yawolFloatingID: + +# OpenStack network ID in which the Load Balancer is placed +# +# Placed in LoadBalancer.spec.infrastructure.networkID +yawolNetworkID: + +# default value for flavor that yawol Load Balancer instances should use +# can be overridden by annotation +# +# Placed in LoadBalancer.spec.infrastructure.flavor.flavor_id +yawolFlavorID: + +# default value for ID of the image used for the Load Balancer instance +# can be overridden by annotation +# +# Placed in LoadBalancer.spec.infrastructure.image.image_id +yawolImageID: + +# default value for the AZ used for the Load Balancer instance +# can be overridden by annotation. If not set, empty string is used. +# +# Placed in LoadBalancer.spec.infrastructure.availabilityZone +yawolAvailabilityZone: "" + +# URL/IP of the Kubernetes API server that contains the LoadBalancer resources +yawolAPIHost: From cd106a21ba1113c3d1c03d1672859b1aa2835bab Mon Sep 17 00:00:00 2001 From: Malte Poll Date: Tue, 2 May 2023 09:35:52 +0200 Subject: [PATCH 2/2] cli: deploy yawol as OpenStack loadbalancer --- .../internal/kubernetes/kubernetes.go | 35 +++++- cli/internal/cmd/init.go | 17 +-- cli/internal/helm/BUILD.bazel | 20 +++ .../constellation-services/Chart.yaml | 10 ++ .../ccm/templates/openstack-daemonset.yaml | 2 +- .../ccm/templates/openstack-secret.yaml | 2 +- .../constellation-services/values.yaml | 4 + cli/internal/helm/loader.go | 26 +++- .../ccm/templates/openstack-daemonset.yaml | 2 +- .../ccm/templates/openstack-secret.yaml | 2 +- internal/cloud/openstack/accountkey.go | 30 ++++- internal/cloud/openstack/accountkey_test.go | 29 ++++- internal/cloud/openstack/api.go | 1 + internal/cloud/openstack/api_test.go | 6 + internal/cloud/openstack/imds.go | 51 ++++++-- internal/cloud/openstack/imds_test.go | 118 ++++++++++++++++++ internal/cloud/openstack/openstack.go | 10 ++ internal/cloud/openstack/openstack_test.go | 40 ++++++ internal/config/config.go | 17 ++- internal/config/config_doc.go | 17 ++- 20 files changed, 398 insertions(+), 41 deletions(-) diff --git a/bootstrapper/internal/kubernetes/kubernetes.go b/bootstrapper/internal/kubernetes/kubernetes.go index e5830497d6..aa1494755a 100644 --- a/bootstrapper/internal/kubernetes/kubernetes.go +++ b/bootstrapper/internal/kubernetes/kubernetes.go @@ -18,6 +18,11 @@ import ( "strings" "time" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" + "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/kubewaiter" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" @@ -29,10 +34,6 @@ import ( "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/role" "github.com/edgelesssys/constellation/v2/internal/versions/components" - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" ) var validHostnameRegex = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`) @@ -481,13 +482,31 @@ func (k *KubeWrapper) setupExtraVals(ctx context.Context, serviceConfig constell if err != nil { return nil, err } - credsIni := creds.CloudINI().String() + credsIni := creds.CloudINI().FullConfiguration() + networkIDsGetter, ok := k.providerMetadata.(openstackMetadata) + if !ok { + return nil, errors.New("generating yawol configuration: cloud provider metadata does not implement OpenStack specific methods") + } + networkIDs, err := networkIDsGetter.GetNetworkIDs(ctx) + if err != nil { + return nil, fmt.Errorf("getting network IDs: %w", err) + } + if len(networkIDs) == 0 { + return nil, errors.New("getting network IDs: no network IDs found") + } extraVals["ccm"] = map[string]any{ "OpenStack": map[string]any{ "secretData": credsIni, }, } - + yawolIni := creds.CloudINI().YawolConfiguration() + extraVals["yawol-config"] = map[string]any{ + "secretData": yawolIni, + } + extraVals["yawol-controller"] = map[string]any{ + "yawolNetworkID": networkIDs[0], + "yawolAPIHost": fmt.Sprintf("https://%s:%d", serviceConfig.loadBalancerIP, constants.KubernetesPort), + } } return extraVals, nil } @@ -515,3 +534,7 @@ type constellationServicesConfig struct { cloudServiceAccountURI string loadBalancerIP string } + +type openstackMetadata interface { + GetNetworkIDs(ctx context.Context) ([]string, error) +} diff --git a/cli/internal/cmd/init.go b/cli/internal/cmd/init.go index 487af62bbb..4c565cc22c 100644 --- a/cli/internal/cmd/init.go +++ b/cli/internal/cmd/init.go @@ -22,6 +22,15 @@ import ( "github.com/edgelesssys/constellation/v2/internal/atls" "github.com/edgelesssys/constellation/v2/internal/compatibility" + "github.com/spf13/afero" + "github.com/spf13/cobra" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/clientcmd" + clientcodec "k8s.io/client-go/tools/clientcmd/api/latest" + "sigs.k8s.io/yaml" + "github.com/edgelesssys/constellation/v2/bootstrapper/initproto" "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" "github.com/edgelesssys/constellation/v2/cli/internal/clusterid" @@ -40,14 +49,6 @@ import ( "github.com/edgelesssys/constellation/v2/internal/license" "github.com/edgelesssys/constellation/v2/internal/retry" "github.com/edgelesssys/constellation/v2/internal/versions" - "github.com/spf13/afero" - "github.com/spf13/cobra" - "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/tools/clientcmd" - clientcodec "k8s.io/client-go/tools/clientcmd/api/latest" - "sigs.k8s.io/yaml" ) // NewInitCmd returns a new cobra.Command for the init command. diff --git a/cli/internal/helm/BUILD.bazel b/cli/internal/helm/BUILD.bazel index 54c3f5086a..7c6f7c2aaf 100644 --- a/cli/internal/helm/BUILD.bazel +++ b/cli/internal/helm/BUILD.bazel @@ -314,6 +314,26 @@ go_library( "charts/edgeless/operators/values.yaml", "charts/edgeless/constellation-services/charts/ccm/templates/openstack-daemonset.yaml", "charts/edgeless/constellation-services/charts/ccm/templates/openstack-secret.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/Chart.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/README.md", + "charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancermachines.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancers.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/crds/yawol.stackit.cloud_loadbalancersets.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/templates/_helpers.tpl", + "charts/edgeless/constellation-services/charts/yawol-controller/templates/rbac-yawol-cloud-controller.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/templates/rbac-yawol-controller.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/templates/sa-yawol-cloud-controller.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/templates/sa-yawol-controller.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/templates/vpa.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-cloud-controller.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-controller.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/templates/yawol-gardener-monitoring.yaml", + "charts/edgeless/constellation-services/charts/yawol-controller/values.yaml", + "charts/edgeless/constellation-services/charts/yawol-config/.helmignore", + "charts/edgeless/constellation-services/charts/yawol-config/Chart.yaml", + "charts/edgeless/constellation-services/charts/yawol-config/templates/secret.yaml", + "charts/edgeless/constellation-services/charts/yawol-config/values.schema.json", + "charts/edgeless/constellation-services/charts/yawol-config/values.yaml", ], importpath = "github.com/edgelesssys/constellation/v2/cli/internal/helm", visibility = ["//cli:__subpackages__"], diff --git a/cli/internal/helm/charts/edgeless/constellation-services/Chart.yaml b/cli/internal/helm/charts/edgeless/constellation-services/Chart.yaml index 82fb9d4417..b2c5fff184 100644 --- a/cli/internal/helm/charts/edgeless/constellation-services/Chart.yaml +++ b/cli/internal/helm/charts/edgeless/constellation-services/Chart.yaml @@ -67,3 +67,13 @@ dependencies: condition: azure.deployCSIDriver tags: - Azure + - name: yawol-config + version: 0.0.0 + condition: openstack.deployYawolLoadBalancer + tags: + - OpenStack + - name: yawol-controller + version: 0.0.0 + condition: openstack.deployYawolLoadBalancer + tags: + - OpenStack diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/ccm/templates/openstack-daemonset.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/ccm/templates/openstack-daemonset.yaml index 4b32a4bc94..740d91f8ac 100644 --- a/cli/internal/helm/charts/edgeless/constellation-services/charts/ccm/templates/openstack-daemonset.yaml +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/ccm/templates/openstack-daemonset.yaml @@ -21,7 +21,7 @@ spec: args: - /bin/openstack-cloud-controller-manager - --cloud-provider=openstack - - --cloud-config=/etc/config/cloud.conf + - --cloud-config=/etc/config/cloudprovider.conf - --leader-elect=true - --allocate-node-cidrs=false - -v=2 diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/ccm/templates/openstack-secret.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/ccm/templates/openstack-secret.yaml index fb93b8bfac..66bebb0c33 100644 --- a/cli/internal/helm/charts/edgeless/constellation-services/charts/ccm/templates/openstack-secret.yaml +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/ccm/templates/openstack-secret.yaml @@ -5,5 +5,5 @@ metadata: name: openstackkey namespace: {{ .Release.Namespace }} data: - cloud.conf: {{ .Values.OpenStack.secretData | b64enc }} + cloudprovider.conf: {{ .Values.OpenStack.secretData | b64enc }} {{- end -}} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/values.yaml b/cli/internal/helm/charts/edgeless/constellation-services/values.yaml index 26bdef388b..90aac76fba 100644 --- a/cli/internal/helm/charts/edgeless/constellation-services/values.yaml +++ b/cli/internal/helm/charts/edgeless/constellation-services/values.yaml @@ -16,6 +16,10 @@ gcp: azure: deployCSIDriver: false +# OpenStack specific configuration +openstack: + deployYawolLoadBalancer: false + # Set one of the tags to true to indicate which CSP you are deploying to. tags: AWS: false diff --git a/cli/internal/helm/loader.go b/cli/internal/helm/loader.go index 519fc355b9..1c1455ff51 100644 --- a/cli/internal/helm/loader.go +++ b/cli/internal/helm/loader.go @@ -17,6 +17,12 @@ import ( "path/filepath" "strings" + "github.com/pkg/errors" + "helm.sh/helm/pkg/ignore" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/chartutil" + "github.com/edgelesssys/constellation/v2/cli/internal/helm/imageversion" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/compatibility" @@ -24,11 +30,6 @@ import ( "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/deploy/helm" "github.com/edgelesssys/constellation/v2/internal/versions" - "github.com/pkg/errors" - "helm.sh/helm/pkg/ignore" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" ) // Run `go generate` to download (and patch) upstream helm charts. @@ -507,6 +508,21 @@ func extendConstellationServicesValues( in["gcp"] = map[string]any{ "deployCSIDriver": cfg.DeployCSIDriver(), } + + case cloudprovider.OpenStack: + in["openstack"] = map[string]any{ + "deployYawolLoadBalancer": cfg.DeployYawolLoadBalancer(), + } + if cfg.DeployYawolLoadBalancer() { + in["yawol-controller"] = map[string]any{ + "yawolOSSecretName": "yawolkey", + // has to be larger than ~30s to account for slow OpenStack API calls. + "openstackTimeout": "1m", + "yawolFloatingID": cfg.Provider.OpenStack.FloatingIPPoolID, + "yawolFlavorID": cfg.Provider.OpenStack.YawolFlavorID, + "yawolImageID": cfg.Provider.OpenStack.YawolImageID, + } + } } return nil diff --git a/cli/internal/helm/testdata/OpenStack/constellation-services/charts/ccm/templates/openstack-daemonset.yaml b/cli/internal/helm/testdata/OpenStack/constellation-services/charts/ccm/templates/openstack-daemonset.yaml index 2beeb4871a..a8e823826a 100644 --- a/cli/internal/helm/testdata/OpenStack/constellation-services/charts/ccm/templates/openstack-daemonset.yaml +++ b/cli/internal/helm/testdata/OpenStack/constellation-services/charts/ccm/templates/openstack-daemonset.yaml @@ -20,7 +20,7 @@ spec: args: - /bin/openstack-cloud-controller-manager - --cloud-provider=openstack - - --cloud-config=/etc/config/cloud.conf + - --cloud-config=/etc/config/cloudprovider.conf - --leader-elect=true - --allocate-node-cidrs=false - -v=2 diff --git a/cli/internal/helm/testdata/OpenStack/constellation-services/charts/ccm/templates/openstack-secret.yaml b/cli/internal/helm/testdata/OpenStack/constellation-services/charts/ccm/templates/openstack-secret.yaml index f0061a2ef8..f870605159 100644 --- a/cli/internal/helm/testdata/OpenStack/constellation-services/charts/ccm/templates/openstack-secret.yaml +++ b/cli/internal/helm/testdata/OpenStack/constellation-services/charts/ccm/templates/openstack-secret.yaml @@ -4,4 +4,4 @@ metadata: name: openstackkey namespace: testNamespace data: - cloud.conf: YmFhYWFhYWQ= + cloudprovider.conf: YmFhYWFhYWQ= diff --git a/internal/cloud/openstack/accountkey.go b/internal/cloud/openstack/accountkey.go index 5e52f2584d..277f0cc1a7 100644 --- a/internal/cloud/openstack/accountkey.go +++ b/internal/cloud/openstack/accountkey.go @@ -98,7 +98,7 @@ func (k AccountKey) CloudINI() CloudINI { AuthURL: k.AuthURL, Username: k.Username, Password: k.Password, - TenantID: k.ProjectID, + ProjectID: k.ProjectID, TenantName: k.ProjectName, UserDomainName: k.UserDomainName, TenantDomainName: k.ProjectDomainName, @@ -111,20 +111,20 @@ type CloudINI struct { AuthURL string `gcfg:"auth-url" mapstructure:"auth-url" name:"os-authURL" dependsOn:"os-password|os-trustID|os-applicationCredentialSecret|os-clientCertPath"` Username string `name:"os-userName" value:"optional" dependsOn:"os-password"` Password string `name:"os-password" value:"optional" dependsOn:"os-domainID|os-domainName,os-projectID|os-projectName,os-userID|os-userName"` - TenantID string `gcfg:"tenant-id" mapstructure:"project-id" name:"os-projectID" value:"optional" dependsOn:"os-password|os-clientCertPath"` + ProjectID string `gcfg:"project-id" mapstructure:"project-id" name:"os-projectID" value:"optional" dependsOn:"os-password|os-clientCertPath"` TenantName string `gcfg:"tenant-name" mapstructure:"project-name" name:"os-projectName" value:"optional" dependsOn:"os-password|os-clientCertPath"` UserDomainName string `gcfg:"user-domain-name" mapstructure:"user-domain-name" name:"os-userDomainName" value:"optional"` TenantDomainName string `gcfg:"tenant-domain-name" mapstructure:"project-domain-name" name:"os-projectDomainName" value:"optional"` Region string `name:"os-region"` } -// String returns the string representation of the CloudINI. -func (i CloudINI) String() string { +// FullConfiguration returns the string representation of the full CloudINI. +func (i CloudINI) FullConfiguration() string { // sanitize parameters to not include newlines authURL := newlineRegexp.ReplaceAllString(i.AuthURL, "") username := newlineRegexp.ReplaceAllString(i.Username, "") password := newlineRegexp.ReplaceAllString(i.Password, "") - tenantID := newlineRegexp.ReplaceAllString(i.TenantID, "") + tenantID := newlineRegexp.ReplaceAllString(i.ProjectID, "") tenantName := newlineRegexp.ReplaceAllString(i.TenantName, "") userDomainName := newlineRegexp.ReplaceAllString(i.UserDomainName, "") tenantDomainName := newlineRegexp.ReplaceAllString(i.TenantDomainName, "") @@ -142,4 +142,24 @@ region = %s `, authURL, username, password, tenantID, tenantName, userDomainName, tenantDomainName, region) } +// YawolConfiguration returns the string representation of the CloudINI subset yawol expects. +func (i CloudINI) YawolConfiguration() string { + // sanitize parameters to not include newlines + authURL := newlineRegexp.ReplaceAllString(i.AuthURL, "") + username := newlineRegexp.ReplaceAllString(i.Username, "") + password := newlineRegexp.ReplaceAllString(i.Password, "") + projectID := newlineRegexp.ReplaceAllString(i.ProjectID, "") + userDomainName := newlineRegexp.ReplaceAllString(i.UserDomainName, "") + region := newlineRegexp.ReplaceAllString(i.Region, "") + + return fmt.Sprintf(`[Global] +auth-url = %s +username = %s +password = %s +project-id = %s +domain-name = %s +region = %s +`, authURL, username, password, projectID, userDomainName, region) +} + var newlineRegexp = regexp.MustCompile(`[\r\n]+`) diff --git a/internal/cloud/openstack/accountkey_test.go b/internal/cloud/openstack/accountkey_test.go index bdf5e51de1..b46ce14a5f 100644 --- a/internal/cloud/openstack/accountkey_test.go +++ b/internal/cloud/openstack/accountkey_test.go @@ -143,7 +143,7 @@ func TestAccountKeyToCloudINI(t *testing.T) { AuthURL: "auth-url", Username: "username", Password: "password", - TenantID: "project-id", + ProjectID: "project-id", TenantName: "project-name", UserDomainName: "user-domain-name", TenantDomainName: "project-domain-name", @@ -151,12 +151,12 @@ func TestAccountKeyToCloudINI(t *testing.T) { }, ini) } -func TestCloudINIToString(t *testing.T) { +func TestFullConfiguration(t *testing.T) { ini := CloudINI{ AuthURL: "auth-url", Username: "username", Password: "password", - TenantID: "project-id", + ProjectID: "project-id", TenantName: "project-name", UserDomainName: "user-domain-name", TenantDomainName: "project-domain-name", @@ -171,5 +171,26 @@ tenant-name = project-name user-domain-name = user-domain-name tenant-domain-name = project-domain-name region = region-name -`, ini.String()) +`, ini.FullConfiguration()) +} + +func TestYawolConfiguration(t *testing.T) { + ini := CloudINI{ + AuthURL: "auth-url", + Username: "username", + Password: "password", + ProjectID: "project-id", + TenantName: "project-name", + UserDomainName: "user-domain-name", + TenantDomainName: "project-domain-name", + Region: "region-name", + } + assert.Equal(t, `[Global] +auth-url = auth-url +username = username +password = password +project-id = project-id +domain-name = user-domain-name +region = region-name +`, ini.YawolConfiguration()) } diff --git a/internal/cloud/openstack/api.go b/internal/cloud/openstack/api.go index 62a73a1229..e66c314985 100644 --- a/internal/cloud/openstack/api.go +++ b/internal/cloud/openstack/api.go @@ -23,6 +23,7 @@ type imdsAPI interface { initSecretHash(ctx context.Context) (string, error) role(ctx context.Context) (role.Role, error) vpcIP(ctx context.Context) (string, error) + networkIDs(ctx context.Context) ([]string, error) } type serversAPI interface { diff --git a/internal/cloud/openstack/api_test.go b/internal/cloud/openstack/api_test.go index 11d1c1fcf5..b6f623296d 100644 --- a/internal/cloud/openstack/api_test.go +++ b/internal/cloud/openstack/api_test.go @@ -30,6 +30,8 @@ type stubIMDSClient struct { roleErr error vpcIPResult string vpcIPErr error + networkIDsResult []string + networkIDsErr error } func (c *stubIMDSClient) providerID(_ context.Context) (string, error) { @@ -60,6 +62,10 @@ func (c *stubIMDSClient) vpcIP(_ context.Context) (string, error) { return c.vpcIPResult, c.vpcIPErr } +func (c *stubIMDSClient) networkIDs(_ context.Context) ([]string, error) { + return c.networkIDsResult, c.networkIDsErr +} + type stubServersClient struct { serversPager stubPager subnetsPager stubPager diff --git a/internal/cloud/openstack/imds.go b/internal/cloud/openstack/imds.go index b4cabe5d67..01e31268b7 100644 --- a/internal/cloud/openstack/imds.go +++ b/internal/cloud/openstack/imds.go @@ -22,18 +22,21 @@ import ( // documentation of OpenStack Metadata Service: https://docs.openstack.org/nova/rocky/user/metadata-service.html const ( - imdsMetaDataURL = "http://169.254.169.254/openstack/2018-08-27/meta_data.json" - ec2ImdsBaseURL = "http://169.254.169.254/1.0/meta-data" - maxCacheAge = 12 * time.Hour + imdsMetaDataURL = "http://169.254.169.254/openstack/2018-08-27/meta_data.json" + imdsNetworkDataURL = "http://169.254.169.254/openstack/2018-08-27/network_data.json" + ec2ImdsBaseURL = "http://169.254.169.254/1.0/meta-data" + maxCacheAge = 12 * time.Hour ) type imdsClient struct { client httpClient - vpcIPCache string - vpcIPCacheTime time.Time - cache metadataResponse - cacheTime time.Time + vpcIPCache string + vpcIPCacheTime time.Time + networkCache networkResponse + networkCacheTime time.Time + cache metadataResponse + cacheTime time.Time } // providerID returns the provider ID of the instance the function is called from. @@ -223,6 +226,30 @@ func (c *imdsClient) vpcIP(ctx context.Context) (string, error) { return c.vpcIPCache, nil } +func (c *imdsClient) networkIDs(ctx context.Context) ([]string, error) { + if c.timeForUpdate(c.networkCacheTime) || len(c.networkCache.Networks) == 0 { + resp, err := httpGet(ctx, c.client, imdsNetworkDataURL) + if err != nil { + return nil, err + } + var networkResp networkResponse + if err := json.Unmarshal(resp, &networkResp); err != nil { + return nil, err + } + c.networkCache = networkResp + c.networkCacheTime = time.Now() + } + + var networkIDs []string + for _, network := range c.networkCache.Networks { + if network.NetworkID == "" { + continue + } + networkIDs = append(networkIDs, network.NetworkID) + } + return networkIDs, nil +} + func httpGet(ctx context.Context, c httpClient, url string) ([]byte, error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody) if err != nil { @@ -254,6 +281,16 @@ type metadataTags struct { Password string `json:"openstack-password,omitempty"` } +// networkResponse contains networkResponse with only the required values. +type networkResponse struct { + Networks []metadataNetwork `json:"networks,omitempty"` +} + +type metadataNetwork struct { + ID string `json:"id,omitempty"` + NetworkID string `json:"network_id,omitempty"` +} + type httpClient interface { Do(req *http.Request) (*http.Response, error) } diff --git a/internal/cloud/openstack/imds_test.go b/internal/cloud/openstack/imds_test.go index 57430bb8c8..5d5e0d25ac 100644 --- a/internal/cloud/openstack/imds_test.go +++ b/internal/cloud/openstack/imds_test.go @@ -338,6 +338,124 @@ func TestVPCIP(t *testing.T) { } } +func TestNetworkIDs(t *testing.T) { + someErr := errors.New("failed") + + testCases := map[string]struct { + cache networkResponse + cacheTime time.Time + client *stubHTTPClient + wantResult []string + wantCall bool + wantErr bool + }{ + "cached": { + cache: networkResponse{Networks: []metadataNetwork{ + {ID: "net0", NetworkID: "0000000-00000-0000-0000-000000000000"}, + {ID: "net1", NetworkID: "1111111-11111-1111-1111-111111111111"}, + {ID: "invalid"}, + }}, + cacheTime: time.Now(), + wantResult: []string{ + "0000000-00000-0000-0000-000000000000", + "1111111-11111-1111-1111-111111111111", + }, + wantCall: false, + }, + "from http": { + client: &stubHTTPClient{ + response: ` + { + "networks": [ + { + "id": "net0", + "network_id": "0000000-00000-0000-0000-000000000000" + }, + { + "id": "net1", + "network_id": "1111111-11111-1111-1111-111111111111" + } + ] + }`, + }, + wantResult: []string{ + "0000000-00000-0000-0000-000000000000", + "1111111-11111-1111-1111-111111111111", + }, + wantCall: true, + }, + "cache outdated": { + cache: networkResponse{Networks: []metadataNetwork{ + {ID: "net0", NetworkID: "0000000-00000-0000-0000-000000000000"}, + }}, + cacheTime: time.Now().AddDate(0, 0, -1), + client: &stubHTTPClient{ + response: ` + { + "networks": [ + { + "id": "net1", + "network_id": "1111111-11111-1111-1111-111111111111" + } + ] + }`, + }, + wantResult: []string{"1111111-11111-1111-1111-111111111111"}, + wantCall: true, + }, + "cache empty": { + cacheTime: time.Now(), + client: &stubHTTPClient{ + response: ` + { + "networks": [ + { + "id": "net0", + "network_id": "0000000-00000-0000-0000-000000000000" + } + ] + }`, + }, + wantResult: []string{"0000000-00000-0000-0000-000000000000"}, + wantCall: true, + }, + "http error": { + client: &stubHTTPClient{err: someErr}, + wantCall: true, + wantErr: true, + }, + "http empty response": { + client: &stubHTTPClient{}, + wantCall: true, + wantErr: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + + imds := &imdsClient{ + client: tc.client, + networkCache: tc.cache, + networkCacheTime: tc.cacheTime, + } + + result, err := imds.networkIDs(context.Background()) + + if tc.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(tc.wantResult, result) + if tc.client != nil { + assert.Equal(tc.wantCall, tc.client.called) + } + } + }) + } +} + func TestTimeForUpdate(t *testing.T) { testCases := map[string]struct { cacheTime time.Time diff --git a/internal/cloud/openstack/openstack.go b/internal/cloud/openstack/openstack.go index 700d6fe6b5..a996cb7b49 100644 --- a/internal/cloud/openstack/openstack.go +++ b/internal/cloud/openstack/openstack.go @@ -297,6 +297,16 @@ func (c *Cloud) GetLoadBalancerEndpoint(ctx context.Context) (string, error) { return "", errors.New("no load balancer endpoint found") } +// GetNetworkIDs returns the IDs of the networks the current instance is part of. +// This method is OpenStack specific. +func (c *Cloud) GetNetworkIDs(ctx context.Context) ([]string, error) { + networkIDs, err := c.imds.networkIDs(ctx) + if err != nil { + return nil, fmt.Errorf("retrieving network IDs: %w", err) + } + return networkIDs, nil +} + func (c *Cloud) getSubnetCIDR(uidTag string) (netip.Prefix, error) { listSubnetsOpts := subnets.ListOpts{Tags: uidTag} subnetsPage, err := c.api.ListSubnets(listSubnetsOpts).AllPages() diff --git a/internal/cloud/openstack/openstack_test.go b/internal/cloud/openstack/openstack_test.go index 46e12a8822..a0478d8293 100644 --- a/internal/cloud/openstack/openstack_test.go +++ b/internal/cloud/openstack/openstack_test.go @@ -653,6 +653,46 @@ func TestGetLoadBalancerEndpoint(t *testing.T) { } } +func TestGetNetworkIDs(t *testing.T) { + someErr := fmt.Errorf("failed") + + testCases := map[string]struct { + imds imdsAPI + want []string + wantErr bool + }{ + "success": { + imds: &stubIMDSClient{ + networkIDsResult: []string{"id1", "id2"}, + }, + want: []string{"id1", "id2"}, + }, + "fail to get network IDs": { + imds: &stubIMDSClient{ + networkIDsErr: someErr, + }, + wantErr: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + + c := &Cloud{imds: tc.imds} + + got, err := c.GetNetworkIDs(context.Background()) + + if tc.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(tc.want, got) + } + }) + } +} + // newSubnetPager returns a subnet pager as we would get from a ListSubnets. func newSubnetPager(nets []subnets.Subnet, err error) stubPager { return stubPager{ diff --git a/internal/config/config.go b/internal/config/config.go index 79aa32c65c..e1875bd6b6 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -230,6 +230,15 @@ type OpenStackConfig struct { // description: | // If enabled, downloads OS image directly from source URL to OpenStack. Otherwise, downloads image to local machine and uploads to OpenStack. DirectDownload *bool `yaml:"directDownload" validate:"required"` + // description: | + // Deploy Yawol loadbalancer. For details see: https://github.com/stackitcloud/yawol + DeployYawolLoadBalancer *bool `yaml:"deployYawolLoadBalancer" validate:"required"` + // description: | + // OpenStack OS image used by the yawollet. For details see: https://github.com/stackitcloud/yawol + YawolImageID string `yaml:"yawolImageID"` + // description: | + // OpenStack flavor id used for yawollets. For details see: https://github.com/stackitcloud/yawol + YawolFlavorID string `yaml:"yawolFlavorID"` } // QEMUConfig holds config information for QEMU based Constellation deployments. @@ -321,7 +330,8 @@ func Default() *Config { DeployCSIDriver: toPtr(true), }, OpenStack: &OpenStackConfig{ - DirectDownload: toPtr(true), + DirectDownload: toPtr(true), + DeployYawolLoadBalancer: toPtr(true), }, QEMU: &QEMUConfig{ ImageFormat: "raw", @@ -523,6 +533,11 @@ func (c *Config) DeployCSIDriver() bool { c.Provider.GCP != nil && c.Provider.GCP.DeployCSIDriver != nil && *c.Provider.GCP.DeployCSIDriver } +// DeployYawolLoadBalancer returns whether the Yawol load balancer should be deployed. +func (c *Config) DeployYawolLoadBalancer() bool { + return c.Provider.OpenStack != nil && c.Provider.OpenStack.DeployYawolLoadBalancer != nil && *c.Provider.OpenStack.DeployYawolLoadBalancer +} + // Validate checks the config values and returns validation errors. func (c *Config) Validate(force bool) error { trans := ut.New(en.New()).GetFallback() diff --git a/internal/config/config_doc.go b/internal/config/config_doc.go index 8581a23a98..c94ed39f39 100644 --- a/internal/config/config_doc.go +++ b/internal/config/config_doc.go @@ -276,7 +276,7 @@ func init() { FieldName: "openstack", }, } - OpenStackConfigDoc.Fields = make([]encoder.Doc, 14) + OpenStackConfigDoc.Fields = make([]encoder.Doc, 17) OpenStackConfigDoc.Fields[0].Name = "cloud" OpenStackConfigDoc.Fields[0].Type = "string" OpenStackConfigDoc.Fields[0].Note = "" @@ -347,6 +347,21 @@ func init() { OpenStackConfigDoc.Fields[13].Note = "" OpenStackConfigDoc.Fields[13].Description = "If enabled, downloads OS image directly from source URL to OpenStack. Otherwise, downloads image to local machine and uploads to OpenStack." OpenStackConfigDoc.Fields[13].Comments[encoder.LineComment] = "If enabled, downloads OS image directly from source URL to OpenStack. Otherwise, downloads image to local machine and uploads to OpenStack." + OpenStackConfigDoc.Fields[14].Name = "deployYawolLoadBalancer" + OpenStackConfigDoc.Fields[14].Type = "bool" + OpenStackConfigDoc.Fields[14].Note = "" + OpenStackConfigDoc.Fields[14].Description = "Deploy Yawol loadbalancer. For details see: https://github.com/stackitcloud/yawol" + OpenStackConfigDoc.Fields[14].Comments[encoder.LineComment] = "Deploy Yawol loadbalancer. For details see: https://github.com/stackitcloud/yawol" + OpenStackConfigDoc.Fields[15].Name = "yawolImageID" + OpenStackConfigDoc.Fields[15].Type = "string" + OpenStackConfigDoc.Fields[15].Note = "" + OpenStackConfigDoc.Fields[15].Description = "OpenStack OS image used by the yawollet. For details see: https://github.com/stackitcloud/yawol" + OpenStackConfigDoc.Fields[15].Comments[encoder.LineComment] = "OpenStack OS image used by the yawollet. For details see: https://github.com/stackitcloud/yawol" + OpenStackConfigDoc.Fields[16].Name = "yawolFlavorID" + OpenStackConfigDoc.Fields[16].Type = "string" + OpenStackConfigDoc.Fields[16].Note = "" + OpenStackConfigDoc.Fields[16].Description = "OpenStack flavor id used for yawollets. For details see: https://github.com/stackitcloud/yawol" + OpenStackConfigDoc.Fields[16].Comments[encoder.LineComment] = "OpenStack flavor id used for yawollets. For details see: https://github.com/stackitcloud/yawol" QEMUConfigDoc.Type = "QEMUConfig" QEMUConfigDoc.Comments[encoder.LineComment] = "QEMUConfig holds config information for QEMU based Constellation deployments."