Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migration to Gardener Operator #360

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 168 additions & 0 deletions control-plane/roles/gardener-operator/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
gardener_image_vector_overwrite:
gardener_component_image_vector_overwrite:

gardener_operator_enabled: false

gardener_apiserver_replicas: 1
gardener_apiserver_vpa: true
gardener_apiserver_feature_gates: {}
gardener_apiserver_shoot_kubeconfig_max_expiration: "8760h"

gardener_apiserver_resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 400m
memory: 1Gi

gardener_controller_manager_resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 750m
memory: 512Mi

gardener_scheduler_resources:
requests:
cpu: 50m
memory: 50Mi
limits:
cpu: 300m
memory: 256Mi

gardener_dns_domain:
gardener_dns_provider:

gardener_backup_infrastructure:
provider: local
bucket: gardener-operator

# provider: gcp
# region:
# secretRef:
# name: backup-secret
# namespace: garden
# bucket:
#
# provider: S3
# endpoint: "{{ gardener_backup_infrastructure_secret.endpoint | b64decode }}"
# accessKeyID: "{{ gardener_backup_infrastructure_secret.accessKeyID | b64decode }}"
# secretAccessKey: "{{ gardener_backup_infrastructure_secret.secretAccessKey | b64decode}}"

gardener_backup_infrastructure_secret:
hostPath: "{{ '/etc/gardener/local-backupbuckets' | b64encode }}"

# for gcp:
# serviceaccount.json: "{{ gardener_backup_infrastructure_service_account_json | b64encode }}"
#
# for S3:
# endpoint:
# accessKeyID:
# secretAccessKey:

gardener_soil_name: "{{ metal_control_plane_stage_name }}"
gardener_soil_kubeconfig_file_path: "{{ lookup('env', 'KUBECONFIG') }}"
gardener_soil_vertical_pod_autoscaler_enabled: false
gardener_soil_project_owner_name: admin
gardener_soil_project_members: []

gardener_gardenlet_shoot_concurrent_syncs: 20
gardener_gardenlet_shoot_reconcile_in_maintenance_only: false
gardener_gardenlet_shoot_respect_sync_period_overwrite: true

gardener_local_tmp_dir: "{{ playbook_dir }}/.ansible/tmp"

gardener_logging_enabled: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
# TODO: move to image vector:
gardener_operator_image_name: europe-docker.pkg.dev/gardener-project/releases/gardener/operator
gardener_operator_image_tag: "{{ gardener_gardenlet_image_tag }}"
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
---
gardener_virtual_api_server_svc_cluster_ip_add: 20
gardener_virtual_api_server_public_dns: gardener-kube-apiserver.{{ metal_control_plane_ingress_dns }}
gardener_virtual_api_server_public_wait_port: 443
gardener_virtual_api_server_healthcheck_static_token:
gardener_virtual_api_server_port: 443

gardener_etcd_backup_schedule: "0,5,10,15,20,25,30,35,40,45,50,55 * * * *"
gardener_etcd_snapshot_period: "0"
Expand Down
286 changes: 286 additions & 0 deletions control-plane/roles/gardener-operator/filter_plugins/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
from __future__ import (absolute_import, division, print_function)

__metaclass__ = type

import base64
import ipaddress
import yaml

from ansible.module_utils.six import PY3
from ansible.plugins.test.core import version_compare
from ansible.errors import AnsibleFilterError


def b64decode(source):
content = base64.b64decode(source)
if PY3:
content = content.decode('utf-8')
return content


def b64encode(source):
if PY3:
source = source.encode('utf-8')
content = base64.b64encode(source)
if PY3:
content = content.decode('utf-8')
return content


def _extract_cluster_from_kubeconfig(kubeconfig_path):
with open(kubeconfig_path, 'r') as f:
kubeconfig = yaml.safe_load(f)

current_context_name = kubeconfig['current-context']
contexts = kubeconfig.get('contexts', [])
current_contexts = [context for context in contexts if context["name"] == current_context_name]

if not current_contexts:
raise AnsibleFilterError("current context not found in kubeconfig")

current_context = current_contexts[0]
cluster_name = current_context.get("context", dict()).get("cluster")

if not cluster_name:
raise AnsibleFilterError("cluster name not defined in current context")

clusters = kubeconfig.get('clusters', [])
current_clusters = [cluster for cluster in clusters if cluster["name"] == cluster_name]

if not current_clusters:
raise AnsibleFilterError("current cluster not found in kubeconfig")

return current_clusters[0].get("cluster", dict())


def kubeconfig_for_sa(kubeconfig_path, secret):
cluster = _extract_cluster_from_kubeconfig(kubeconfig_path)

server = cluster.get("server")

secret_data = secret.get("data", dict())
ca = str(secret_data.get("ca.crt"))
token = b64decode(secret_data.get("token"))
namespace = b64decode(secret_data.get("namespace"))

return yaml.safe_dump({
"apiVersion": "v1",
"kind": "Config",
"clusters": [
{
"name": "default-cluster",
"cluster": {
"certificate-authority-data": ca,
"server": server,
}
}
],
"contexts": [
{
"name": "default-context",
"context": {
"cluster": "default-cluster",
"namespace": namespace,
"user": "default-user",
}
}
],
"current-context": "default-context",
"users": [
{
"name": "default-user",
"user": {
"token": token,
}
}
],
})


def extract_gcp_node_network(subnets, region):
for subnet in subnets:
subnetwork = subnet.get("subnetwork")
if not subnetwork:
continue

if region in subnetwork:
return subnet.get("ipCidrRange")

raise AnsibleFilterError("no node network found for region: %s" % region)


def managed_seed_annotation(managed_seed_api_server, api_server=None):
if not managed_seed_api_server:
return ""

settings = []

if api_server:
defaultReplicas = api_server.get("replicas")
if defaultReplicas:
settings.append("apiServer.replicas=" + str(defaultReplicas))

autoscaler = api_server.get("autoscaler", dict())

min_replicas = autoscaler.get("min_replicas")
if min_replicas:
settings.append("apiServer.autoscaler.minReplicas=" + str(min_replicas))

max_replicas = autoscaler.get("max_replicas")
if max_replicas:
settings.append("apiServer.autoscaler.maxReplicas=" + str(max_replicas))

return ",".join(settings)


def network_cidr_add(cidr, add):
return str(ipaddress.ip_network(cidr).network_address + add)


def kubeconfig_from_cert(server, ca, cert, key, prepend_https=False):
if prepend_https and not server.startswith("https"):
server = "https://" + server

return yaml.safe_dump({
"apiVersion": "v1",
"kind": "Config",
"clusters": [
{
"name": "default-cluster",
"cluster": {
"certificate-authority-data": b64encode(ca),
"server": server,
}
}
],
"current-context": "default-context",
"contexts": [
{
"name": "default-context",
"context": {
"cluster": "default-cluster",
"user": "default-user",
}
}
],
"users": [
{
"name": "default-user",
"user": {
"client-certificate-data": b64encode(cert),
"client-key-data": b64encode(key),
}
}
],
})


def kubeconfig_from_token(server, ca, token, prepend_https=False):
if prepend_https and not server.startswith("https"):
server = "https://" + server

return yaml.safe_dump({
"apiVersion": "v1",
"kind": "Config",
"clusters": [
{
"name": "default-cluster",
"cluster": {
"certificate-authority-data": b64encode(ca),
"server": server,
}
}
],
"current-context": "default-context",
"contexts": [
{
"name": "default-context",
"context": {
"cluster": "default-cluster",
"user": "default-user",
}
}
],
"users": [
{
"name": "default-user",
"user": {
"token": token,
}
}
],
})


def machine_images_for_cloud_profile(image_list, cris=None):
images = dict()
for image in image_list:
if 'machine' not in image.get("features", list()):
continue

if image.get('omit_from_cloud_profile', False):
continue

image_id = image.get("id")
if image_id is None:
continue

parts = image_id.split("-")
name = "-".join(parts[:-1])

version = parts[-1]

version_parts = version.split(".")
# ubuntu-19.10.20200331
# major = version_parts[0]
minor = ".".join(version_parts[:2])

image_versions = images.get(name, set())
# Do not add the major version to the vector
# metal-api cannot match latest version if only major is given
# image_versions.add(major)
image_versions.add(minor)
image_versions.add(version)
images[name] = image_versions

result = list()
for name, value in images.items():
versions = list()
for v in sorted(list(value)):
version = dict(
version=v
)

if cris is not None and name in cris:
cri = cris[name].copy()
cri_config = cri.pop("cris", [])
cri_condition = cri.pop("when", None)

if cri_condition is None:
version["cri"] = cri_config
else:
if version_compare(v, cri_condition["version"], cri_condition["operator"]):
version["cri"] = cri_config

versions.append(version)

image = dict(
name=name,
versions=versions,
)
result.append(image)

return result


class FilterModule(object):
def filters(self):
return {
'network_cidr_add': network_cidr_add,
'kubeconfig_from_cert': kubeconfig_from_cert,
'kubeconfig_from_token': kubeconfig_from_token,
'machine_images_for_cloud_profile': machine_images_for_cloud_profile,
'kubeconfig_for_sa': kubeconfig_for_sa,
'extract_gcp_node_network': extract_gcp_node_network,
'managed_seed_annotation': managed_seed_annotation,
}
Loading
Loading