├── .dockerignore ├── .github ├── CODEOWNERS ├── stale.yml └── workflows │ └── tests.yaml ├── .gitignore ├── .golangci.yaml ├── CHANGELOG.md ├── Dockerfile ├── Dockerfile.kubernikus ├── Dockerfile.kubernikus-binaries ├── Dockerfile.kubernikus-docs ├── Dockerfile.kubernikus-docs2 ├── LICENSE ├── Makefile ├── Procfile ├── README.md ├── assets ├── canary_192x192.png ├── canary_64x64.png ├── grafana-banners-kubernikus.png ├── grafana-banners-openstack.png ├── grafana-banners-scaleout.png ├── images │ └── docs │ │ └── containers │ │ └── kubernetes │ │ ├── credentials.png │ │ ├── loadbalancer0.png │ │ ├── loadbalancer1.png │ │ ├── loadbalancer2.png │ │ ├── roleassignment.png │ │ ├── selection.png │ │ ├── setup.png │ │ └── userroleassignments.png ├── kubernetes_192x192.png ├── kubernetes_64x64.png ├── kubernikus.png ├── kubernikus.svg ├── kubernikus_14x14.png ├── kubernikus_192x192.png ├── kubernikus_64x64.png └── openstack_64x64.png ├── charts ├── .gitignore ├── README.md ├── images.yaml ├── k8sniff │ ├── Chart.yaml │ ├── templates │ │ ├── configmap.yaml │ │ ├── deployment.yaml │ │ ├── rbac.yaml │ │ └── service.yaml │ └── values.yaml ├── kube-master │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── charts │ │ └── etcd │ │ │ ├── .helmignore │ │ │ ├── Chart.yaml │ │ │ ├── templates │ │ │ ├── _helpers.tpl │ │ │ ├── deployment.yaml │ │ │ ├── pvc.yaml │ │ │ ├── secrets.yaml │ │ │ ├── service.yaml │ │ │ └── vpa.yaml │ │ │ └── values.yaml │ ├── templates │ │ ├── _helpers.tpl │ │ ├── _openstack-ccmanager.config.tpl │ │ ├── _openstack-csi.config.tpl │ │ ├── _openstack.config.tpl │ │ ├── api-vpa.yaml │ │ ├── api.yaml │ │ ├── cloud-controller-manager.yaml │ │ ├── configmap.yaml │ │ ├── controller-manager.yaml │ │ ├── csi-driver-controller.yaml │ │ ├── dashboard.yaml │ │ ├── dex-configmap.yaml │ │ ├── dex.yaml │ │ ├── ingress.yaml │ │ ├── scheduler.yaml │ │ ├── secrets.yaml │ │ ├── service-metrics.yaml │ │ └── service.yaml │ ├── test-values.yaml │ └── values.yaml ├── kubernikus-dex │ ├── Chart.yaml │ ├── templates │ │ ├── _helpers.tpl │ │ ├── dex-ingress.yaml │ │ ├── keystone-secret.yaml │ │ └── ldap-secret.yaml │ ├── test-values.yaml │ └── values.yaml ├── kubernikus │ ├── Chart.lock │ ├── Chart.yaml │ ├── templates │ │ ├── _helpers.url │ │ ├── api-service.yaml │ │ ├── api.yaml │ │ ├── clusterrolebinding.yaml │ │ ├── dex-config.yaml │ │ ├── dex.yaml │ │ ├── ingress.yaml │ │ ├── operator.yaml │ │ ├── secret-api.yaml │ │ ├── secret-operator.yaml │ │ └── serviceaccounts.yaml │ ├── test-values.yaml │ ├── values.yaml │ └── vendor │ │ ├── owner-info │ │ ├── Chart.yaml │ │ ├── README.md │ │ ├── ci │ │ │ └── test-values.yaml │ │ ├── templates │ │ │ └── configmap.yaml │ │ └── values.yaml │ │ └── rbac │ │ ├── Chart.yaml │ │ ├── templates │ │ └── rbac.yaml │ │ └── values.yaml └── seed │ ├── .helmignore │ ├── Chart.yaml │ ├── crds │ ├── volumesnapshot.yaml │ ├── volumesnapshotclass.yaml │ └── volumesnapshotcontent.yaml │ ├── templates │ ├── cni.yaml │ ├── coredns.yaml │ ├── csi.yaml │ ├── kubeadm.yaml │ ├── kubedns.yaml │ ├── proxy.yaml │ ├── rbac.yaml │ ├── storageclasses.yaml │ └── wormhole.yaml │ ├── test-values.yaml │ └── values.yaml ├── ci ├── Makefile ├── pipeline.yaml.erb ├── task_build_args.yaml ├── task_checksum.yaml ├── task_cli.yaml ├── task_e2e_tests.yaml ├── task_github_compare_url.yaml ├── task_helm_kubernikus-dex.yaml ├── task_helm_kubernikus.yaml ├── task_helm_seed.yaml ├── task_oci_build.yaml └── task_whitesource.yaml ├── cmd ├── apiserver │ └── main.go ├── go-swagger-deps │ └── main.go ├── kubernikus │ └── main.go ├── kubernikusctl │ └── main.go └── wormhole │ └── main.go ├── code-generate.mk ├── contrib ├── all │ ├── Dockerfile.apiserver │ ├── Dockerfile.etcd │ ├── Dockerfile.kubelet │ ├── Makefile │ └── api-liveness.go ├── cni-plugins │ ├── Dockerfile │ └── Makefile ├── fluentd │ ├── Dockerfile │ ├── Makefile │ └── auth_options.diff ├── k8sniff │ ├── Dockerfile │ └── Makefile ├── kubernikus-changelog-builder │ ├── .github_changelog_generator │ ├── Dockerfile │ └── Makefile ├── kubernikus-docs-builder │ ├── Dockerfile │ ├── Makefile │ └── data │ │ ├── config.toml │ │ ├── layouts │ │ ├── index.css │ │ └── index.html │ │ ├── static │ │ ├── api │ │ │ └── index.html │ │ ├── images │ │ │ ├── kubernikus.svg │ │ │ ├── logo.png │ │ │ ├── logo.svg │ │ │ └── sapcc.png │ │ └── stylesheets │ │ │ ├── hacks.css │ │ │ └── syntax.css │ │ └── themes │ │ └── hugo-material-docs │ │ ├── CHANGELOG.md │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── archetypes │ │ └── default.md │ │ ├── exampleSite │ │ ├── config.toml │ │ ├── content │ │ │ ├── adding-content │ │ │ │ └── index.md │ │ │ ├── getting-started │ │ │ │ └── index.md │ │ │ ├── index.md │ │ │ ├── license │ │ │ │ └── index.md │ │ │ └── roadmap │ │ │ │ └── index.md │ │ └── static │ │ │ └── .gitkeep │ │ ├── images │ │ ├── screenshot.png │ │ └── tn.png │ │ ├── layouts │ │ ├── 404.html │ │ ├── _default │ │ │ ├── list.html │ │ │ └── single.html │ │ ├── index.html │ │ ├── partials │ │ │ ├── drawer.html │ │ │ ├── footer.html │ │ │ ├── footer_js.html │ │ │ ├── head.html │ │ │ ├── header.html │ │ │ ├── nav.html │ │ │ └── nav_link.html │ │ └── shortcodes │ │ │ ├── note.html │ │ │ └── warning.html │ │ ├── static │ │ ├── fonts │ │ │ ├── icon.eot │ │ │ ├── icon.svg │ │ │ ├── icon.ttf │ │ │ └── icon.woff │ │ ├── images │ │ │ ├── colors.png │ │ │ ├── favicon.ico │ │ │ ├── logo.png │ │ │ └── screen.png │ │ ├── javascripts │ │ │ ├── application.js │ │ │ └── modernizr.js │ │ └── stylesheets │ │ │ ├── application.css │ │ │ ├── highlight │ │ │ └── highlight.css │ │ │ ├── palettes.css │ │ │ └── temporary.css │ │ └── theme.toml ├── kubernikus-terraform │ ├── Dockerfile │ └── Makefile ├── kubernikus-tests │ └── Dockerfile ├── kubernikusctl │ └── Dockerfile ├── multus-cni │ ├── Dockerfile │ └── Makefile └── serve_hostname │ ├── Dockerfile │ └── Makefile ├── docs ├── _index.md ├── about.md ├── development │ ├── changing_docs.md │ ├── controllers.md │ ├── helm_dev.md │ └── migrations.md ├── features.md ├── guide │ ├── authentication.md │ ├── best_practices.md │ ├── common_addons.md │ └── getting_started.md └── operations │ ├── buildup.md │ ├── controlplane.md │ └── logging.md ├── etc ├── policy-ccadmin.json └── policy.json ├── go.mod ├── go.sum ├── pkg ├── api │ ├── auth │ │ ├── authorizer.go │ │ ├── authorizer_test.go │ │ ├── keystone_auth.go │ │ └── oauth.go │ ├── client │ │ ├── kubernikus_client.go │ │ └── operations │ │ │ ├── create_cluster_parameters.go │ │ │ ├── create_cluster_responses.go │ │ │ ├── get_auth_callback_parameters.go │ │ │ ├── get_auth_callback_responses.go │ │ │ ├── get_auth_login_parameters.go │ │ │ ├── get_auth_login_responses.go │ │ │ ├── get_bootstrap_config_parameters.go │ │ │ ├── get_bootstrap_config_responses.go │ │ │ ├── get_cluster_credentials_kubeadm_parameters.go │ │ │ ├── get_cluster_credentials_kubeadm_responses.go │ │ │ ├── get_cluster_credentials_o_id_c_parameters.go │ │ │ ├── get_cluster_credentials_o_id_c_responses.go │ │ │ ├── get_cluster_credentials_parameters.go │ │ │ ├── get_cluster_credentials_responses.go │ │ │ ├── get_cluster_events_parameters.go │ │ │ ├── get_cluster_events_responses.go │ │ │ ├── get_cluster_info_parameters.go │ │ │ ├── get_cluster_info_responses.go │ │ │ ├── get_cluster_kubeadm_secret_parameters.go │ │ │ ├── get_cluster_kubeadm_secret_responses.go │ │ │ ├── get_cluster_values_parameters.go │ │ │ ├── get_cluster_values_responses.go │ │ │ ├── get_openstack_metadata_parameters.go │ │ │ ├── get_openstack_metadata_responses.go │ │ │ ├── info_parameters.go │ │ │ ├── info_responses.go │ │ │ ├── list_api_versions_parameters.go │ │ │ ├── list_api_versions_responses.go │ │ │ ├── list_clusters_parameters.go │ │ │ ├── list_clusters_responses.go │ │ │ ├── operations_client.go │ │ │ ├── show_cluster_parameters.go │ │ │ ├── show_cluster_responses.go │ │ │ ├── terminate_cluster_parameters.go │ │ │ ├── terminate_cluster_responses.go │ │ │ ├── update_cluster_parameters.go │ │ │ └── update_cluster_responses.go │ ├── handlers │ │ ├── create_cluster.go │ │ ├── errors.go │ │ ├── get_cluster_bootstrap.go │ │ ├── get_cluster_credentials.go │ │ ├── get_cluster_credentials_oidc.go │ │ ├── get_cluster_events.go │ │ ├── get_cluster_info.go │ │ ├── get_cluster_info_test.go │ │ ├── get_cluster_kubeadm_secret.go │ │ ├── get_cluster_values.go │ │ ├── get_openstack_metadata.go │ │ ├── info.go │ │ ├── list_api_versions.go │ │ ├── list_clusters.go │ │ ├── root.go │ │ ├── show_cluster.go │ │ ├── terminate_cluster.go │ │ ├── update_cluster.go │ │ ├── util.go │ │ └── util_test.go │ ├── models │ │ ├── api_versions.go │ │ ├── authentication_configuration.go │ │ ├── availability_zone.go │ │ ├── binaries.go │ │ ├── bootstrap_config.go │ │ ├── credentials.go │ │ ├── doc.go │ │ ├── error.go │ │ ├── event.go │ │ ├── flavor.go │ │ ├── flavor_sorter.go │ │ ├── flavor_sorter_test.go │ │ ├── get_auth_callback_o_k_body.go │ │ ├── get_cluster_values_o_k_body.go │ │ ├── info.go │ │ ├── key_pair.go │ │ ├── kluster.go │ │ ├── kluster_info.go │ │ ├── kluster_phase.go │ │ ├── kluster_print.go │ │ ├── kluster_spec.go │ │ ├── kluster_status.go │ │ ├── kubeadm_secret.go │ │ ├── link.go │ │ ├── network.go │ │ ├── node_pool.go │ │ ├── node_pool_config.go │ │ ├── node_pool_info.go │ │ ├── node_pool_info_test.go │ │ ├── node_pool_test.go │ │ ├── o_id_c.go │ │ ├── openstack_metadata.go │ │ ├── openstack_spec.go │ │ ├── principal.go │ │ ├── router.go │ │ ├── security_group.go │ │ ├── subnet.go │ │ └── zz_generated.deepcopy.go │ ├── rest │ │ ├── api_test.go │ │ ├── configure.go │ │ ├── configure_kubernikus.go │ │ ├── doc.go │ │ ├── kubeclient.go │ │ ├── metrics.go │ │ ├── operations │ │ │ ├── create_cluster.go │ │ │ ├── create_cluster_parameters.go │ │ │ ├── create_cluster_responses.go │ │ │ ├── create_cluster_urlbuilder.go │ │ │ ├── get_auth_callback.go │ │ │ ├── get_auth_callback_parameters.go │ │ │ ├── get_auth_callback_responses.go │ │ │ ├── get_auth_callback_urlbuilder.go │ │ │ ├── get_auth_login.go │ │ │ ├── get_auth_login_parameters.go │ │ │ ├── get_auth_login_responses.go │ │ │ ├── get_auth_login_urlbuilder.go │ │ │ ├── get_bootstrap_config.go │ │ │ ├── get_bootstrap_config_parameters.go │ │ │ ├── get_bootstrap_config_responses.go │ │ │ ├── get_bootstrap_config_urlbuilder.go │ │ │ ├── get_cluster_credentials.go │ │ │ ├── get_cluster_credentials_kubeadm.go │ │ │ ├── get_cluster_credentials_kubeadm_parameters.go │ │ │ ├── get_cluster_credentials_kubeadm_responses.go │ │ │ ├── get_cluster_credentials_kubeadm_urlbuilder.go │ │ │ ├── get_cluster_credentials_o_id_c.go │ │ │ ├── get_cluster_credentials_o_id_c_parameters.go │ │ │ ├── get_cluster_credentials_o_id_c_responses.go │ │ │ ├── get_cluster_credentials_o_id_c_urlbuilder.go │ │ │ ├── get_cluster_credentials_parameters.go │ │ │ ├── get_cluster_credentials_responses.go │ │ │ ├── get_cluster_credentials_urlbuilder.go │ │ │ ├── get_cluster_events.go │ │ │ ├── get_cluster_events_parameters.go │ │ │ ├── get_cluster_events_responses.go │ │ │ ├── get_cluster_events_urlbuilder.go │ │ │ ├── get_cluster_info.go │ │ │ ├── get_cluster_info_parameters.go │ │ │ ├── get_cluster_info_responses.go │ │ │ ├── get_cluster_info_urlbuilder.go │ │ │ ├── get_cluster_kubeadm_secret.go │ │ │ ├── get_cluster_kubeadm_secret_parameters.go │ │ │ ├── get_cluster_kubeadm_secret_responses.go │ │ │ ├── get_cluster_kubeadm_secret_urlbuilder.go │ │ │ ├── get_cluster_values.go │ │ │ ├── get_cluster_values_parameters.go │ │ │ ├── get_cluster_values_responses.go │ │ │ ├── get_cluster_values_urlbuilder.go │ │ │ ├── get_openstack_metadata.go │ │ │ ├── get_openstack_metadata_parameters.go │ │ │ ├── get_openstack_metadata_responses.go │ │ │ ├── get_openstack_metadata_urlbuilder.go │ │ │ ├── info.go │ │ │ ├── info_parameters.go │ │ │ ├── info_responses.go │ │ │ ├── info_urlbuilder.go │ │ │ ├── kubernikus_api.go │ │ │ ├── list_api_versions.go │ │ │ ├── list_api_versions_parameters.go │ │ │ ├── list_api_versions_responses.go │ │ │ ├── list_api_versions_urlbuilder.go │ │ │ ├── list_clusters.go │ │ │ ├── list_clusters_parameters.go │ │ │ ├── list_clusters_responses.go │ │ │ ├── list_clusters_urlbuilder.go │ │ │ ├── show_cluster.go │ │ │ ├── show_cluster_parameters.go │ │ │ ├── show_cluster_responses.go │ │ │ ├── show_cluster_urlbuilder.go │ │ │ ├── terminate_cluster.go │ │ │ ├── terminate_cluster_parameters.go │ │ │ ├── terminate_cluster_responses.go │ │ │ ├── terminate_cluster_urlbuilder.go │ │ │ ├── update_cluster.go │ │ │ ├── update_cluster_parameters.go │ │ │ ├── update_cluster_responses.go │ │ │ └── update_cluster_urlbuilder.go │ │ └── server.go │ ├── runtime.go │ └── spec │ │ ├── document.go │ │ ├── document_test.go │ │ └── embedded_spec.go ├── apis │ └── kubernikus │ │ ├── factory.go │ │ └── v1 │ │ ├── constants.go │ │ ├── doc.go │ │ ├── kluster.go │ │ ├── register.go │ │ ├── secret.go │ │ ├── secret_test.go │ │ └── zz_generated.deepcopy.go ├── client │ ├── helm │ │ └── client.go │ ├── kubernetes │ │ ├── client.go │ │ ├── shared_client_factory.go │ │ └── tunnel.go │ ├── kubernikus │ │ └── client.go │ └── openstack │ │ ├── admin │ │ ├── client.go │ │ └── logging.go │ │ ├── compute │ │ └── create.go │ │ ├── domains │ │ ├── requests.go │ │ ├── results.go │ │ └── urls.go │ │ ├── factory.go │ │ ├── kluster │ │ ├── client.go │ │ ├── logging.go │ │ └── node.go │ │ ├── project │ │ ├── client.go │ │ └── logging.go │ │ └── roles │ │ ├── requests.go │ │ ├── results.go │ │ └── urls.go ├── cmd │ ├── env.go │ ├── errors.go │ ├── kubernikus │ │ ├── certificates.go │ │ ├── certificates │ │ │ ├── files.go │ │ │ ├── io.go │ │ │ └── plain.go │ │ ├── helm.go │ │ ├── kubernikus.go │ │ ├── operator.go │ │ ├── seed.go │ │ └── seed │ │ │ └── dns.go │ ├── kubernikusctl │ │ ├── auth.go │ │ ├── auth │ │ │ ├── init.go │ │ │ └── refresh.go │ │ ├── common │ │ │ ├── kubecontext.go │ │ │ ├── kubecontext_test.go │ │ │ ├── kubernikus.go │ │ │ ├── log.go │ │ │ ├── openstack.go │ │ │ └── util.go │ │ ├── create.go │ │ ├── create │ │ │ ├── cluster.go │ │ │ └── common.go │ │ ├── delete.go │ │ ├── delete │ │ │ ├── cluster.go │ │ │ └── common.go │ │ ├── get.go │ │ ├── get │ │ │ ├── cluster.go │ │ │ ├── common.go │ │ │ ├── nodepool.go │ │ │ └── values.go │ │ ├── kubernikusctl.go │ │ └── version.go │ ├── printers │ │ └── printable.go │ ├── runner.go │ ├── validate.go │ └── wormhole │ │ ├── client.go │ │ ├── server.go │ │ └── wormhole.go ├── controller │ ├── base │ │ ├── controller.go │ │ ├── instrumenting.go │ │ ├── logging.go │ │ └── polling_controller.go │ ├── certs │ │ └── certs.go │ ├── config │ │ └── config.go │ ├── deorbit │ │ ├── controller.go │ │ ├── controller_test.go │ │ ├── deorbiter.go │ │ ├── deorbiter_test.go │ │ ├── events.go │ │ ├── fake.go │ │ ├── logs.go │ │ └── metrics.go │ ├── events │ │ └── event.go │ ├── flight │ │ ├── controller.go │ │ ├── controller_test.go │ │ ├── factory.go │ │ ├── instance.go │ │ ├── logging.go │ │ ├── reconciler.go │ │ └── reconciler_test.go │ ├── ground.go │ ├── ground │ │ ├── bootstrap.go │ │ ├── bootstrap │ │ │ ├── ccm │ │ │ │ ├── ccm.go │ │ │ │ └── manifest.go │ │ │ ├── csi │ │ │ │ ├── csi.go │ │ │ │ ├── csi123.go │ │ │ │ └── manifest.go │ │ │ ├── dns │ │ │ │ ├── coredns.go │ │ │ │ └── kubedns.go │ │ │ ├── network │ │ │ │ ├── cni.go │ │ │ │ ├── kube_proxy_manifests.go │ │ │ │ ├── manifest.go │ │ │ │ └── wormhole_manifests.go │ │ │ └── util.go │ │ ├── gvk.go │ │ └── reconciler.go │ ├── hammertime │ │ └── hammertime.go │ ├── launch │ │ ├── controller.go │ │ ├── eventing.go │ │ ├── instrumenting.go │ │ ├── logging.go │ │ ├── pool_manager.go │ │ └── pool_manager_test.go │ ├── metrics │ │ ├── controller.go │ │ ├── deorbit.go │ │ ├── hammertime.go │ │ ├── kluster.go │ │ ├── launch.go │ │ ├── metrics.go │ │ ├── migration.go │ │ └── routegc.go │ ├── migration │ │ ├── controller.go │ │ └── status.go │ ├── nodeobservatory │ │ ├── controller.go │ │ ├── controller_test.go │ │ ├── factory.go │ │ ├── interface.go │ │ ├── nodeInformer.go │ │ └── testing.go │ ├── operator.go │ ├── routegc │ │ └── controller.go │ └── servicing │ │ ├── collector.go │ │ ├── controller.go │ │ ├── controller_test.go │ │ ├── coreos │ │ ├── releases.go │ │ ├── releases_test.go │ │ ├── testing.go │ │ ├── version.go │ │ └── version_test.go │ │ ├── drain │ │ ├── cordon.go │ │ ├── default.go │ │ ├── drain.go │ │ ├── filters.go │ │ └── logger.go │ │ ├── flatcar │ │ ├── releases.go │ │ ├── releases_test.go │ │ ├── testing.go │ │ ├── version.go │ │ └── version_test.go │ │ ├── lifecycler.go │ │ ├── lifecycler_test.go │ │ ├── lister.go │ │ ├── lister_test.go │ │ ├── reconciler.go │ │ └── testing.go ├── generated │ ├── clientset │ │ ├── clientset.go │ │ ├── doc.go │ │ ├── fake │ │ │ ├── clientset_generated.go │ │ │ ├── doc.go │ │ │ └── register.go │ │ ├── scheme │ │ │ ├── doc.go │ │ │ └── register.go │ │ └── typed │ │ │ └── kubernikus │ │ │ └── v1 │ │ │ ├── doc.go │ │ │ ├── fake │ │ │ ├── doc.go │ │ │ ├── fake_kluster.go │ │ │ └── fake_kubernikus_client.go │ │ │ ├── generated_expansion.go │ │ │ ├── kluster.go │ │ │ └── kubernikus_client.go │ ├── informers │ │ └── externalversions │ │ │ ├── factory.go │ │ │ ├── generic.go │ │ │ ├── internalinterfaces │ │ │ └── factory_interfaces.go │ │ │ └── kubernikus │ │ │ ├── interface.go │ │ │ └── v1 │ │ │ ├── interface.go │ │ │ └── kluster.go │ └── listers │ │ └── kubernikus │ │ └── v1 │ │ ├── expansion_generated.go │ │ └── kluster.go ├── migration │ ├── 01_init.go │ ├── 02_add_aggregation_layer_certificates.go │ ├── 03_etcdbr_create_storage_container.go │ ├── 04_migrate_kluster_secret.go │ ├── 05_insert_avz_into_nodepools.go │ ├── 06_seed_storage_classes.go │ ├── 07_seed_allow_apiserver_to_access_kubelet.go │ ├── 08_noop.go │ ├── 09_reconcile_k8s_version_in_spec.go │ ├── 10_ensure_lb_floating_network_id.go │ ├── 11_ensure_security_group_name.go │ ├── 13_fix_certificates.go │ ├── 14_cleanup_namespaces.go │ ├── 15_default_node_pool_options.go │ ├── 16_fix_update_conf.go │ ├── 17_dex.go │ ├── 18_advertisePort.go │ ├── 19_fix_flatcar.go │ ├── 20_kluster_secret_openstack_ids.go │ ├── 21_helm_2to3.go │ ├── migration.go │ ├── migration_test.go │ ├── register.go │ └── suppository.go ├── templates │ ├── ignition.go │ ├── ignition_test.go │ ├── node_1.10.go │ ├── node_1.11.go │ ├── node_1.12.go │ ├── node_1.14.go │ ├── node_1.17.go │ ├── node_1.19.go │ ├── node_1.20.go │ ├── node_1.21.go │ ├── node_1.24.go │ ├── node_1.26.go │ └── node_1.27.go ├── util │ ├── bootstrap.go │ ├── bootstraptoken │ │ └── util.go │ ├── certificates.go │ ├── certificates_test.go │ ├── constants.go │ ├── etcd │ │ └── etcd.go │ ├── generator │ │ └── namegenerator.go │ ├── helm │ │ ├── helm.go │ │ ├── helm_test.go │ │ └── merge_map.go │ ├── icmp │ │ ├── configure_darwin.go │ │ ├── configure_linux.go │ │ ├── icmp.go │ │ ├── icmp_linux_test.go │ │ ├── icmp_test.go │ │ └── redirect.go │ ├── ip │ │ ├── cidr.go │ │ ├── cidr_test.go │ │ └── ip.go │ ├── ip_or_dnsname.go │ ├── ip_or_dnsname_test.go │ ├── iptables │ │ ├── iptables.go │ │ └── save_restore.go │ ├── k8s │ │ └── namespace.go │ ├── kluster.go │ ├── kluster_test.go │ ├── log │ │ ├── authlogger.go │ │ ├── error_origin.go │ │ ├── gophercloud.go │ │ ├── levelfilter.go │ │ ├── levelfilter_test.go │ │ ├── logging.go │ │ ├── middleware.go │ │ └── nilfilter.go │ ├── netutil │ │ ├── interface.go │ │ ├── interface_darwin.go │ │ ├── interface_linux.go │ │ └── interface_windows.go │ ├── node.go │ ├── node_test.go │ ├── owner.go │ ├── pod │ │ └── pod.go │ ├── pvc_accessmode.go │ ├── retry.go │ ├── strings.go │ ├── version │ │ └── version.go │ ├── wait │ │ └── wait.go │ └── workqueue │ │ └── prometheus │ │ └── prometheus.go ├── version │ ├── base.go │ ├── images.go │ └── images_test.go └── wormhole │ ├── client │ ├── guttle.go │ └── health.go │ └── server │ ├── controller.go │ ├── options.go │ ├── server.go │ └── tunnel.go ├── scripts ├── adjust-resources.sh ├── coredns │ ├── clusterrole.yaml │ ├── clusterrolebinding.yaml │ ├── configmap.yaml │ ├── delete-kube-dns.sh │ ├── deployment.yaml │ ├── dnsutils.yaml │ ├── install-coredns.sh │ ├── service.yaml │ ├── serviceaccount.yaml │ └── test-dns.sh ├── fix-br-netfilter.sh ├── fix-flatcar-update-reset.sh ├── fix.sh ├── flatcar-update-reset.sh ├── kube-proxy-fixer.sh ├── kubeconfig.sh ├── load-br-netfilter.sh └── wormhole-fixer.sh ├── swagger.yml └── test ├── charts └── charts.sh ├── e2e ├── api_test.go ├── cleanup_test.go ├── etcdbr_test.go ├── framework │ ├── auth_info.go │ ├── conditions.go │ ├── exec.go │ ├── kubernetes.go │ ├── kubernikus.go │ └── openstack.go ├── main_test.go ├── network_test.go ├── node_test.go ├── preflight_test.go ├── pyrolisis_test.go ├── setup_test.go └── volume_test.go ├── gofmt.sh ├── patch-node-annotations └── main.go └── volume-test ├── .helmignore ├── Chart.yaml ├── templates ├── _helpers.tpl └── pod-and-pvc.yaml └── values.yaml /.dockerignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | _output 3 | _scratch 4 | .git 5 | _output 6 | Docker* 7 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | # These owners will be the default owners for everything in 4 | # the repo. Unless a later match takes precedence, 5 | 6 | * @databus23 @jknipper @Nuckal777 7 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 90 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: 7 | - master 8 | name: Tests 9 | jobs: 10 | test: 11 | strategy: 12 | matrix: 13 | go-version: [1.22.x] 14 | os: [ubuntu-latest] 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - name: Install Go 18 | uses: actions/setup-go@v3 19 | with: 20 | go-version: ${{ matrix.go-version }} 21 | - name: Checkout code 22 | uses: actions/checkout@v3 23 | - name: golangci-lint 24 | uses: golangci/golangci-lint-action@v3 25 | with: 26 | version: v1.62.2 27 | - name: Tests 28 | run: /bin/bash -c make gotest 29 | - name: Build e2e 30 | run: make build-e2e 31 | - name: Charts 32 | run: make test-charts 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | _scratch 3 | _output 4 | *.swp 5 | .envrc 6 | .idea 7 | .env 8 | /ci/pipeline.yaml 9 | .vscode 10 | 11 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 5m 3 | linters: 4 | enable: 5 | - gci 6 | - gofmt 7 | disable: 8 | - errcheck 9 | - gosimple 10 | issues: 11 | fix: false 12 | linters-settings: 13 | gci: 14 | sections: 15 | - standard 16 | - default 17 | - prefix(github.com/sapcc/kubernikus) 18 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG VERSION=latest 2 | 3 | FROM sapcc/kubernikus-binaries:$VERSION as kubernikus-binaries 4 | FROM sapcc/kubernikus-docs:$VERSION as kubernikus-docs 5 | 6 | FROM alpine:3.21 as kubernikus 7 | LABEL source_repository="https://github.com/sapcc/kubernikus" 8 | MAINTAINER "Fabian Ruff " 9 | RUN apk add --no-cache curl iptables 10 | RUN curl -Lo /bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64 \ 11 | && chmod +x /bin/dumb-init \ 12 | && dumb-init -V 13 | COPY etc/*.json /etc/kubernikus/ 14 | COPY charts/ /etc/kubernikus/charts 15 | COPY --from=kubernikus-binaries /apiserver /kubernikus /wormhole /usr/local/bin/ 16 | #COPY --from=kubernikus-binaries /kubernikusctl /static/binaries/linux/amd64/kubernikusctl 17 | COPY --from=kubernikus-docs /public/docs /static/docs 18 | ENTRYPOINT ["dumb-init", "--"] 19 | CMD ["apiserver"] 20 | -------------------------------------------------------------------------------- /Dockerfile.kubernikus: -------------------------------------------------------------------------------- 1 | ARG DOCS_IMAGE=keppel.eu-de-1.cloud.sap/ccloud/kubernikus-docs-builder:latest 2 | FROM keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/golang:1.23-alpine3.21 as builder 3 | WORKDIR /app 4 | RUN apk add --no-cache make bash git curl gcc musl-dev 5 | RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.62.2 6 | COPY . . 7 | ENV GOARCH=amd64 8 | ARG VERSION 9 | RUN make linters 10 | RUN make all 11 | RUN make gotest 12 | RUN make build-e2e 13 | 14 | FROM ${DOCS_IMAGE} as kubernikus-docs 15 | 16 | FROM keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/alpine:3.21 as kubernikus 17 | LABEL source_repository="https://github.com/sapcc/kubernikus" 18 | MAINTAINER "Fabian Ruff " 19 | RUN apk add --no-cache curl iptables 20 | RUN curl -Lo /bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64 \ 21 | && chmod +x /bin/dumb-init \ 22 | && dumb-init -V 23 | COPY etc/*.json /etc/kubernikus/ 24 | COPY --from=kubernikus-docs /public/docs /static/docs 25 | COPY charts/ /etc/kubernikus/charts 26 | COPY --from=builder /app/bin/linux/kubernikus \ 27 | /app/bin/linux/apiserver \ 28 | /app/bin/linux/wormhole /usr/local/bin/ 29 | ENTRYPOINT ["dumb-init", "--"] 30 | CMD ["apiserver"] 31 | -------------------------------------------------------------------------------- /Dockerfile.kubernikus-binaries: -------------------------------------------------------------------------------- 1 | FROM keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/golang:1.23-alpine3.21 as builder 2 | RUN apk add --no-cache make git curl bash gcc musl-dev 3 | WORKDIR /app 4 | RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.62.2 5 | COPY . . 6 | ENV GOARCH=amd64 7 | ARG VERSION 8 | #We run linter before compiling for faster feedback 9 | RUN make linters 10 | RUN make all 11 | RUN make gotest 12 | RUN make build-e2e 13 | 14 | FROM scratch as kubernikus-binaries 15 | COPY --from=builder /app/bin/linux/* / 16 | -------------------------------------------------------------------------------- /Dockerfile.kubernikus-docs: -------------------------------------------------------------------------------- 1 | ARG VERSION=latest 2 | FROM sapcc/kubernikus-docs-builder:$VERSION 3 | -------------------------------------------------------------------------------- /Dockerfile.kubernikus-docs2: -------------------------------------------------------------------------------- 1 | FROM keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/alpine:3.21 2 | LABEL source_repository="https://github.com/sapcc/kubernikus" 3 | 4 | ARG HUGO_VERSION=0.30.2 5 | 6 | RUN apk add --no-cache curl 7 | RUN curl -Lo hugo.tar.gz https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.tar.gz 8 | RUN tar xvf hugo.tar.gz hugo -C /usr/local/bin 9 | RUN curl -Lo /usr/local/bin/yaml2json https://github.com/bronze1man/yaml2json/releases/download/v1.2/yaml2json_linux_amd64 && \ 10 | chmod +x /usr/local/bin/yaml2json 11 | COPY contrib/kubernikus-docs-builder/data / 12 | 13 | COPY docs /content 14 | RUN hugo --baseURL "/docs" --destination /public/docs 15 | RUN hugo --baseURL "/kubernikus" --destination /public/kubernikus 16 | 17 | COPY swagger.yml / 18 | RUN yaml2json < /swagger.yml > /public/docs/api/swagger.json 19 | RUN yaml2json < /swagger.yml > /public/kubernikus/api/swagger.json 20 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | operator: bin/$(go env GOOS)/kubernikus operator --auth-username=$KS_USERNAME --auth-domain=$KS_USER_DOMAIN_NAME --auth-password="$KS_PASSWORD" --auth-project=$KS_PROJECT_NAME --auth-project-domain=$KS_PROJECT_DOMAIN_NAME --auth-url=$KS_AUTH_URL --namespace=$KS_NAMESPACE --context=$KS_CONTEXT --kubernikus-domain=$KS_DOMAIN --v=5 2 | api: bin/$(go env GOOS)/apiserver --context=$KS_CONTEXT --namespace=$KS_NAMESPACE --auth-url=$KS_AUTH_URL --images-file=charts/images.yaml --v=5 3 | -------------------------------------------------------------------------------- /assets/canary_192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/canary_192x192.png -------------------------------------------------------------------------------- /assets/canary_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/canary_64x64.png -------------------------------------------------------------------------------- /assets/grafana-banners-kubernikus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/grafana-banners-kubernikus.png -------------------------------------------------------------------------------- /assets/grafana-banners-openstack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/grafana-banners-openstack.png -------------------------------------------------------------------------------- /assets/grafana-banners-scaleout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/grafana-banners-scaleout.png -------------------------------------------------------------------------------- /assets/images/docs/containers/kubernetes/credentials.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/images/docs/containers/kubernetes/credentials.png -------------------------------------------------------------------------------- /assets/images/docs/containers/kubernetes/loadbalancer0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/images/docs/containers/kubernetes/loadbalancer0.png -------------------------------------------------------------------------------- /assets/images/docs/containers/kubernetes/loadbalancer1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/images/docs/containers/kubernetes/loadbalancer1.png -------------------------------------------------------------------------------- /assets/images/docs/containers/kubernetes/loadbalancer2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/images/docs/containers/kubernetes/loadbalancer2.png -------------------------------------------------------------------------------- /assets/images/docs/containers/kubernetes/roleassignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/images/docs/containers/kubernetes/roleassignment.png -------------------------------------------------------------------------------- /assets/images/docs/containers/kubernetes/selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/images/docs/containers/kubernetes/selection.png -------------------------------------------------------------------------------- /assets/images/docs/containers/kubernetes/setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/images/docs/containers/kubernetes/setup.png -------------------------------------------------------------------------------- /assets/images/docs/containers/kubernetes/userroleassignments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/images/docs/containers/kubernetes/userroleassignments.png -------------------------------------------------------------------------------- /assets/kubernetes_192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/kubernetes_192x192.png -------------------------------------------------------------------------------- /assets/kubernetes_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/kubernetes_64x64.png -------------------------------------------------------------------------------- /assets/kubernikus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/kubernikus.png -------------------------------------------------------------------------------- /assets/kubernikus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/kubernikus_14x14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/kubernikus_14x14.png -------------------------------------------------------------------------------- /assets/kubernikus_192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/kubernikus_192x192.png -------------------------------------------------------------------------------- /assets/kubernikus_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/kubernikus_64x64.png -------------------------------------------------------------------------------- /assets/openstack_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/assets/openstack_64x64.png -------------------------------------------------------------------------------- /charts/.gitignore: -------------------------------------------------------------------------------- 1 | **.tgz -------------------------------------------------------------------------------- /charts/README.md: -------------------------------------------------------------------------------- 1 | The directory names correspond to the namespaces of the entities created by the Helm charts with the exception of `kube-master`. 2 | `kube-master` contains the Helm charts used to create the Kubernetes master (API, controller manager, etcd, etc.) for our Kubernikus users. 3 | -------------------------------------------------------------------------------- /charts/k8sniff/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for k8sniff 3 | name: k8sniff 4 | version: 0.2.0 5 | -------------------------------------------------------------------------------- /charts/k8sniff/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: k8sniff-configmap 5 | data: 6 | k8sniff.json: | 7 | { 8 | "bind": { 9 | "host": "0.0.0.0", 10 | "port": 8443 11 | }, 12 | "metrics": { 13 | "host": "0.0.0.0", 14 | "port": {{ .Values.metrics_port }}, 15 | "path": "/metrics" 16 | }, 17 | "kubernetes": {} 18 | } 19 | -------------------------------------------------------------------------------- /charts/k8sniff/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.useRBAC -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: "k8sniff" 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRole 9 | metadata: 10 | name: "k8sniff" 11 | rules: 12 | - apiGroups: 13 | - "" 14 | resources: 15 | - services 16 | verbs: 17 | - watch 18 | - list 19 | - get 20 | - apiGroups: 21 | - extensions 22 | - networking.k8s.io 23 | resources: 24 | - ingresses 25 | verbs: 26 | - watch 27 | - list 28 | - get 29 | --- 30 | apiVersion: rbac.authorization.k8s.io/v1 31 | kind: ClusterRoleBinding 32 | metadata: 33 | name: "k8sniff" 34 | subjects: 35 | - kind: ServiceAccount 36 | name: "k8sniff" 37 | namespace: "{{ .Release.Namespace }}" 38 | roleRef: 39 | kind: ClusterRole 40 | name: "k8sniff" 41 | apiGroup: rbac.authorization.k8s.io 42 | {{ end }} 43 | -------------------------------------------------------------------------------- /charts/k8sniff/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: k8sniff-ingress-lb 5 | spec: 6 | type: {{ .Values.service.type }} 7 | ports: 8 | - port: 443 9 | targetPort: 8443 10 | {{- if .Values.service.nodePort }} 11 | nodePort: {{ .Values.service.nodePort }} 12 | {{- end }} 13 | protocol: TCP 14 | selector: 15 | role: k8sniff-ingress-lb 16 | {{- if .Values.service.externalIP }} 17 | externalIPs: 18 | - {{ .Values.service.externalIP }} 19 | {{- end }} 20 | {{- if .Values.service.loadBalancerIP }} 21 | loadBalancerIP: {{ .Values.service.loadBalancerIP }} 22 | {{- end }} 23 | 24 | -------------------------------------------------------------------------------- /charts/k8sniff/values.yaml: -------------------------------------------------------------------------------- 1 | image: "keppel.global.cloud.sap/ccloud/k8sniff" 2 | tag: "c3651f238719362eac34170f1f59370967f10f20" 3 | metrics_port: 9091 4 | log_level: 2 5 | replicas: 2 6 | useRBAC: false 7 | 8 | service: 9 | type: LoadBalancer 10 | #nodePort: 11 | #loadBalancerIP: 12 | #externalIP: 13 | -------------------------------------------------------------------------------- /charts/kube-master/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /charts/kube-master/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for Kubernetes 3 | name: kube-master 4 | version: 2.1.9 5 | -------------------------------------------------------------------------------- /charts/kube-master/charts/etcd/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /charts/kube-master/charts/etcd/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for Kubernetes 3 | name: etcd 4 | version: 0.1.3 5 | -------------------------------------------------------------------------------- /charts/kube-master/charts/etcd/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | */}} 13 | {{- define "fullname" -}} 14 | {{- $name := default .Chart.Name .Values.nameOverride -}} 15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 16 | {{- end -}} 17 | -------------------------------------------------------------------------------- /charts/kube-master/charts/etcd/templates/pvc.yaml: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=gotexttmpl: */ -}} 2 | {{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) -}} 3 | kind: PersistentVolumeClaim 4 | apiVersion: v1 5 | metadata: 6 | name: {{ include "fullname" .}} 7 | labels: 8 | chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" 9 | release: {{ .Release.Name }} 10 | annotations: 11 | {{- if .Values.persistence.storageClass }} 12 | volume.beta.kubernetes.io/storage-class: {{ .Values.persistence.storageClass | quote }} 13 | {{- end }} 14 | spec: 15 | accessModes: 16 | - {{ .Values.persistence.accessMode | quote }} 17 | resources: 18 | requests: 19 | storage: {{ .Values.persistence.size | quote }} 20 | {{- end -}} 21 | -------------------------------------------------------------------------------- /charts/kube-master/charts/etcd/templates/secrets.yaml: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=gotexttmpl: */ -}} 2 | {{- if .Values.backup.enabled }} 3 | {{- if eq .Values.backup.storageProvider "Swift" }} 4 | apiVersion: v1 5 | kind: Secret 6 | metadata: 7 | name: {{ include "fullname" . }} 8 | labels: 9 | chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" 10 | release: {{ .Release.Name }} 11 | type: Opaque 12 | data: 13 | openstack-auth-url: {{ required "missing openstack-auth-url" .Values.openstack.authURL | b64enc }} 14 | openstack-username: {{ required "missing openstack-username" .Values.openstack.username | b64enc }} 15 | openstack-password: {{ required "missing openstack-password" .Values.openstack.password | b64enc }} 16 | openstack-domain-name: {{ required "missing openstack-domain-name" .Values.openstack.domainName | b64enc }} 17 | openstack-project-id: {{ required "missing openstack-project-id" .Values.openstack.projectID | b64enc }} 18 | {{- end }} 19 | {{- end }} 20 | -------------------------------------------------------------------------------- /charts/kube-master/charts/etcd/templates/service.yaml: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=gotexttmpl: */ -}} 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ include "fullname" . }} 6 | labels: 7 | chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" 8 | release: {{ .Release.Name }} 9 | spec: 10 | type: ClusterIP 11 | ports: 12 | - port: 2379 13 | name: etcd 14 | - port: 8080 15 | name: backup 16 | selector: 17 | app: {{ include "fullname" . }} 18 | -------------------------------------------------------------------------------- /charts/kube-master/charts/etcd/templates/vpa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "autoscaling.k8s.io/v1" 2 | kind: VerticalPodAutoscaler 3 | metadata: 4 | name: {{ include "fullname" . }} 5 | spec: 6 | targetRef: 7 | {{- if .Capabilities.APIVersions.Has "apps/v1" }} 8 | apiVersion: "apps/v1" 9 | {{- else }} 10 | apiVersion: "extensions/v1beta1" 11 | {{- end }} 12 | kind: Deployment 13 | name: {{ include "fullname" . }} 14 | resourcePolicy: 15 | containerPolicies: 16 | - containerName: etcd 17 | controlledResources: ["cpu", "memory"] 18 | minAllowed: 19 | {{- toYaml .Values.resources.requests | nindent 12 }} 20 | {{- if .Values.resources.limits }} 21 | maxAllowed: 22 | {{- toYaml .Values.resources.limits | nindent 12 }} 23 | {{- end }} 24 | {{- if and .Values.backup.enabled .Values.resources}} 25 | - containerName: backup 26 | controlledResources: ["cpu", "memory"] 27 | minAllowed: 28 | {{- toYaml .Values.backup.resources.requests | nindent 12 }} 29 | {{- if .Values.resources.limits }} 30 | maxAllowed: 31 | {{- toYaml .Values.resources.limits| nindent 12 }} 32 | {{- end }} 33 | {{- end }} 34 | -------------------------------------------------------------------------------- /charts/kube-master/charts/etcd/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for etcd. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | images: 5 | etcd: 6 | repository: sapcc/etcd 7 | tag: v3.3.14 8 | etcdBackup: 9 | repository: sapcc/etcdbrctl 10 | tag: 0.5.2 11 | ## Persist data to a persitent volume 12 | persistence: 13 | enabled: true 14 | accessMode: ReadWriteOnce 15 | size: 10Gi 16 | # Re-use existing (unmanged) PVC 17 | # existingClaim: claimName 18 | resources: 19 | requests: 20 | cpu: 200m 21 | memory: 500Mi 22 | limits: 23 | cpu: 1 24 | memory: 2560Mi 25 | backup: 26 | enabled: true 27 | # do a full-backup every hour 28 | schedule: "15 * * * *" 29 | # keep number of backups 30 | # only used if garbageCollectionPolicy is LimitBased 31 | # maxBackups: 168 32 | # delta-snapshot every 30 seconds 33 | deltaSnapshotPeriod: 30 34 | # clean-up old backups every hour 35 | garbageCollectionPeriod: 3600 36 | # condense in time 37 | garbageCollectionPolicy: "Exponential" 38 | resources: 39 | requests: 40 | cpu: 100m 41 | memory: 128Mi 42 | limits: 43 | cpu: 500m 44 | memory: 1.5Gi 45 | secure: 46 | enabled: true 47 | version: {} 48 | # kubernikus: 49 | # kubernetes: 1.10.11 50 | -------------------------------------------------------------------------------- /charts/kube-master/templates/_openstack-ccmanager.config.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=gotexttmpl: */ -}} 2 | [Global] 3 | auth-url = {{ required "missing openstack.authURL" .Values.openstack.authURL }} 4 | username = {{ required "missing openstack.username" .Values.openstack.username }} 5 | password = {{ required "missing openstack.password" .Values.openstack.password }} 6 | user-domain-name = {{ required "missing openstack.domainName" .Values.openstack.domainName }} 7 | {{- if .Values.openstack.projectScope }} 8 | tenant-id = {{ .Values.openstack.projectID }} 9 | {{- end }} 10 | {{- if .Values.openstack.region }} 11 | region = {{ .Values.openstack.region }} 12 | {{- end }} 13 | [LoadBalancer] 14 | lb-version=v2 15 | lb-provider = f5 16 | use-octavia = {{ default "no" .Values.openstack.useOctavia }} 17 | subnet-id= {{ required "missing openstack.lbSubnetID" .Values.openstack.lbSubnetID }} 18 | floating-network-id= {{ required "missing openstack.lbFloatingNetworkID" .Values.openstack.lbFloatingNetworkID }} 19 | create-monitor = yes 20 | monitor-delay = 1m 21 | monitor-timeout = 30s 22 | monitor-max-retries = 3 23 | [BlockStorage] 24 | trust-device-path = no 25 | [Route] 26 | router-id = {{ required "missing openstack.routerID" .Values.openstack.routerID }} 27 | -------------------------------------------------------------------------------- /charts/kube-master/templates/_openstack-csi.config.tpl: -------------------------------------------------------------------------------- 1 | [Global] 2 | auth-url = {{ required "missing openstack.authURL" .Values.openstack.authURL }} 3 | domain-name = {{ required "missing openstack.domainName" .Values.openstack.domainName }} 4 | tenant-id = {{ required "missing openstack.projectID" .Values.openstack.projectID }} 5 | username = {{ required "missing openstack.username" .Values.openstack.username }} 6 | password = {{ required "missing openstack.password" .Values.openstack.password }} 7 | region = {{ required "missing openstack.region" .Values.openstack.region }} 8 | 9 | [BlockStorage] 10 | rescan-on-resize = yes 11 | -------------------------------------------------------------------------------- /charts/kube-master/templates/_openstack.config.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=gotexttmpl: */ -}} 2 | [Global] 3 | auth-url = {{ required "missing openstack.authURL" .Values.openstack.authURL }} 4 | username = {{ required "missing openstack.username" .Values.openstack.username }} 5 | password = {{ required "missing openstack.password" .Values.openstack.password }} 6 | domain-name = {{ required "missing openstack.domainName" .Values.openstack.domainName }} 7 | {{- if .Values.openstack.projectScope }} 8 | tenant-id = {{ .Values.openstack.projectID }} 9 | {{- end }} 10 | {{- if .Values.openstack.region }} 11 | region = {{ .Values.openstack.region }} 12 | {{- end }} 13 | [LoadBalancer] 14 | lb-version=v2 15 | use-octavia = {{ default "no" .Values.openstack.useOctavia }} 16 | subnet-id= {{ required "missing openstack.lbSubnetID" .Values.openstack.lbSubnetID }} 17 | floating-network-id= {{ required "missing openstack.lbFloatingNetworkID" .Values.openstack.lbFloatingNetworkID }} 18 | create-monitor = yes 19 | monitor-delay = 1m 20 | monitor-timeout = 30s 21 | monitor-max-retries = 3 22 | [BlockStorage] 23 | trust-device-path = no 24 | [Route] 25 | router-id = {{ required "missing openstack.routerID" .Values.openstack.routerID }} 26 | -------------------------------------------------------------------------------- /charts/kube-master/templates/api-vpa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "autoscaling.k8s.io/v1" 2 | kind: VerticalPodAutoscaler 3 | metadata: 4 | name: {{ include "master.fullname" . }}-apiserver 5 | spec: 6 | targetRef: 7 | {{- if .Capabilities.APIVersions.Has "apps/v1" }} 8 | apiVersion: "apps/v1" 9 | {{- else }} 10 | apiVersion: "extensions/v1beta1" 11 | {{- end }} 12 | kind: Deployment 13 | name: {{ include "master.fullname" . }}-apiserver 14 | resourcePolicy: 15 | containerPolicies: 16 | - containerName: apiserver 17 | controlledResources: ["cpu", "memory"] 18 | minAllowed: 19 | {{- toYaml .Values.api.resources.requests | nindent 12 }} 20 | {{- if .Values.api.resources.limits }} 21 | maxAllowed: 22 | {{- toYaml .Values.api.resources.limits | nindent 12 }} 23 | {{- end }} 24 | -------------------------------------------------------------------------------- /charts/kube-master/templates/secrets.yaml: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=gotexttmpl: */ -}} 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: {{ include "master.fullname" . }}-generated 6 | labels: 7 | chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" 8 | release: {{ .Release.Name }} 9 | type: Opaque 10 | data: 11 | {{- if .Values.openstack }} 12 | openstack.config: {{ include (print $.Template.BasePath "/_openstack.config.tpl") . | b64enc}} 13 | {{- if (semverCompare ">= 1.16-0" .Values.version.kubernetes) }} 14 | openstack-ccmanager.config: {{ include (print $.Template.BasePath "/_openstack-ccmanager.config.tpl") . | b64enc}} 15 | {{- end }} 16 | {{- if (semverCompare ">= 1.20-0" .Values.version.kubernetes) }} 17 | openstack-csi.config: {{ include (print $.Template.BasePath "/_openstack-csi.config.tpl") . | b64enc}} 18 | {{- end }} 19 | {{- end }} 20 | -------------------------------------------------------------------------------- /charts/kube-master/templates/service-metrics.yaml: -------------------------------------------------------------------------------- 1 | {{- if (semverCompare ">= 1.12-0" .Values.version.kubernetes) }} 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | labels: 6 | component: controller-manager-metrics 7 | release: {{ .Release.Name }} 8 | name: {{ .Release.Name }}-cm-met 9 | spec: 10 | ports: 11 | - name: metrics 12 | port: 10257 13 | protocol: TCP 14 | targetPort: 10257 15 | selector: 16 | component: controller-manager 17 | release: {{ .Release.Name }} 18 | --- 19 | apiVersion: v1 20 | kind: Service 21 | metadata: 22 | labels: 23 | component: scheduler-metrics 24 | release: {{ .Release.Name }} 25 | name: {{ .Release.Name }}-sched-met 26 | spec: 27 | ports: 28 | - name: metrics 29 | port: 10259 30 | protocol: TCP 31 | targetPort: 10259 32 | selector: 33 | component: scheduler 34 | release: {{ .Release.Name }} 35 | --- 36 | {{- end }} 37 | apiVersion: v1 38 | kind: Service 39 | metadata: 40 | labels: 41 | component: etcd-metrics 42 | release: {{ .Release.Name }} 43 | name: {{ .Release.Name }}-etcd-met 44 | spec: 45 | ports: 46 | - name: metrics 47 | port: 8081 48 | protocol: TCP 49 | targetPort: 8081 50 | selector: 51 | component: etcd 52 | release: {{ .Release.Name }} 53 | -------------------------------------------------------------------------------- /charts/kube-master/templates/service.yaml: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=gotexttmpl: */ -}} 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ include "master.fullname" . }} 6 | labels: 7 | component: apiserver 8 | chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" 9 | release: {{ .Release.Name }} 10 | spec: 11 | type: ClusterIP 12 | ports: 13 | - name: apiserver 14 | port: {{ .Values.advertisePort }} 15 | - name: wormhole 16 | port: 6553 17 | selector: 18 | app: {{ include "master.fullname" . }}-apiserver 19 | -------------------------------------------------------------------------------- /charts/kube-master/test-values.yaml: -------------------------------------------------------------------------------- 1 | domain: xyz.com 2 | bootstrapToken: xyz 3 | nodePassword: xyz 4 | secretName: test 5 | openstack: 6 | authURL: http://xyz.com 7 | username: xyz 8 | password: xyz 9 | domainName: xyz.com 10 | lbSubnetID: xyz 11 | lbFloatingNetworkID: xyz 12 | routerID: xyz 13 | projectID: xy 14 | region: xy-xy-1 15 | projectDomainName: xyz 16 | api: 17 | apiserverHost: xyz.com 18 | wormholeHost: xyz.com 19 | version: 20 | kubernikus: xyz 21 | kubernetes: 1.0.0 22 | etcd: 23 | backup: 24 | storageProvider: Swift 25 | secure: 26 | enabled: true 27 | openstack: 28 | authURL: http://xyz.com 29 | username: xyz 30 | password: xyz 31 | domainName: xyz.com 32 | projectID: xyz 33 | images: 34 | etcd: 35 | repository: abc 36 | tag: xyz 37 | etcdBackup: 38 | repository: abc 39 | tag: xyz 40 | version: 41 | kubernikus: xyz 42 | kubernetes: 1.0.0 43 | images: 44 | hyperkube: 45 | repository: abc 46 | tag: xyz 47 | wormhole: 48 | repository: abc 49 | etcd: 50 | repository: abc 51 | tag: xyz 52 | etcdBackup: 53 | repository: abc 54 | tag: xyz 55 | fluentd: 56 | repository: abc 57 | tag: xyz 58 | recycler: 59 | repository: abc 60 | tag: xyz 61 | 62 | dashboard: 63 | enabled: false 64 | dex: 65 | enabled: false 66 | 67 | audit: "swift" 68 | -------------------------------------------------------------------------------- /charts/kubernikus-dex/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "1.0" 3 | description: Install global dex related credentials needed for kubernikus clusters 4 | name: kubernikus-dex 5 | version: 0.1.6 6 | -------------------------------------------------------------------------------- /charts/kubernikus-dex/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{- define "dex.url" -}} 2 | {{- printf "*.%s.%s.%s" .Values.dex.dns.zone .Values.global.region .Values.global.tld -}} 3 | {{- end -}} 4 | -------------------------------------------------------------------------------- /charts/kubernikus-dex/templates/dex-ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" }} 2 | apiVersion: networking.k8s.io/v1 3 | {{- else }} 4 | apiVersion: networking.k8s.io/v1beta1 5 | {{- end }} 6 | kind: Ingress 7 | metadata: 8 | annotations: 9 | kubernetes.io/tls-acme: "true" 10 | kubernetes.io/ingress.class: "nginx" 11 | name: kubernikus-dex 12 | namespace: {{ default .Release.Namespace .Values.namespaceOverride }} 13 | spec: 14 | rules: 15 | - host: {{ include "dex.url" . | quote }} 16 | tls: 17 | - hosts: 18 | - {{ include "dex.url" . | quote }} 19 | secretName: kubernikus-dex 20 | --- 21 | apiVersion: v1 22 | kind: Secret 23 | metadata: 24 | name: kubernikus-dex 25 | type: Opaque 26 | -------------------------------------------------------------------------------- /charts/kubernikus-dex/templates/keystone-secret.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.dex.connectors.keystone.enabled }} 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: kubernikus-dex-keystone 6 | namespace: {{ default .Release.Namespace .Values.namespaceOverride }} 7 | annotations: 8 | cloud.sap/inject-secrets: "true" 9 | type: Opaque 10 | data: 11 | adminUsername: {{ required "api.adminUser undefined" .Values.api.adminUser | b64enc | quote}} 12 | adminPassword: {{ required "api.adminPassword undefined" .Values.api.adminPassword | b64enc | quote}} 13 | adminUserDomain: {{ required "api.adminUserDomain undefined" .Values.api.adminUserDomain | b64enc | quote}} 14 | adminProject: {{ required "openstack.api.adminProject undefined" .Values.api.adminProject | b64enc | quote }} 15 | adminDomain: {{ required "openstack.api.adminDomain undefined" .Values.api.adminDomain | b64enc | quote }} 16 | {{ end }} 17 | -------------------------------------------------------------------------------- /charts/kubernikus-dex/templates/ldap-secret.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.dex.connectors.ldap.enabled }} 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: kubernikus-dex-ldap 6 | namespace: {{ default .Release.Namespace .Values.namespaceOverride }} 7 | type: Opaque 8 | data: 9 | host: {{ required ".Values.dex.connectors.ldap.config.host undefined" .Values.dex.connectors.ldap.config.host | b64enc | quote}} 10 | bindDN: {{ required ".Values.dex.connectors.ldap.config.bindDN undefined" .Values.dex.connectors.ldap.config.bindDN | b64enc | quote}} 11 | bindPW: {{ required ".Values.dex.connectors.ldap.config.bindPW undefined" .Values.dex.connectors.ldap.config.bindPW | b64enc | quote}} 12 | userSearchBaseDN: {{ required ".Values.dex.connectors.ldap.userSearch.baseDN undefined" .Values.dex.connectors.ldap.userSearch.baseDN | b64enc | quote}} 13 | userSearchFilter: {{ required ".Values.dex.connectors.ldap.userSearch.filter undefined" .Values.dex.connectors.ldap.userSearch.filter | b64enc | quote}} 14 | groupSearchBaseDN: {{ required ".Values.dex.connectors.ldap.groupSearch.baseDN undefined" .Values.dex.connectors.ldap.groupSearch.baseDN | b64enc | quote}} 15 | groupSearchFilter: {{ required ".Values.dex.connectors.ldap.groupSearch.filter undefined" .Values.dex.connectors.ldap.groupSearch.filter | b64enc | quote}} 16 | {{ end }} 17 | -------------------------------------------------------------------------------- /charts/kubernikus-dex/test-values.yaml: -------------------------------------------------------------------------------- 1 | # secrets/$REGION/values/global.yaml 2 | global: 3 | region: xyz 4 | tld: xyz.com 5 | 6 | dex: 7 | connectors: 8 | keystone: 9 | enabled: true 10 | ldap: 11 | enabled: false 12 | dns: 13 | zone: xyz 14 | 15 | api: 16 | adminPassword: xyz 17 | adminUser: xyz 18 | adminUserDomain: xyz 19 | adminProject: xyz 20 | adminDomain: xyz -------------------------------------------------------------------------------- /charts/kubernikus-dex/values.yaml: -------------------------------------------------------------------------------- 1 | # secrets/$REGION/values/global.yaml 2 | #global: 3 | # region: eu-nl-1 4 | # tld: cloud.sap 5 | #namespaceOverride: 6 | 7 | dex: 8 | connectors: 9 | keystone: 10 | enabled: true 11 | ldap: 12 | enabled: false 13 | #config: 14 | # host: 15 | # bindDN: 16 | # bindPW: 17 | #userSearch: 18 | # baseDN: 19 | # filter: 20 | #groupSearch: 21 | # baseDN: 22 | # filter: 23 | 24 | dns: 25 | zone: ingress.kubernikus 26 | 27 | api: 28 | # adminPassword: # from secrets/$REGION/values/keystone.yaml 29 | # adminUsername: admin # from secrets/$REGION/values/keystone.yaml 30 | adminUserDomain: Default 31 | adminProject: admin 32 | adminDomain: Default 33 | -------------------------------------------------------------------------------- /charts/kubernikus/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: k8sniff 3 | repository: file://../k8sniff 4 | version: 0.2.0 5 | - name: rbac 6 | repository: file://vendor/rbac 7 | version: 0.1.6 8 | - name: owner-info 9 | repository: file://vendor/owner-info 10 | version: 0.2.0 11 | digest: sha256:0111c6527e67624cf7869d4a3af5f58dfc2193020266b13c1feaf5b715043207 12 | generated: "2023-05-24T09:26:58.683150792+02:00" 13 | -------------------------------------------------------------------------------- /charts/kubernikus/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | description: A Helm chart for Kubernetes 3 | name: kubernikus 4 | type: application 5 | version: 0.3.23 6 | dependencies: 7 | - name: k8sniff 8 | repository: file://../k8sniff 9 | version: 0.2.0 10 | condition: k8sniff.enabled 11 | - name: rbac 12 | repository: file://vendor/rbac 13 | version: 0.1.6 14 | condition: includeRBAC 15 | - name: owner-info 16 | repository: file://vendor/owner-info 17 | version: 0.2.0 18 | -------------------------------------------------------------------------------- /charts/kubernikus/templates/_helpers.url: -------------------------------------------------------------------------------- 1 | {{- define "oidc.issuer" -}} 2 | {{- printf "auth.%s" .Values.domain -}} 3 | {{- end -}} 4 | -------------------------------------------------------------------------------- /charts/kubernikus/templates/api-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: kubernikus-api 5 | spec: 6 | selector: 7 | app: kubernikus 8 | type: api 9 | ports: 10 | - port: {{ .Values.api.port }} 11 | -------------------------------------------------------------------------------- /charts/kubernikus/templates/clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.standalone}} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: kubernikus-default 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: cluster-admin 10 | subjects: 11 | - kind: ServiceAccount 12 | name: default 13 | namespace: kubernikus-system 14 | {{- end }} 15 | -------------------------------------------------------------------------------- /charts/kubernikus/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" }} 2 | apiVersion: networking.k8s.io/v1 3 | {{- else }} 4 | apiVersion: networking.k8s.io/v1beta1 5 | {{- end }} 6 | kind: Ingress 7 | 8 | metadata: 9 | name: kubernikus-api 10 | annotations: 11 | {{- if .Values.linkerd.enabled }} 12 | nginx.ingress.kubernetes.io/service-upstream: "true" 13 | {{- end }} 14 | {{- range $key, $val := .Values.ingress.annotations }} 15 | {{- if typeIs "string" $val }} 16 | {{ $key }}: {{ $val | quote }} 17 | {{- end }} 18 | {{- end}} 19 | 20 | spec: 21 | tls: 22 | - secretName: kubernikus-api 23 | hosts: [{{ required "domain missing" .Values.domain }}] 24 | rules: 25 | - host: {{ required "domain missing" .Values.domain }} 26 | http: 27 | paths: 28 | - path: / 29 | {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" }} 30 | pathType: Prefix 31 | {{- end }} 32 | backend: 33 | {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" }} 34 | service: 35 | name: kubernikus-api 36 | port: 37 | number: {{ .Values.api.port }} 38 | {{- else }} 39 | serviceName: kubernikus-api 40 | servicePort: {{ .Values.api.port }} 41 | {{- end }} 42 | -------------------------------------------------------------------------------- /charts/kubernikus/templates/secret-api.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.api.tls_crt}} 2 | apiVersion: v1 3 | kind: Secret 4 | type: Opaque 5 | 6 | metadata: 7 | name: kubernikus-api 8 | 9 | data: 10 | tls.crt: {{ required "api.tls_crt undefined" .Values.api.tls_crt | b64enc | quote }} 11 | tls.key: {{ required "api.tls_key undefined" .Values.api.tls_key | b64enc | quote }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/kubernikus/templates/secret-operator.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.openstack.auth_url -}} 2 | apiVersion: v1 3 | kind: Secret 4 | type: Opaque 5 | 6 | metadata: 7 | name: kubernikus-operator 8 | 9 | data: 10 | authURL: {{ required "openstack.auth_url undefined" .Values.openstack.auth_url | trimSuffix "/" | trimSuffix "/v3" | printf "%s/v3" | b64enc }} 11 | username: {{ required "openstack.auth_user_id undefined" .Values.openstack.auth_user_id | b64enc }} 12 | password: {{ required "openstack.auth_user_password undefined" .Values.openstack.auth_user_password | b64enc }} 13 | userDomain: {{ required "openstack.auth_domain undefined" .Values.openstack.auth_domain | b64enc }} 14 | project: {{ required "openstack.auth_project undefined" .Values.openstack.auth_project | b64enc }} 15 | projectDomain: {{ required "openstack.auth_project_domain undefined" .Values.openstack.auth_project_domain | b64enc }} 16 | {{- end }} 17 | 18 | 19 | -------------------------------------------------------------------------------- /charts/kubernikus/templates/serviceaccounts.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.useServiceAccount -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: "kubernikus-operator" 6 | --- 7 | apiVersion: v1 8 | kind: ServiceAccount 9 | metadata: 10 | name: "kubernikus-api" 11 | {{- end }} 12 | -------------------------------------------------------------------------------- /charts/kubernikus/test-values.yaml: -------------------------------------------------------------------------------- 1 | domain: xyz.com 2 | openstack: 3 | auth_url: http://xyz.com 4 | auth_user_id: xyz 5 | auth_user_password: xyz 6 | auth_domain: xyz 7 | auth_project: xyz 8 | auth_project_domain: xyz 9 | region: xy-xy-1 10 | dex: 11 | clientSecret: uhu-ah-secret 12 | ldap: 13 | config: 14 | host: ldap.host.xyz 15 | bindDN: urks=burks 16 | bindPW: ldap-pass 17 | userSearch: 18 | baseDN: user=looser 19 | filter: hase=nase 20 | groupSearch: 21 | baseDN: group=pup 22 | filter: igel=fuchs 23 | connectors: 24 | - name: ldap 25 | id: ldap1 26 | projectID: project1 27 | - name: ldap2 28 | id: ldap2 29 | projectID: project2 30 | operator: 31 | resources: 32 | limits: 33 | cpu: 100m 34 | memory: 128Mi 35 | api: 36 | resources: 37 | limits: 38 | cpu: 200m 39 | memory: 256Mi 40 | -------------------------------------------------------------------------------- /charts/kubernikus/vendor/owner-info/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | description: owner-info deploys a ConfigMap that contains info about chart's owner. 3 | It is meant to be used as a dependency by other charts. 4 | name: owner-info 5 | version: 0.2.0 6 | -------------------------------------------------------------------------------- /charts/kubernikus/vendor/owner-info/README.md: -------------------------------------------------------------------------------- 1 | # owner-info 2 | 3 | This chart deploys a ConfigMap that contains owner info about a chart. 4 | 5 | This chart **should not** be deployed stand-alone, it is meant to be used as a dependency 6 | by other charts. 7 | 8 | **Caveat:** Only use `owner-info` for the top-level chart (i.e. the chart that you're 9 | deploying). If you use it for any dependencies of your top-level chart then you will get 10 | multiple ConfigMaps with name clash. 11 | 12 | ## Usage 13 | 14 | Add `owner-info` as a dependency to your chart's `Chart.yaml` file: 15 | 16 | ```yaml 17 | dependencies: 18 | - name: owner-info 19 | repository: https://charts.eu-de-2.cloud.sap 20 | version: # use owner-info's current version from Chart.yaml 21 | ``` 22 | 23 | then run: 24 | 25 | ```sh 26 | $ helm dep update 27 | ``` 28 | 29 | ## Configuration 30 | 31 | The following table lists the configurable parameters of the `owner-info` chart and their default values. 32 | 33 | | Parameter | Default | Description | 34 | | --- | --- | --- | 35 | | `maintainers` | `[]` | List of people that maintain your chart. The list should be ordered by priority, i.e. primary maintainer should be at the top. | 36 | | `helm-chart-url` | `WHERE-TO-FIND-THE-CHART-IN-GITHUB` | URL to your chart in github, e.g. `https://github.com/sapcc/helm-charts/tree/master/common/owner-info` | 37 | -------------------------------------------------------------------------------- /charts/kubernikus/vendor/owner-info/ci/test-values.yaml: -------------------------------------------------------------------------------- 1 | helm-chart-url: https://github.com/sapcc/helm-charts/tree/master/common/owner-info 2 | support-group: test 3 | -------------------------------------------------------------------------------- /charts/kubernikus/vendor/owner-info/values.yaml: -------------------------------------------------------------------------------- 1 | # "helm-chart-url" must be a HTTP(S) URL describing where to find the Helm chart in GitHub etc. 2 | helm-chart-url: '' 3 | # "maintainers" is an optional list of names of all people who maintain the Helm chart. If 4 | # multiple people can help with issues regarding the chart, feel free to include as many names 5 | # as you like. 6 | maintainers: [] 7 | 8 | # "support-group" is required for routing alerts/tickets regarding this 9 | # deployment to the right support group 10 | support-group: '' 11 | # "service" is optional and allows sorting of alerts/tickets within the realm 12 | # of a single support group 13 | service: '' 14 | -------------------------------------------------------------------------------- /charts/kubernikus/vendor/rbac/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | description: A Helm chart for Kubernetes 3 | name: rbac 4 | type: application 5 | version: 0.1.6 6 | -------------------------------------------------------------------------------- /charts/kubernikus/vendor/rbac/values.yaml: -------------------------------------------------------------------------------- 1 | #name: (default: .Release.Name) 2 | #saNamespace: (default: .Release.Namespace) 3 | #roleNamespace: (default: .Release.Namespace) 4 | -------------------------------------------------------------------------------- /charts/seed/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/seed/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: seed 3 | description: Kubernikus Seed Chart 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | # It is recommended to use it with quotes. 24 | appVersion: "1.16.0" 25 | -------------------------------------------------------------------------------- /charts/seed/templates/storageclasses.yaml: -------------------------------------------------------------------------------- 1 | {{- define "seed.storage-provisioner" -}} 2 | {{- if semverCompare ">= 1.20-0" .Capabilities.KubeVersion.Version -}} 3 | cinder.csi.openstack.org 4 | {{- else -}} 5 | kubernetes.io/cinder 6 | {{- end -}} 7 | {{- end -}} 8 | {{- define "seed.storage-expansion" -}} 9 | {{- if semverCompare ">= 1.20-0" .Capabilities.KubeVersion.Version -}} 10 | true 11 | {{- else -}} 12 | false 13 | {{- end -}} 14 | {{- end -}} 15 | 16 | {{- if and (not .Values.seedKubeadm) (not .Values.seedVirtual) .Values.openstack -}} 17 | 18 | {{- range $az := .Values.openstack.azs -}} 19 | apiVersion: storage.k8s.io/v1 20 | kind: StorageClass 21 | metadata: 22 | name: cinder-zone-{{ $az | trunc -1 }} 23 | provisioner: {{ template "seed.storage-provisioner" $ }} 24 | allowVolumeExpansion: {{ template "seed.storage-expansion" $ }} 25 | volumeBindingMode: Immediate 26 | parameters: 27 | availability: {{ $az }} 28 | --- 29 | {{- end -}} 30 | 31 | apiVersion: storage.k8s.io/v1 32 | kind: StorageClass 33 | metadata: 34 | name: cinder-default 35 | annotations: 36 | storageclass.kubernetes.io/is-default-class: "true" 37 | provisioner: {{ template "seed.storage-provisioner" $ }} 38 | allowVolumeExpansion: {{ template "seed.storage-expansion" $ }} 39 | volumeBindingMode: WaitForFirstConsumer 40 | 41 | {{- end -}} 42 | -------------------------------------------------------------------------------- /charts/seed/test-values.yaml: -------------------------------------------------------------------------------- 1 | dns: 2 | kube: false 3 | images: 4 | wormhole: 5 | registry: reg 6 | tag: tag 7 | kubeProxy: 8 | registry: reg 9 | tag: tag 10 | coreDNS: 11 | registry: reg 12 | tag: tag 13 | cniPlugins: 14 | registry: reg 15 | tag: tag 16 | flannelCNIPlugin: 17 | registry: reg 18 | tag: tag 19 | flannel: 20 | registry: reg 21 | tag: tag 22 | -------------------------------------------------------------------------------- /ci/Makefile: -------------------------------------------------------------------------------- 1 | FLY ?= fly7 -t services 2 | 3 | default: clean pipeline.yaml 4 | $(FLY) set-pipeline -p kubernikus -c pipeline.yaml 5 | 6 | clean: FORCE 7 | if [ -e pipeline.yaml ]; then rm pipeline.yaml; fi 8 | 9 | %.yaml: %.yaml.erb 10 | if [ -e $@ ]; then rm $@; fi 11 | erb $< > $@ 12 | 13 | FORCE: 14 | -------------------------------------------------------------------------------- /ci/task_build_args.yaml: -------------------------------------------------------------------------------- 1 | platform: linux 2 | image_resource: 3 | type: registry-image 4 | source: 5 | repository: keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/alpine 6 | tag: latest 7 | inputs: 8 | - name: docs-builder.image 9 | - name: kubernikus.git 10 | outputs: 11 | - name: kubernikus.git 12 | run: 13 | path: sh 14 | args: 15 | - -exc 16 | - | 17 | SHA=$(cat kubernikus.git/.git/ref) 18 | DIGEST=$(cat docs-builder.image/digest) 19 | echo "VERSION=$SHA" > kubernikus.git/build-args.txt 20 | echo "DOCS_IMAGE=keppel.eu-de-1.cloud.sap/ccloud/kubernikus-docs-builder@$DIGEST" >> kubernikus.git/build-args.txt 21 | -------------------------------------------------------------------------------- /ci/task_checksum.yaml: -------------------------------------------------------------------------------- 1 | platform: 'linux' 2 | 3 | image_resource: 4 | type: registry-image 5 | source: 6 | repository: keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/golang 7 | tag: 1.23-alpine3.21 8 | 9 | inputs: 10 | - name: kubernikus.builds 11 | path: gopath/src/github.com/sapcc/kubernikus 12 | outputs: 13 | - name: checksum 14 | 15 | run: 16 | path: /bin/sh 17 | args: 18 | - -c 19 | - | 20 | set -exo pipefail 21 | if [ -z "$TARGET" ]; then 22 | echo TARGET not set 23 | exit 1 24 | fi 25 | export GOPATH=$PWD/gopath 26 | OUTPUT=$PWD/checksum 27 | cd gopath/src/github.com/sapcc/kubernikus 28 | apk add --no-cache make bash 29 | make $TARGET VERSION=latest 30 | CHECKSUM=$(sha256sum $TARGET | cut -f1 -d ' ') 31 | echo checksum=$CHECKSUM > $OUTPUT/properties 32 | params: 33 | TARGET: 34 | -------------------------------------------------------------------------------- /ci/task_github_compare_url.yaml: -------------------------------------------------------------------------------- 1 | platform: 'linux' 2 | 3 | image_resource: 4 | type: registry-image 5 | source: 6 | repository: keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/alpine 7 | tag: latest 8 | inputs: 9 | - name: kubernikus.builds 10 | outputs: 11 | 12 | run: 13 | path: /bin/sh 14 | args: 15 | - -ec 16 | - | 17 | VERSION=$(cat kubernikus.builds/.git/HEAD) 18 | echo "Go to this link to see the changes in master since the last prod release:" 19 | echo 20 | echo " https://github.com/sapcc/kubernikus/compare/${VERSION}...master" 21 | 22 | -------------------------------------------------------------------------------- /ci/task_helm_seed.yaml: -------------------------------------------------------------------------------- 1 | platform: linux 2 | 3 | image_resource: 4 | type: registry-image 5 | source: 6 | repository: keppel.eu-de-1.cloud.sap/ccloud/unified-kubernetes-toolbox 7 | tag: 'latest' 8 | 9 | inputs: 10 | - name: secrets.git 11 | - name: helm-charts.git 12 | 13 | run: 14 | path: sh 15 | args: 16 | - -exc 17 | - | 18 | set -o pipefail 19 | 20 | vault-injector interpolate secrets.git/global/values/kubernikus-seed.yaml secrets.git/$REGION/values/globals.yaml 21 | 22 | helm dependency build helm-charts.git/openstack/kubernikus --debug 23 | 24 | helm upgrade $RELEASE helm-charts.git/openstack/kubernikus --namespace $NAMESPACE --values secrets.git/global/values/kubernikus-seed.yaml --values secrets.git/$REGION/values/globals.yaml --install 25 | 26 | params: 27 | REGION: 28 | CONTEXT: 29 | KUBELOGON_USER: 30 | KUBELOGON_PASSWORD: 31 | NAMESPACE: monsoon3 32 | RELEASE: kubernikus 33 | VAULT_ADDR: https://vault.global.cloud.sap # DO NOT CHANGE 34 | VAULT_KV_ENGINE: secrets # DO NOT CHANGE 35 | VAULT_ROLE_ID: # (required) set this to ((auth.role_id)) to receive credentials automatically from Concourse 36 | VAULT_SECRET_ID: # (required) set this to ((auth.secret_id)) to receive credentials automatically from Concourse 37 | -------------------------------------------------------------------------------- /ci/task_oci_build.yaml: -------------------------------------------------------------------------------- 1 | platform: linux 2 | image_resource: 3 | type: registry-image 4 | source: 5 | repository: keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/concourse/oci-build-task 6 | tag: 0.8.1 7 | 8 | inputs: 9 | - name: context 10 | 11 | outputs: 12 | - name: image 13 | 14 | caches: 15 | - path: cache 16 | 17 | run: 18 | #path: build 19 | path: sh 20 | args: 21 | - -ec 22 | - | 23 | echo Executing on $(wget -q -O- http://169.254.169.254/latest/meta-data/local-hostname) 24 | du -hs cache/ 25 | build 26 | params: 27 | DEBUG: true 28 | DOCKERFILE: 29 | CONTEXT: context 30 | BUILD_ARGS_FILE: 31 | -------------------------------------------------------------------------------- /cmd/kubernikus/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/sapcc/kubernikus/pkg/cmd" 8 | "github.com/sapcc/kubernikus/pkg/cmd/kubernikus" 9 | ) 10 | 11 | func main() { 12 | baseName := filepath.Base(os.Args[0]) 13 | err := kubernikus.NewCommand(baseName).Execute() 14 | cmd.CheckError(err) 15 | } 16 | -------------------------------------------------------------------------------- /cmd/kubernikusctl/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | goflag "flag" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/sapcc/kubernikus/pkg/cmd" 9 | "github.com/sapcc/kubernikus/pkg/cmd/kubernikusctl" 10 | ) 11 | 12 | func main() { 13 | if f := goflag.Lookup("logtostderr"); f != nil { 14 | f.Value.Set("true") 15 | } 16 | 17 | baseName := filepath.Base(os.Args[0]) 18 | 19 | err := kubernikusctl.NewCommand(baseName).Execute() 20 | cmd.CheckError(err) 21 | } 22 | -------------------------------------------------------------------------------- /cmd/wormhole/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/sapcc/kubernikus/pkg/cmd" 8 | "github.com/sapcc/kubernikus/pkg/cmd/wormhole" 9 | ) 10 | 11 | func main() { 12 | baseName := filepath.Base(os.Args[0]) 13 | 14 | err := wormhole.NewCommand(baseName).Execute() 15 | cmd.CheckError(err) 16 | } 17 | -------------------------------------------------------------------------------- /contrib/all/Dockerfile.apiserver: -------------------------------------------------------------------------------- 1 | ARG IMAGE 2 | 3 | FROM keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/golang:1.23-alpine3.21 as builder 4 | 5 | WORKDIR / 6 | ADD api-liveness.go . 7 | RUN CGO_ENABLED=0 go build -o /api-liveness /api-liveness.go 8 | 9 | FROM keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/alpine AS socat 10 | RUN apk --update add build-base bash automake git curl linux-headers 11 | ARG SOCAT_VERSION=1.7.4.2 12 | WORKDIR /build 13 | RUN curl -LO http://www.dest-unreach.org/socat/download/socat-${SOCAT_VERSION}.tar.gz \ 14 | && tar xzvf socat-${SOCAT_VERSION}.tar.gz \ 15 | && cd socat-${SOCAT_VERSION} \ 16 | && CC='/usr/bin/gcc -static' CFLAGS='-fPIC' CPPFLAGS='-I/build -DNETDB_INTERNAL=-1' ./configure \ 17 | && make -j4 \ 18 | && strip socat \ 19 | && mv socat /socat 20 | 21 | FROM $IMAGE 22 | COPY --from=builder /api-liveness /api-liveness 23 | COPY --from=socat /socat /usr/bin/socat 24 | RUN ["socat", "-V"] 25 | LABEL source_repository="https://github.com/kubernetes/kubernetes" 26 | -------------------------------------------------------------------------------- /contrib/all/Dockerfile.etcd: -------------------------------------------------------------------------------- 1 | ARG IMAGE 2 | FROM $IMAGE as etcd 3 | 4 | FROM alpine:3.21 5 | LABEL source_repository="https://github.com/sapcc/kubernikus" 6 | ENV PATH "$PATH:/usr/local/bin" 7 | 8 | COPY --from=etcd /usr/local/bin/etcd /usr/local/bin/etcd 9 | COPY --from=etcd /usr/local/bin/etcdctl /usr/local/bin/etcdctl 10 | RUN apk add --no-cache bash curl wget 11 | 12 | WORKDIR / 13 | ENTRYPOINT ["/usr/local/bin/etcd"] 14 | -------------------------------------------------------------------------------- /contrib/all/Makefile: -------------------------------------------------------------------------------- 1 | VERSION?=v1.25.3 2 | REGISTRY?=registry.k8s.io 3 | IMAGE?=keppel.eu-de-1.cloud.sap/ccloud 4 | ARCH?=amd64 5 | 6 | # see https://github.com/kubernetes/release/blob/master/images/build/debian-base/variants.yaml 7 | BASE_VERSION?=bullseye-v1.4.1 8 | BASE_IMAGE?=registry.k8s.io/build-image/debian-base:${BASE_VERSION} 9 | 10 | CNI_PLUGINS_RELEASE=v1.0.1 11 | 12 | FLANNEL_VERSION=v0.17.0 13 | COREDNS_VERSION=1.9.1 14 | 15 | ETCD_IMAGE=gcr.io/etcd-development/etcd 16 | ETCD_VERSION=v3.4.35 17 | 18 | OPTS?=--network=host 19 | 20 | all: build push 21 | 22 | build: 23 | docker build ${OPTS} -t ${IMAGE}/kube-apiserver:${VERSION} --build-arg IMAGE=${REGISTRY}/kube-apiserver:${VERSION} -f Dockerfile.apiserver . 24 | docker build ${OPTS} -t ${IMAGE}/kubelet:${VERSION} --build-arg BASE_IMAGE=${BASE_IMAGE} --build-arg KUBERNETES_VERSION=${VERSION} --build-arg ARCH=${ARCH} --build-arg CNI_PLUGINS_RELEASE=${CNI_PLUGINS_RELEASE} - < Dockerfile.kubelet 25 | 26 | push: 27 | docker push ${IMAGE}/kube-apiserver:${VERSION} 28 | docker push ${IMAGE}/kubelet:${VERSION} 29 | 30 | etcd: 31 | docker build ${OPTS} -t ${IMAGE}/etcd:${ETCD_VERSION} --build-arg IMAGE=${ETCD_IMAGE}:${ETCD_VERSION} - < Dockerfile.etcd 32 | 33 | etcd-push: 34 | docker push ${IMAGE}/etcd:${ETCD_VERSION} 35 | 36 | .PHONY: all build push 37 | -------------------------------------------------------------------------------- /contrib/cni-plugins/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | ARG TARGETARCH 3 | ARG CNI_PLUGINS_RELEASE 4 | 5 | LABEL source_repository=https://github.com/sapcc/kubernikus/tree/master/contrib/cni-plugins 6 | WORKDIR /cni-plugins 7 | RUN mkdir -p /cni-plugins && \ 8 | wget -O- https://github.com/containernetworking/plugins/releases/download/${CNI_PLUGINS_RELEASE}/cni-plugins-linux-${TARGETARCH}-${CNI_PLUGINS_RELEASE}.tgz | tar -xz 9 | -------------------------------------------------------------------------------- /contrib/cni-plugins/Makefile: -------------------------------------------------------------------------------- 1 | CNI_PLUGINS_RELEASE=v1.1.1 2 | IMAGE?=keppel.eu-de-1.cloud.sap/ccloud 3 | ARCH?=amd64 4 | 5 | 6 | build: 7 | docker build -t ${IMAGE}/cni-plugins:${CNI_PLUGINS_RELEASE} --build-arg CNI_PLUGINS_RELEASE=${CNI_PLUGINS_RELEASE} . 8 | 9 | push: 10 | docker push ${IMAGE}/cni-plugins:${CNI_PLUGINS_RELEASE} 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /contrib/fluentd/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/fluent/fluentd:v1.14.6-1.1 2 | 3 | LABEL source_repository=https://github.com/fluent/fluentd 4 | USER root 5 | RUN fluent-gem install fluent-plugin-elasticsearch fluent-plugin-openstack 6 | # Fix OpenStack Auth in Fluentd plugin 7 | RUN apk add --no-cache patch 8 | COPY auth_options.diff / 9 | RUN patch /usr/lib/ruby/gems/2.7.0/gems/fluent-plugin-openstack-2.0.1/lib/fluent/plugin/out_swift.rb auth_options.diff && rm auth_options.diff 10 | USER fluent 11 | -------------------------------------------------------------------------------- /contrib/fluentd/Makefile: -------------------------------------------------------------------------------- 1 | IMAGE:= keppel.eu-de-1.cloud.sap/ccloud/kubernikus-fluentd 2 | VERSION := v1.14-1 3 | 4 | build: 5 | docker build --build-arg VERSION=$(VERSION) -t $(IMAGE):$(VERSION) . 6 | push: 7 | docker push $(IMAGE):$(VERSION) 8 | -------------------------------------------------------------------------------- /contrib/k8sniff/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/golang:1.23-alpine3.21 2 | 3 | WORKDIR /go/src/github.com/kubermatic/k8sniff 4 | 5 | RUN apk add --no-cache curl git \ 6 | && curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 7 | ARG VERSION=master 8 | RUN git clone https://github.com/kubermatic/k8sniff.git . \ 9 | && git checkout $VERSION 10 | 11 | RUN dep ensure 12 | 13 | RUN go build -v -o k8sniff . 14 | 15 | FROM alpine:3.21 16 | LABEL source_repository="https://github.com/sapcc/kubernikus" 17 | 18 | RUN apk add --no-cache ca-certificates 19 | COPY --from=0 /go/src/github.com/kubermatic/k8sniff /pipeline/source/k8sniff 20 | -------------------------------------------------------------------------------- /contrib/k8sniff/Makefile: -------------------------------------------------------------------------------- 1 | IMAGE:= sapcc/k8sniff 2 | VERSION := e7435d989925e8559b0e5ca26da69f84a1035c32 3 | 4 | build: 5 | docker build --build-arg VERSION=$(VERSION) -t $(IMAGE):$(VERSION) . 6 | push: 7 | docker push $(IMAGE):$(VERSION) 8 | -------------------------------------------------------------------------------- /contrib/kubernikus-changelog-builder/.github_changelog_generator: -------------------------------------------------------------------------------- 1 | project=kubernikus 2 | user=sapcc 3 | exclude_tags_regex=v1.0.0+.+ 4 | output=/host/CHANGELOG.md 5 | -------------------------------------------------------------------------------- /contrib/kubernikus-changelog-builder/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.21 2 | LABEL source_repository="https://github.com/sapcc/kubernikus" 3 | 4 | ENV GITHUB_CHANGELOG_GENERATOR_VERSION "1.14.3" 5 | 6 | RUN apk --no-cache add ruby ruby-json libstdc++ tzdata bash ca-certificates 7 | RUN echo 'gem: --no-document' > /etc/gemrc 8 | RUN gem install github_changelog_generator --version $GITHUB_CHANGELOG_GENERATOR_VERSION 9 | 10 | COPY .github_changelog_generator / 11 | 12 | CMD github_changelog_generator --token $GITHUB_TOKEN 13 | -------------------------------------------------------------------------------- /contrib/kubernikus-changelog-builder/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/sh 2 | DATE := $(shell date +%Y%m%d%H%M%S) 3 | VERSION ?= v$(DATE) 4 | IMAGE := sapcc/kubernikus-changelog-builder 5 | 6 | GITHUB_CHANGELOG_GENERATOR_VERSION := 1.14.3 7 | BUILD_ARGS = --build-arg GITHUB_CHANGELOG_GENERATOR_VERSION=$(GITHUB_CHANGELOG_GENERATOR_VERSION) 8 | 9 | .PHONY: all 10 | all: build push 11 | 12 | build: 13 | docker build $(BUILD_ARGS) -t $(IMAGE):$(VERSION) -t $(IMAGE):latest . 14 | 15 | push: 16 | docker push $(IMAGE):$(VERSION) 17 | docker push $(IMAGE):latest 18 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/alpine:3.21 2 | LABEL source_repository="https://github.com/sapcc/kubernikus" 3 | 4 | ARG HUGO_VERSION=0.30.2 5 | 6 | RUN apk add --no-cache curl 7 | RUN curl -Lo hugo.tar.gz https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.tar.gz 8 | RUN tar xvf hugo.tar.gz hugo -C /usr/local/bin 9 | RUN curl -Lo /usr/local/bin/yaml2json https://github.com/bronze1man/yaml2json/releases/download/v1.2/yaml2json_linux_amd64 && \ 10 | chmod +x /usr/local/bin/yaml2json 11 | COPY data / 12 | 13 | ONBUILD COPY docs /content 14 | ONBUILD RUN hugo --baseURL "/docs" --destination /public/docs 15 | ONBUILD RUN hugo --baseURL "/kubernikus" --destination /public/kubernikus 16 | 17 | ONBUILD COPY swagger.yml / 18 | ONBUILD RUN yaml2json < /swagger.yml > /public/docs/api/swagger.json 19 | ONBUILD RUN yaml2json < /swagger.yml > /public/kubernikus/api/swagger.json 20 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/sh 2 | DATE := $(shell date +%Y%m%d%H%M%S) 3 | VERSION ?= v$(DATE) 4 | IMAGE := sapcc/kubernikus-docs-builder 5 | 6 | HUGO_VERSION := 0.30.2 # Without v 7 | BUILD_ARGS = --build-arg HUGO_VERSION=$(HUGO_VERSION) 8 | 9 | .PHONY: all 10 | all: build push 11 | 12 | build: 13 | docker build $(BUILD_ARGS) -t $(IMAGE):$(VERSION) -t $(IMAGE):latest . 14 | 15 | push: 16 | docker push $(IMAGE):$(VERSION) 17 | docker push $(IMAGE):latest 18 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/config.toml: -------------------------------------------------------------------------------- 1 | baseURL = "" 2 | languageCode = "en-us" 3 | title = "Kubernikus" 4 | theme = "hugo-material-docs" 5 | metadataformat = "yaml" 6 | canonifyurls = false 7 | googleAnalytics = "" 8 | sectionPagesMenu = "main" 9 | pluralizelisttitles = false 10 | 11 | [params] 12 | # Repository 13 | provider = "GitHub" 14 | repo_url = "https://github.com/sapcc/kubernikus" 15 | 16 | logo = "images/logo.png" 17 | favicon = "" 18 | 19 | permalink = "#" 20 | 21 | # Custom assets 22 | custom_css = ["stylesheets/hacks.css"] 23 | custom_js = [] 24 | 25 | # Syntax highlighting theme 26 | highlight_css = "" 27 | 28 | [params.palette] 29 | primary = "blue" 30 | accent = "teal" 31 | 32 | [params.font] 33 | text = "Ubuntu" 34 | code = "Ubuntu Mono" 35 | 36 | [social] 37 | github = "sapcc/kubernikus" 38 | 39 | [blackfriday] 40 | smartypants = true 41 | fractions = true 42 | smartDashes = true 43 | plainIDAnchors = true 44 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/layouts/index.html: -------------------------------------------------------------------------------- 1 | {{ partial "head" . }} 2 | 3 | 4 |
5 |
6 | 7 | 17 |
18 |
19 |
20 |
21 |
22 | KUBERNIKUS 23 |
24 | Managed Kubernetes Clusters 25 |
26 |
27 | 28 | 29 | {{ range where .Data.Pages "Type" "index" }} 30 |
31 |
32 | {{ .Content }} 33 |
34 |
35 | {{ end }} 36 | 37 |
38 | 39 | 40 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/static/api/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | API documentation 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/static/images/kubernikus.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/static/images/logo.png -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/static/images/sapcc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/static/images/sapcc.png -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/static/stylesheets/hacks.css: -------------------------------------------------------------------------------- 1 | .project .logo img { 2 | background: none; 3 | border-radius: none; 4 | } 5 | 6 | .project:focus .logo img, .project:hover .logo img { 7 | box-shadow: none; 8 | } 9 | 10 | .about { 11 | position: absolute; 12 | top: 450px; 13 | width: 100%; 14 | left: 0; 15 | text-align: center; 16 | background: #6ea4fb; 17 | } 18 | 19 | .about .wrap { 20 | padding: 4em 0 4em 0; 21 | width: 100%; 22 | max-width: 1200px; 23 | display: inline-block; 24 | text-align: left; 25 | font-size: 28px; 26 | line-height: 1.5em; 27 | color: #fff; 28 | letter-spacing: 0.05em; 29 | font-family: "Source Sans Pro"; 30 | height: 400px; 31 | } 32 | 33 | .features { 34 | position: absolute; 35 | top: 820px; 36 | width: 100%; 37 | left: 0; 38 | text-align: center; 39 | background: #ddeefd; 40 | } 41 | 42 | .features .wrap { 43 | padding: 1em 0 2em 0; 44 | width: 100%; 45 | max-width: 1200px; 46 | display: inline-block; 47 | text-align: left; 48 | line-height: 1.5em; 49 | font-family: "Source Sans Pro"; 50 | font-size: 3em; 51 | } 52 | 53 | .features .wrap h2 { 54 | padding: 1em 0 0.5em 0; 55 | } 56 | 57 | 58 | body.palette-primary-blue { 59 | background: #ddeefd; 60 | } 61 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ### 8th April 2017 5 | 6 | `.Now` has been deprecated. Hence the required minimum version of Hugo is v0.20. 7 | 8 | ### 11th May 2016 9 | 10 | #### Add templates for section lists 11 | 12 | Sections such as www.example.com/foo/ will now be rendered with a list of all pages that are part of this section. The list shows the pages' title and a summary of their content. 13 | 14 | [Show me the diff](https://github.com/digitalcraftsman/hugo-material-docs/commit/1f8393a8d4ce1b8ee3fc7d87be05895c12810494) 15 | 16 | ### 22nd March 2016 17 | 18 | #### Changing setup for Google Analytics 19 | 20 | Formerly, the tracking id for Google Analytics was set like below: 21 | 22 | ```toml 23 | [params] 24 | google_analytics = ["UA-XXXXXXXX-X", "auto"] 25 | ``` 26 | 27 | Now the theme uses Hugo's own Google Analytics config option. The variable moved outside the scope of `params` and the setup requires only the tracking id as a string: 28 | 29 | ```toml 30 | googleAnalytics = "UA-XXXXXXXX-X" 31 | ``` 32 | 33 | [Show me the diff](https://github.com/digitalcraftsman/hugo-material-docs/commit/fa10c8eef935932426d46b662a51f29a5e0d48e2) 34 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Digitalcraftsman
2 | Copyright (c) 2016 Martin Donath 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to 6 | deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | IN THE SOFTWARE. -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/archetypes/default.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/exampleSite/content/license/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-03-09T20:10:46+01:00 3 | title: License 4 | weight: 40 5 | --- 6 | 7 | Copyright (c) 2016 Digitalcraftsman
8 | Copyright (c) 2016 Martin Donath 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to 12 | deal in the Software without restriction, including without limitation the 13 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 14 | sell copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 26 | IN THE SOFTWARE. 27 | 28 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/exampleSite/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/exampleSite/static/.gitkeep -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/images/screenshot.png -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/images/tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/images/tn.png -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/layouts/404.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/layouts/404.html -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/layouts/_default/list.html: -------------------------------------------------------------------------------- 1 | {{ partial "head" . }} 2 | 3 |
4 |
5 |
6 | 7 | 8 | 9 | 10 | 11 |
12 | {{ partial "header" . }} 13 |
14 | 15 |
16 |
17 | {{ partial "drawer" . }} 18 |
19 | 20 |
21 |
22 |

Pages in {{ .Title | singularize }}

23 | 24 | {{ range .Data.Pages }} 25 | 26 |

{{ .Title }}

27 |
28 | 29 |
30 | {{ printf "%s" .Summary | markdownify }} 31 |
32 | {{ end }} 33 | 34 | 39 |
40 |
41 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | 52 | {{ partial "footer_js" . }} 53 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/layouts/partials/nav.html: -------------------------------------------------------------------------------- 1 | {{ $section := .Section }} 2 | {{ $url := .URL }} 3 | 4 |
    5 | {{ range .Site.Sections }} 6 |
  • 7 | {{ .Title }} 8 | 9 | {{ if eq $section .Section }} 10 |
      11 | {{ range .Pages.ByWeight }} 12 |
    • {{ .Title }}
    • 13 | {{ end }} 14 |
    15 | {{ end }} 16 |
  • 17 | {{ end }} 18 |
19 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/layouts/partials/nav_link.html: -------------------------------------------------------------------------------- 1 | {{ $currentMenuEntry := .Scratch.Get "currentMenuEntry" }} 2 | {{ $isCurrent := eq .Permalink ($currentMenuEntry.URL | absURL | printf "%s") }} 3 | 4 | 5 | 6 | {{ $currentMenuEntry.Pre }} 7 | {{ $currentMenuEntry.Name }} 8 | 9 | 10 | {{ if $isCurrent }} 11 |
    12 |
13 | {{ end }} 14 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/layouts/shortcodes/note.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ .Get "title" }}

3 |

{{ printf "%s" .Inner | markdownify }}

4 |
-------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/layouts/shortcodes/warning.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ .Get "title" }}

3 |

{{ printf "%s" .Inner | markdownify }}

4 |
-------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/fonts/icon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/fonts/icon.eot -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/fonts/icon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/fonts/icon.ttf -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/fonts/icon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/fonts/icon.woff -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/images/colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/images/colors.png -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/images/favicon.ico -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/images/logo.png -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/images/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapcc/kubernikus/d0fe42e5a2ad151c45691d7994036895f85d34a3/contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/images/screen.png -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/static/stylesheets/temporary.css: -------------------------------------------------------------------------------- 1 | /* This file only exists (temporarily) until the 2 | custom styling can be replaced with the 3 | implementation of the upstream project. 4 | */ 5 | 6 | blockquote { 7 | padding: 0 20px; 8 | margin: 0 0 20px; 9 | font-size: inherit; 10 | border-left: 5px solid #eee; 11 | } 12 | -------------------------------------------------------------------------------- /contrib/kubernikus-docs-builder/data/themes/hugo-material-docs/theme.toml: -------------------------------------------------------------------------------- 1 | name = "Material Docs" 2 | license = "MIT" 3 | licenselink = "https://github.com/digitalcraftsman/hugo-material-docs/blob/master/LICENSE.md" 4 | description = "A material design theme for documentations." 5 | homepage = "https://github.com/digitalcraftsman/hugo-material-docs" 6 | tags = ["material", "documentation", "docs", "google analytics", "responsive"] 7 | features = [] 8 | min_version = 0.20 9 | 10 | [author] 11 | name = "Digitalcraftsman" 12 | homepage = "https://github.com/digitalcraftsman" 13 | 14 | # If porting an existing theme 15 | [original] 16 | name = "Martin Donath" 17 | homepage = "http://struct.cc/" 18 | repo = "https://github.com/squidfunk/mkdocs-material" 19 | -------------------------------------------------------------------------------- /contrib/kubernikus-terraform/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/golang:1.23-alpine3.21 as builder 2 | 3 | RUN apk add --update git make bash gcc musl-dev zip 4 | 5 | ARG TERRAFORM_PROVIDER_CCLOUD_VERSION 6 | WORKDIR /go/src/github.com/sapcc/terraform-provider-ccloud 7 | RUN git clone https://github.com/sapcc/terraform-provider-ccloud.git . 8 | RUN git reset --hard ${TERRAFORM_PROVIDER_CCLOUD_VERSION} 9 | RUN make 10 | 11 | ARG TERRAFORM_PROVIDER_OPENSTACK_VERSION 12 | WORKDIR /go/src/github.com/terraform-providers/terraform-provider-openstack 13 | RUN git clone https://github.com/kayrus/terraform-provider-openstack.git . 14 | RUN git reset --hard ${TERRAFORM_PROVIDER_OPENSTACK_VERSION} 15 | RUN make 16 | 17 | # WORKDIR /go/src/github.com/hashicorp/terraform 18 | # RUN git clone https://github.com/jtopjian/terraform.git --branch backend-swift-auth-update . 19 | # RUN make tools 20 | # RUN make fmt 21 | # RUN XC_OS=linux XC_ARCH=amd64 make bin 22 | 23 | FROM sapcc/kubernikus-terraform:v20181113152146 as terraform11 24 | FROM alpine:3.21 25 | LABEL source_repository="https://github.com/sapcc/kubernikus" 26 | 27 | RUN apk add --update make ca-certificates 28 | COPY --from=builder /go/bin/* /usr/local/bin/ 29 | COPY --from=terraform11 /usr/local/bin/terraform /usr/local/bin/ 30 | 31 | -------------------------------------------------------------------------------- /contrib/kubernikus-terraform/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/sh 2 | IMAGE := sapcc/kubernikus-terraform 3 | DATE := $(shell date +%Y%m%d%H%M%S) 4 | VERSION ?= v$(DATE) 5 | 6 | TERRAFORM_VERSION := 0.11.7-r0 7 | TERRAFORM_PROVIDER_OPENSTACK_VERSION := v1.21.0 8 | TERRAFORM_PROVIDER_CCLOUD_VERSION := v1.1.2 9 | 10 | 11 | .PHONY: all 12 | all: build push 13 | 14 | build: 15 | docker build -t $(IMAGE):$(VERSION) -t $(IMAGE):latest \ 16 | --build-arg TERRAFORM_VERSION=$(TERRAFORM_VERSION) \ 17 | --build-arg TERRAFORM_PROVIDER_OPENSTACK_VERSION=$(TERRAFORM_PROVIDER_OPENSTACK_VERSION) \ 18 | --build-arg TERRAFORM_PROVIDER_CCLOUD_VERSION=$(TERRAFORM_PROVIDER_CCLOUD_VERSION) \ 19 | . 20 | 21 | push: 22 | docker push $(IMAGE):$(VERSION) 23 | docker push $(IMAGE):latest 24 | -------------------------------------------------------------------------------- /contrib/kubernikus-tests/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/golang:1.23-alpine3.21 2 | LABEL source_repository="https://github.com/sapcc/kubernikus" 3 | 4 | WORKDIR /go/src/github.com/sapcc/kubernikus/ 5 | 6 | RUN apk add --no-cache make git curl bash 7 | 8 | RUN curl -Lf https://get.helm.sh/helm-v3.8.2-linux-amd64.tar.gz\ 9 | | tar --strip-components=1 -C /usr/local/bin -zxv \ 10 | && helm version -c 11 | 12 | RUN curl -Lf https://github.com/prometheus/prometheus/releases/download/v2.12.0/prometheus-2.12.0.linux-amd64.tar.gz \ 13 | | tar --strip-components=1 -C /usr/local/bin -zxv prometheus-2.12.0.linux-amd64/promtool \ 14 | && promtool --version 15 | 16 | RUN curl -Lfo /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/2.2.1/yq_linux_amd64 \ 17 | && chmod +x /usr/local/bin/yq 18 | 19 | RUN curl -Lf https://github.com/kyoh86/exportloopref/releases/download/v0.1.8/exportloopref_0.1.8_linux_amd64.tar.gz \ 20 | | tar -C /usr/local/bin -zxv exportloopref \ 21 | && chmod +x /usr/local/bin/exportloopref 22 | -------------------------------------------------------------------------------- /contrib/kubernikusctl/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG VERSION=latest 2 | FROM sapcc/kubernikus-binaries:$VERSION as kubernikus-binaries 3 | 4 | FROM alpine:3.21 5 | LABEL source_repository="https://github.com/sapcc/kubernikus" 6 | COPY --from=kubernikus-binaries /kubernikusctl /usr/local/bin/ 7 | CMD ["kubernikusctl"] 8 | -------------------------------------------------------------------------------- /contrib/multus-cni/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.21 2 | ARG TARGETOS 3 | ARG TARGETARCH 4 | ARG MULTUS_CNI_RELEASE 5 | 6 | LABEL source_repository=https://github.com/sapcc/kubernikus/tree/master/contrib/multus-cni 7 | WORKDIR /multus-cni 8 | RUN mkdir -p /multus-cni && \ 9 | wget -O- https://github.com/intel/multus-cni/releases/download/v${MULTUS_CNI_RELEASE}/multus-cni_${MULTUS_CNI_RELEASE}_${TARGETOS}_${TARGETARCH}.tar.gz | tar --strip 1 -xz 10 | -------------------------------------------------------------------------------- /contrib/multus-cni/Makefile: -------------------------------------------------------------------------------- 1 | MULTUS_CNI_RELEASE=3.9 2 | IMAGE?=keppel.eu-de-1.cloud.sap/ccloud 3 | OS?=linux 4 | ARCH?=amd64 5 | 6 | 7 | build: 8 | docker build -t ${IMAGE}/multus-cni:${MULTUS_CNI_RELEASE} --build-arg MULTUS_CNI_RELEASE=${MULTUS_CNI_RELEASE} . 9 | 10 | push: 11 | docker push ${IMAGE}/multus-cni:${MULTUS_CNI_RELEASE} 12 | 13 | mac: 14 | docker buildx build --platform linux/amd64 -t ${IMAGE}/multus-cni:${MULTUS_CNI_RELEASE} --build-arg MULTUS_CNI_RELEASE=${MULTUS_CNI_RELEASE} . --push 15 | -------------------------------------------------------------------------------- /contrib/serve_hostname/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM keppel.eu-de-1.cloud.sap/ccloud-dockerhub-mirror/library/golang:1.23-alpine3.21 2 | 3 | 4 | WORKDIR / 5 | RUN wget https://raw.githubusercontent.com/kubernetes/kubernetes/416974b8346bb1c219efe871c18a9f29de4fad2d/test/images/serve-hostname/serve_hostname.go 6 | RUN go build -o serve_hostname 7 | 8 | FROM alpine:3.21 9 | LABEL source_repository="https://github.com/sapcc/kubernikus" 10 | RUN apk add --no-cache curl bind-tools 11 | EXPOSE 9376 12 | ENTRYPOINT ["/serve_hostname"] 13 | COPY --from=0 /serve_hostname / 14 | -------------------------------------------------------------------------------- /contrib/serve_hostname/Makefile: -------------------------------------------------------------------------------- 1 | IMAGE:= sapcc/serve-hostname-amd64 2 | VERSION := 1.2-alpine 3 | 4 | build: 5 | docker build -t $(IMAGE):$(VERSION) . 6 | push: 7 | docker push $(IMAGE):$(VERSION) 8 | -------------------------------------------------------------------------------- /docs/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-03-08T21:07:13+01:00 3 | title: Kubernikus 4 | type: index 5 | weight: 0 6 | outputs: 7 | - html 8 | - css 9 | --- 10 | -------------------------------------------------------------------------------- /docs/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 0 3 | type: index 4 | --- 5 | 6 | Kubernikus is "Kubernetes as a Service" for Openstack. 7 | 8 |
9 | 10 | It allows to easily manage Kubernetes clusters that are natively integrated 11 | with Openstack. The architecture is designed to facilitate operations 12 | as a managed service. 13 | -------------------------------------------------------------------------------- /docs/development/changing_docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Changing Docs 3 | --- 4 | 5 | ## Self-Hosted Docs 6 | 7 | The self-hosted documentation served by the apiserver is generated using Hugo. 8 | 9 | It consists of 2 parts: 10 | 11 | * Hugo Theme in `contrib/kubernikus-docs-builder/data` 12 | * Markdown docs in `docs` 13 | 14 | A live preview for development can be started with: 15 | 16 | ``` 17 | make documentation 18 | ... 19 | 20 | docker run --rm -ti -p 1313:1313 \ 21 | -v $PWD/contrib/kubernikus-docs-builder/data:/live \ 22 | -v $PWD/docs/:/live/content \ 23 | --workdir /live \ 24 | sapcc/kubernikus-docs:latest \ 25 | hugo server \ 26 | --bind 0.0.0.0 \ 27 | --baseURL "http://localhost:1313/docs" \ 28 | --debug 29 | ``` 30 | 31 | The docs are then accessible locally on http://localhost:1313 32 | -------------------------------------------------------------------------------- /docs/features.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 0 3 | type: index 4 | --- 5 | 6 | ## Managed 7 | 8 | * Architecured to be operated as a managed service 9 | * Masters are managed centrally 10 | * Nodes are decentralized in customer's projects 11 | 12 | ## Compliant 13 | 14 | * 100% Vanilla Kubernetes 15 | * 100% Compatible Openstack API 16 | 17 | ## Secured 18 | 19 | * Air-Gapped Masters and Nodes 20 | * Full TLS encryption between all components 21 | * Auto-Updating nodes based on CoreOS Container Linux 22 | * Authentication Tooling 23 | * Unified Authorization Policy between Openstack and Kubernetes RBAC 24 | -------------------------------------------------------------------------------- /etc/policy-ccadmin.json: -------------------------------------------------------------------------------- 1 | { 2 | "kubernetes_admin": "role:kubernetes_admin and (domain_name:ccadmin or domain_name:Default)", 3 | "kubernetes_user": "(role:kubernetes_member or role:kubernetes_admin) and (domain_name:ccadmin or domain_name:Default)", 4 | "kubernetes_cloud_admin": "(domain_name:ccadmin or domain_name:Default) and project_name:cloud_admin", 5 | 6 | "GetOpenstackMetadata": "rule:kubernetes_admin", 7 | "ListClusters": "rule:kubernetes_user", 8 | "CreateCluster": "rule:kubernetes_admin", 9 | "ShowCluster": "rule:kubernetes_user or role:member", 10 | "TerminateCluster": "rule:kubernetes_admin", 11 | "UpdateCluster": "rule:kubernetes_admin", 12 | "GetClusterCredentials": "rule:kubernetes_user", 13 | "GetClusterCredentialsOIDC": "rule:kubernetes_user", 14 | "GetClusterEvents": "rule:kubernetes_user", 15 | "GetClusterInfo": "rule:kubernetes_user", 16 | "GetBootstrapConfig": "rule:kubernetes_admin", 17 | "GetClusterValues": "rule:kubernetes_cloud_admin", 18 | "GetClusterKubeadmSecret": "rule:kubernetes_admin" 19 | } 20 | -------------------------------------------------------------------------------- /etc/policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "kubernetes_admin": "role:kubernetes_admin", 3 | "kubernetes_user": "role:kubernetes_member or role:kubernetes_admin", 4 | "kubernetes_cloud_admin": "(domain_name:ccadmin or domain_name:Default) and project_name:cloud_admin", 5 | 6 | "GetOpenstackMetadata": "rule:kubernetes_admin", 7 | "ListClusters": "rule:kubernetes_user", 8 | "CreateCluster": "rule:kubernetes_admin", 9 | "ShowCluster": "rule:kubernetes_user or role:member", 10 | "TerminateCluster": "rule:kubernetes_admin", 11 | "UpdateCluster": "rule:kubernetes_admin", 12 | "GetClusterCredentials": "rule:kubernetes_user", 13 | "GetClusterCredentialsOIDC": "rule:kubernetes_user", 14 | "GetClusterEvents": "rule:kubernetes_user", 15 | "GetClusterInfo": "rule:kubernetes_user", 16 | "GetBootstrapConfig": "rule:kubernetes_admin", 17 | "GetClusterValues": "rule:kubernetes_cloud_admin", 18 | "GetClusterKubeadmSecret": "rule:kubernetes_admin" 19 | } 20 | -------------------------------------------------------------------------------- /pkg/api/handlers/errors.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/go-openapi/runtime" 8 | 9 | "github.com/sapcc/kubernikus/pkg/api/models" 10 | ) 11 | 12 | type ErrorResponse interface { 13 | SetStatusCode(code int) 14 | SetPayload(payload *models.Error) 15 | WriteResponse(rw http.ResponseWriter, producer runtime.Producer) 16 | } 17 | 18 | func NewErrorResponse(resp ErrorResponse, code int, msg string, a ...interface{}) ErrorResponse { 19 | resp.SetStatusCode(code) 20 | m := fmt.Sprintf(msg, a...) 21 | c := int64(code) 22 | 23 | resp.SetPayload(&models.Error{ 24 | Message: m, 25 | Code: c, 26 | }) 27 | return resp 28 | } 29 | -------------------------------------------------------------------------------- /pkg/api/handlers/get_openstack_metadata.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "github.com/go-openapi/runtime/middleware" 5 | 6 | "github.com/sapcc/kubernikus/pkg/api" 7 | "github.com/sapcc/kubernikus/pkg/api/models" 8 | "github.com/sapcc/kubernikus/pkg/api/rest/operations" 9 | ) 10 | 11 | func NewGetOpenstackMetadata(rt *api.Runtime) operations.GetOpenstackMetadataHandler { 12 | return &getOpenstackMetadata{rt} 13 | } 14 | 15 | type getOpenstackMetadata struct { 16 | *api.Runtime 17 | } 18 | 19 | func (d *getOpenstackMetadata) Handle(params operations.GetOpenstackMetadataParams, principal *models.Principal) middleware.Responder { 20 | openstackMetadata, err := fetchOpenstackMetadata(params.HTTPRequest, principal) 21 | if err != nil { 22 | return NewErrorResponse(&operations.GetOpenstackMetadataDefault{}, 500, "%s", err) 23 | } 24 | 25 | return operations.NewGetOpenstackMetadataOK().WithPayload(openstackMetadata) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/api/handlers/list_api_versions.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "github.com/go-openapi/runtime/middleware" 5 | 6 | "github.com/sapcc/kubernikus/pkg/api" 7 | "github.com/sapcc/kubernikus/pkg/api/models" 8 | "github.com/sapcc/kubernikus/pkg/api/rest/operations" 9 | ) 10 | 11 | func NewListAPIVersions(rt *api.Runtime) operations.ListAPIVersionsHandler { 12 | return &listAPIVersions{rt} 13 | } 14 | 15 | type listAPIVersions struct { 16 | *api.Runtime 17 | } 18 | 19 | func (d *listAPIVersions) Handle(params operations.ListAPIVersionsParams) middleware.Responder { 20 | return operations.NewListAPIVersionsOK().WithPayload( 21 | &models.APIVersions{Versions: []string{"v1"}}, 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /pkg/api/handlers/list_clusters.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "github.com/go-openapi/runtime/middleware" 5 | 6 | "github.com/sapcc/kubernikus/pkg/api" 7 | "github.com/sapcc/kubernikus/pkg/api/models" 8 | "github.com/sapcc/kubernikus/pkg/api/rest/operations" 9 | ) 10 | 11 | func NewListClusters(rt *api.Runtime) operations.ListClustersHandler { 12 | return &listClusters{rt} 13 | } 14 | 15 | type listClusters struct { 16 | *api.Runtime 17 | } 18 | 19 | func (d *listClusters) Handle(params operations.ListClustersParams, principal *models.Principal) middleware.Responder { 20 | klusterList, err := d.Klusters.List(accountSelector(principal)) 21 | 22 | if err != nil { 23 | return NewErrorResponse(&operations.ListClustersDefault{}, 500, "%s", err) 24 | } 25 | 26 | clusters := make([]*models.Kluster, 0, len(klusterList)) 27 | for _, kluster := range klusterList { 28 | clusters = append(clusters, klusterFromCRD(kluster)) 29 | } 30 | return operations.NewListClustersOK().WithPayload(clusters) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/api/handlers/root.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "net/http" 5 | "strings" 6 | ) 7 | 8 | var RootContent = `{ 9 | "paths": [ 10 | "/api", 11 | "/api/v1", 12 | "/auth/login", 13 | "/docs", 14 | "/swagger" 15 | ] 16 | }` 17 | 18 | func RootHandler(next http.Handler) http.Handler { 19 | return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { 20 | if r.URL.Path == "/" { 21 | for _, accept := range strings.Split(r.Header.Get("Accept"), ",") { 22 | switch strings.TrimSpace(accept) { 23 | case "text/html": 24 | http.Redirect(rw, r, "/docs", 301) 25 | return 26 | } 27 | } 28 | rw.Write([]byte(RootContent)) 29 | } else { 30 | next.ServeHTTP(rw, r) 31 | } 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /pkg/api/handlers/show_cluster.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "github.com/go-openapi/runtime/middleware" 5 | apierrors "k8s.io/apimachinery/pkg/api/errors" 6 | 7 | "github.com/sapcc/kubernikus/pkg/api" 8 | "github.com/sapcc/kubernikus/pkg/api/models" 9 | "github.com/sapcc/kubernikus/pkg/api/rest/operations" 10 | ) 11 | 12 | func NewShowCluster(rt *api.Runtime) operations.ShowClusterHandler { 13 | return &showCluster{rt} 14 | } 15 | 16 | type showCluster struct { 17 | *api.Runtime 18 | } 19 | 20 | func (d *showCluster) Handle(params operations.ShowClusterParams, principal *models.Principal) middleware.Responder { 21 | name := qualifiedName(params.Name, principal.Account) 22 | kluster, err := d.Klusters.Klusters(d.Namespace).Get(name) 23 | 24 | if err != nil { 25 | if apierrors.IsNotFound(err) { 26 | return NewErrorResponse(&operations.ShowClusterDefault{}, 404, "Not found") 27 | } 28 | return NewErrorResponse(&operations.ShowClusterDefault{}, 500, "%s", err) 29 | } 30 | 31 | return operations.NewShowClusterOK().WithPayload(klusterFromCRD(kluster)) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/api/models/availability_zone.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // AvailabilityZone availability zone 16 | // 17 | // swagger:model AvailabilityZone 18 | type AvailabilityZone struct { 19 | 20 | // name 21 | Name string `json:"name,omitempty"` 22 | } 23 | 24 | // Validate validates this availability zone 25 | func (m *AvailabilityZone) Validate(formats strfmt.Registry) error { 26 | return nil 27 | } 28 | 29 | // ContextValidate validates this availability zone based on context it is used 30 | func (m *AvailabilityZone) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 31 | return nil 32 | } 33 | 34 | // MarshalBinary interface implementation 35 | func (m *AvailabilityZone) MarshalBinary() ([]byte, error) { 36 | if m == nil { 37 | return nil, nil 38 | } 39 | return swag.WriteJSON(m) 40 | } 41 | 42 | // UnmarshalBinary interface implementation 43 | func (m *AvailabilityZone) UnmarshalBinary(b []byte) error { 44 | var res AvailabilityZone 45 | if err := swag.ReadJSON(b, &res); err != nil { 46 | return err 47 | } 48 | *m = res 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /pkg/api/models/credentials.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // Credentials credentials 16 | // 17 | // swagger:model Credentials 18 | type Credentials struct { 19 | 20 | // kubeconfig 21 | Kubeconfig string `json:"kubeconfig,omitempty"` 22 | } 23 | 24 | // Validate validates this credentials 25 | func (m *Credentials) Validate(formats strfmt.Registry) error { 26 | return nil 27 | } 28 | 29 | // ContextValidate validates this credentials based on context it is used 30 | func (m *Credentials) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 31 | return nil 32 | } 33 | 34 | // MarshalBinary interface implementation 35 | func (m *Credentials) MarshalBinary() ([]byte, error) { 36 | if m == nil { 37 | return nil, nil 38 | } 39 | return swag.WriteJSON(m) 40 | } 41 | 42 | // UnmarshalBinary interface implementation 43 | func (m *Credentials) UnmarshalBinary(b []byte) error { 44 | var res Credentials 45 | if err := swag.ReadJSON(b, &res); err != nil { 46 | return err 47 | } 48 | *m = res 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /pkg/api/models/doc.go: -------------------------------------------------------------------------------- 1 | // +k8s:deepcopy-gen=package,register 2 | // +groupName=kubernikus.sap.cc 3 | package models 4 | -------------------------------------------------------------------------------- /pkg/api/models/flavor_sorter.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | type flavorSorter struct { 8 | flavors []Flavor 9 | } 10 | 11 | func SortFlavors(flavors []Flavor) { 12 | sort.Sort(&flavorSorter{flavors: flavors}) 13 | } 14 | 15 | // Part of sort.Interface 16 | func (fs *flavorSorter) Len() int { 17 | return len(fs.flavors) 18 | } 19 | 20 | func (fs *flavorSorter) Swap(i, j int) { 21 | fs.flavors[i], fs.flavors[j] = fs.flavors[j], fs.flavors[i] 22 | } 23 | 24 | func (fs *flavorSorter) Less(i, j int) bool { 25 | if fs.flavors[i].RAM == fs.flavors[j].RAM { 26 | return fs.flavors[i].Vcpus < fs.flavors[j].Vcpus 27 | } 28 | return fs.flavors[i].RAM < fs.flavors[j].RAM 29 | } 30 | -------------------------------------------------------------------------------- /pkg/api/models/flavor_sorter_test.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestFlavorSorter(t *testing.T) { 10 | 11 | m1small := Flavor{ID: "m1.small", RAM: 2046, Vcpus: 2} 12 | m1xsmall := Flavor{ID: "m1.xsmall", RAM: 4096, Vcpus: 2} 13 | m1medium := Flavor{ID: "m1.medium", RAM: 4096, Vcpus: 4} 14 | m1xmedium := Flavor{ID: "m1.xmedium", RAM: 8192, Vcpus: 4} 15 | 16 | input := []Flavor{m1xmedium, m1medium, m1small, m1xsmall} 17 | 18 | SortFlavors(input) 19 | 20 | expected := []Flavor{m1small, m1xsmall, m1medium, m1xmedium} 21 | 22 | assert.Equal(t, expected, input) 23 | 24 | } 25 | -------------------------------------------------------------------------------- /pkg/api/models/key_pair.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // KeyPair key pair 16 | // 17 | // swagger:model KeyPair 18 | type KeyPair struct { 19 | 20 | // name 21 | Name string `json:"name,omitempty"` 22 | 23 | // public key 24 | PublicKey string `json:"publicKey,omitempty"` 25 | } 26 | 27 | // Validate validates this key pair 28 | func (m *KeyPair) Validate(formats strfmt.Registry) error { 29 | return nil 30 | } 31 | 32 | // ContextValidate validates this key pair based on context it is used 33 | func (m *KeyPair) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 34 | return nil 35 | } 36 | 37 | // MarshalBinary interface implementation 38 | func (m *KeyPair) MarshalBinary() ([]byte, error) { 39 | if m == nil { 40 | return nil, nil 41 | } 42 | return swag.WriteJSON(m) 43 | } 44 | 45 | // UnmarshalBinary interface implementation 46 | func (m *KeyPair) UnmarshalBinary(b []byte) error { 47 | var res KeyPair 48 | if err := swag.ReadJSON(b, &res); err != nil { 49 | return err 50 | } 51 | *m = res 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /pkg/api/models/kubeadm_secret.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // KubeadmSecret kubeadm secret 16 | // 17 | // swagger:model KubeadmSecret 18 | type KubeadmSecret struct { 19 | 20 | // secret 21 | Secret string `json:"secret,omitempty"` 22 | } 23 | 24 | // Validate validates this kubeadm secret 25 | func (m *KubeadmSecret) Validate(formats strfmt.Registry) error { 26 | return nil 27 | } 28 | 29 | // ContextValidate validates this kubeadm secret based on context it is used 30 | func (m *KubeadmSecret) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 31 | return nil 32 | } 33 | 34 | // MarshalBinary interface implementation 35 | func (m *KubeadmSecret) MarshalBinary() ([]byte, error) { 36 | if m == nil { 37 | return nil, nil 38 | } 39 | return swag.WriteJSON(m) 40 | } 41 | 42 | // UnmarshalBinary interface implementation 43 | func (m *KubeadmSecret) UnmarshalBinary(b []byte) error { 44 | var res KubeadmSecret 45 | if err := swag.ReadJSON(b, &res); err != nil { 46 | return err 47 | } 48 | *m = res 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /pkg/api/models/link.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // Link link 16 | // 17 | // swagger:model Link 18 | type Link struct { 19 | 20 | // link 21 | Link string `json:"link,omitempty"` 22 | 23 | // platform 24 | Platform string `json:"platform,omitempty"` 25 | } 26 | 27 | // Validate validates this link 28 | func (m *Link) Validate(formats strfmt.Registry) error { 29 | return nil 30 | } 31 | 32 | // ContextValidate validates this link based on context it is used 33 | func (m *Link) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 34 | return nil 35 | } 36 | 37 | // MarshalBinary interface implementation 38 | func (m *Link) MarshalBinary() ([]byte, error) { 39 | if m == nil { 40 | return nil, nil 41 | } 42 | return swag.WriteJSON(m) 43 | } 44 | 45 | // UnmarshalBinary interface implementation 46 | func (m *Link) UnmarshalBinary(b []byte) error { 47 | var res Link 48 | if err := swag.ReadJSON(b, &res); err != nil { 49 | return err 50 | } 51 | *m = res 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /pkg/api/models/node_pool_info_test.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "testing" 5 | 6 | strfmt "github.com/go-openapi/strfmt" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNodePoolInfoValidation(t *testing.T) { 11 | info := NodePoolInfo{ 12 | Name: "test", 13 | } 14 | assert.NoError(t, info.Validate(strfmt.Default)) 15 | json, err := info.MarshalBinary() 16 | assert.NoError(t, err) 17 | assert.Contains(t, string(json), `"size":0`) 18 | 19 | } 20 | -------------------------------------------------------------------------------- /pkg/api/models/o_id_c.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // OIDC Deprecated: Use authenticationConfiguration instead 16 | // 17 | // swagger:model OIDC 18 | type OIDC struct { 19 | 20 | // client ID 21 | ClientID string `json:"clientID,omitempty"` 22 | 23 | // issuer URL 24 | IssuerURL string `json:"issuerURL,omitempty"` 25 | } 26 | 27 | // Validate validates this o ID c 28 | func (m *OIDC) Validate(formats strfmt.Registry) error { 29 | return nil 30 | } 31 | 32 | // ContextValidate validates this o ID c based on context it is used 33 | func (m *OIDC) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 34 | return nil 35 | } 36 | 37 | // MarshalBinary interface implementation 38 | func (m *OIDC) MarshalBinary() ([]byte, error) { 39 | if m == nil { 40 | return nil, nil 41 | } 42 | return swag.WriteJSON(m) 43 | } 44 | 45 | // UnmarshalBinary interface implementation 46 | func (m *OIDC) UnmarshalBinary(b []byte) error { 47 | var res OIDC 48 | if err := swag.ReadJSON(b, &res); err != nil { 49 | return err 50 | } 51 | *m = res 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /pkg/api/models/security_group.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // SecurityGroup security group 16 | // 17 | // swagger:model SecurityGroup 18 | type SecurityGroup struct { 19 | 20 | // id 21 | ID string `json:"id,omitempty"` 22 | 23 | // name 24 | Name string `json:"name,omitempty"` 25 | } 26 | 27 | // Validate validates this security group 28 | func (m *SecurityGroup) Validate(formats strfmt.Registry) error { 29 | return nil 30 | } 31 | 32 | // ContextValidate validates this security group based on context it is used 33 | func (m *SecurityGroup) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 34 | return nil 35 | } 36 | 37 | // MarshalBinary interface implementation 38 | func (m *SecurityGroup) MarshalBinary() ([]byte, error) { 39 | if m == nil { 40 | return nil, nil 41 | } 42 | return swag.WriteJSON(m) 43 | } 44 | 45 | // UnmarshalBinary interface implementation 46 | func (m *SecurityGroup) UnmarshalBinary(b []byte) error { 47 | var res SecurityGroup 48 | if err := swag.ReadJSON(b, &res); err != nil { 49 | return err 50 | } 51 | *m = res 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /pkg/api/models/subnet.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // Subnet subnet 16 | // 17 | // swagger:model Subnet 18 | type Subnet struct { 19 | 20 | // c ID r 21 | CIDR string `json:"CIDR,omitempty"` 22 | 23 | // id 24 | ID string `json:"id,omitempty"` 25 | 26 | // name 27 | Name string `json:"name,omitempty"` 28 | } 29 | 30 | // Validate validates this subnet 31 | func (m *Subnet) Validate(formats strfmt.Registry) error { 32 | return nil 33 | } 34 | 35 | // ContextValidate validates this subnet based on context it is used 36 | func (m *Subnet) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 37 | return nil 38 | } 39 | 40 | // MarshalBinary interface implementation 41 | func (m *Subnet) MarshalBinary() ([]byte, error) { 42 | if m == nil { 43 | return nil, nil 44 | } 45 | return swag.WriteJSON(m) 46 | } 47 | 48 | // UnmarshalBinary interface implementation 49 | func (m *Subnet) UnmarshalBinary(b []byte) error { 50 | var res Subnet 51 | if err := swag.ReadJSON(b, &res); err != nil { 52 | return err 53 | } 54 | *m = res 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /pkg/api/rest/configure_kubernikus.go: -------------------------------------------------------------------------------- 1 | // This file is safe to edit. Once it exists it will not be overwritten 2 | 3 | package rest 4 | 5 | import ( 6 | "crypto/tls" 7 | "net/http" 8 | 9 | "github.com/sapcc/kubernikus/pkg/api/rest/operations" 10 | ) 11 | 12 | //go:generate swagger generate server --target ../../api --name Kubernikus --spec ../../../swagger.yml --server-package rest --principal models.Principal --exclude-main 13 | 14 | func configureFlags(api *operations.KubernikusAPI) { 15 | // api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{ ... } 16 | } 17 | 18 | func configureAPI(api *operations.KubernikusAPI) http.Handler { 19 | // configure the api here 20 | return api.Serve(func(handler http.Handler) http.Handler { 21 | return handler 22 | }) 23 | } 24 | 25 | // The TLS configuration before HTTPS server starts. 26 | func configureTLS(tlsConfig *tls.Config) { 27 | // Make all necessary changes to the TLS configuration here. 28 | } 29 | 30 | // As soon as server is initialized but not run yet, this function will be called. 31 | // If you need to modify a config, store server instance to stop it individually later, this is the place. 32 | // This function can be called multiple times, depending on the number of serving schemes. 33 | // scheme value will be set accordingly: "http", "https" or "unix" 34 | func configureServer(s *http.Server, scheme, addr string) { 35 | } 36 | -------------------------------------------------------------------------------- /pkg/api/rest/doc.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | // Package rest Kubernikus 4 | // 5 | // Schemes: 6 | // http 7 | // Host: localhost 8 | // BasePath: / 9 | // Version: 1.0.0 10 | // 11 | // Consumes: 12 | // - application/json 13 | // 14 | // Produces: 15 | // - application/json 16 | // 17 | // swagger:meta 18 | package rest 19 | -------------------------------------------------------------------------------- /pkg/api/rest/metrics.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import "github.com/prometheus/client_golang/prometheus" 4 | 5 | func init() { 6 | prometheus.Register(HTTPRequestsTotal) 7 | } 8 | 9 | var HTTPRequestsTotal = prometheus.NewCounterVec( 10 | prometheus.CounterOpts{ 11 | Name: "http_requests_total", 12 | Help: "Count of all HTTP requests", 13 | }, 14 | []string{"code", "method"}, 15 | ) 16 | -------------------------------------------------------------------------------- /pkg/api/rest/operations/get_auth_login_responses.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package operations 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "net/http" 10 | 11 | "github.com/go-openapi/runtime" 12 | ) 13 | 14 | // GetAuthLoginFoundCode is the HTTP code returned for type GetAuthLoginFound 15 | const GetAuthLoginFoundCode int = 302 16 | 17 | /* 18 | GetAuthLoginFound Redirect 19 | 20 | swagger:response getAuthLoginFound 21 | */ 22 | type GetAuthLoginFound struct { 23 | } 24 | 25 | // NewGetAuthLoginFound creates GetAuthLoginFound with default headers values 26 | func NewGetAuthLoginFound() *GetAuthLoginFound { 27 | 28 | return &GetAuthLoginFound{} 29 | } 30 | 31 | // WriteResponse to the client 32 | func (o *GetAuthLoginFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { 33 | 34 | rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses 35 | 36 | rw.WriteHeader(302) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/api/spec/document_test.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import "testing" 4 | 5 | func TestDefaulString(t *testing.T) { 6 | value, err := DefaultString("KlusterSpec", "serviceCIDR") 7 | if err != nil { 8 | t.Fatal("returned an error ", err) 9 | } 10 | if value == "" { 11 | t.Fatal("did not return a non-empty value") 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pkg/apis/kubernikus/v1/constants.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | const ( 4 | KlusterResourcePlural = "klusters" 5 | ) 6 | -------------------------------------------------------------------------------- /pkg/apis/kubernikus/v1/doc.go: -------------------------------------------------------------------------------- 1 | // +k8s:deepcopy-gen=package,register 2 | // +groupName=kubernikus.sap.cc 3 | package v1 4 | -------------------------------------------------------------------------------- /pkg/apis/kubernikus/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | "k8s.io/apimachinery/pkg/runtime" 6 | "k8s.io/apimachinery/pkg/runtime/schema" 7 | ) 8 | 9 | var ( 10 | // SchemeBuilder collects the scheme builder functions for the Ark API 11 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 12 | 13 | // AddToScheme applies the SchemeBuilder functions to a specified scheme 14 | AddToScheme = SchemeBuilder.AddToScheme 15 | ) 16 | 17 | // GroupName is the group name for the Ark API 18 | const GroupName = "kubernikus.sap.cc" 19 | 20 | // SchemeGroupVersion is the GroupVersion for the Ark API 21 | var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} 22 | 23 | // Resource gets an Ark GroupResource for a specified resource 24 | func Resource(resource string) schema.GroupResource { 25 | return SchemeGroupVersion.WithResource(resource).GroupResource() 26 | } 27 | 28 | func addKnownTypes(scheme *runtime.Scheme) error { 29 | scheme.AddKnownTypes(SchemeGroupVersion, 30 | &Kluster{}, 31 | &KlusterList{}, 32 | ) 33 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /pkg/apis/kubernikus/v1/secret_test.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | corev1 "k8s.io/api/core/v1" 8 | ) 9 | 10 | func TestSecretMarsheling(t *testing.T) { 11 | 12 | secret := Secret{ 13 | NodePassword: "bla", 14 | BootstrapToken: "blu", 15 | ExtraValues: "joker: joker\n" + 16 | "- one: two\n" + 17 | "- two: three", 18 | } 19 | 20 | data, err := secret.ToData() 21 | require.NoError(t, err) 22 | newSecret, err := NewSecret(&corev1.Secret{Data: data}) 23 | require.NoError(t, err) 24 | require.Equal(t, secret, *newSecret) 25 | 26 | } 27 | -------------------------------------------------------------------------------- /pkg/client/helm/client.go: -------------------------------------------------------------------------------- 1 | package helm 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/go-kit/log" 7 | "helm.sh/helm/v3/pkg/action" 8 | "helm.sh/helm/v3/pkg/kube" 9 | ) 10 | 11 | func NewClient3(releaseNamespace, kubeConfig, kubeContext string, logger log.Logger) (*action.Configuration, error) { 12 | client3 := &action.Configuration{} 13 | err := client3.Init(kube.GetConfig(kubeConfig, kubeContext, releaseNamespace), releaseNamespace, "secrets", func(format string, v ...interface{}) { 14 | logger.Log("component", "helm3", "msg", fmt.Sprintf(format, v), "v", 2) 15 | }) 16 | if err != nil { 17 | return nil, err 18 | } 19 | return client3, nil 20 | } 21 | -------------------------------------------------------------------------------- /pkg/client/kubernikus/client.go: -------------------------------------------------------------------------------- 1 | package kubernikus 2 | 3 | import ( 4 | kube "github.com/sapcc/kubernikus/pkg/client/kubernetes" 5 | "github.com/sapcc/kubernikus/pkg/generated/clientset" 6 | ) 7 | 8 | func NewClient(kubeconfig, context string) (clientset.Interface, error) { 9 | config, err := kube.NewConfig(kubeconfig, context) 10 | if err != nil { 11 | return nil, err 12 | } 13 | 14 | clientset, err := clientset.NewForConfig(config) 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | return clientset, nil 20 | } 21 | -------------------------------------------------------------------------------- /pkg/client/openstack/domains/requests.go: -------------------------------------------------------------------------------- 1 | package domains 2 | 3 | import ( 4 | "github.com/gophercloud/gophercloud" 5 | "github.com/gophercloud/gophercloud/pagination" 6 | ) 7 | 8 | // Get retrieves details on a single project, by ID. 9 | func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { 10 | _, r.Err = client.Get(getURL(client, id), &r.Body, nil) 11 | return 12 | } 13 | 14 | // ListOpts provides options to filter the List results. 15 | type ListOpts struct { 16 | 17 | // Name filters the response by name. 18 | Name string `q:"name"` 19 | 20 | // Enabled filters the response by enabled domains. 21 | Enabled *bool `q:"enabled"` 22 | } 23 | 24 | func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager { 25 | url := listURL(client) 26 | if opts != nil { 27 | query, err := gophercloud.BuildQueryString(opts) 28 | if err != nil { 29 | return pagination.Pager{Err: err} 30 | } 31 | url += query.String() 32 | } 33 | return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { 34 | return DomainPage{pagination.LinkedPageBase{PageResult: r}} 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/client/openstack/domains/urls.go: -------------------------------------------------------------------------------- 1 | package domains 2 | 3 | import "github.com/gophercloud/gophercloud" 4 | 5 | func getURL(client *gophercloud.ServiceClient, domainID string) string { 6 | return client.ServiceURL("domains", domainID) 7 | } 8 | 9 | func listURL(client *gophercloud.ServiceClient) string { 10 | return client.ServiceURL("domains") 11 | } 12 | -------------------------------------------------------------------------------- /pkg/client/openstack/project/logging.go: -------------------------------------------------------------------------------- 1 | package project 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/go-kit/log" 7 | 8 | "github.com/sapcc/kubernikus/pkg/api/models" 9 | ) 10 | 11 | type LoggingClient struct { 12 | Client ProjectClient 13 | Logger log.Logger 14 | } 15 | 16 | func (c LoggingClient) GetMetadata() (metadata *models.OpenstackMetadata, err error) { 17 | defer func(begin time.Time) { 18 | c.Logger.Log( 19 | "msg", "fetched metadata", 20 | "flavors", len(metadata.Flavors), 21 | "keypairs", len(metadata.KeyPairs), 22 | "routers", len(metadata.Routers), 23 | "security_groups", len(metadata.SecurityGroups), 24 | "took", time.Since(begin), 25 | "v", 2, 26 | "err", err, 27 | ) 28 | }(time.Now()) 29 | 30 | return c.Client.GetMetadata() 31 | } 32 | -------------------------------------------------------------------------------- /pkg/client/openstack/roles/requests.go: -------------------------------------------------------------------------------- 1 | package roles 2 | 3 | import ( 4 | "github.com/gophercloud/gophercloud" 5 | "github.com/gophercloud/gophercloud/pagination" 6 | ) 7 | 8 | // ListOpts provides options to filter the List results. 9 | type ListOpts struct { 10 | 11 | // Name filters the response by name. 12 | Name string `q:"name"` 13 | 14 | // Enabled filters the response by domain_id. 15 | DomainID string `q:"domain_id"` 16 | } 17 | 18 | func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager { 19 | url := listURL(client) 20 | if opts != nil { 21 | query, err := gophercloud.BuildQueryString(opts) 22 | if err != nil { 23 | return pagination.Pager{Err: err} 24 | } 25 | url += query.String() 26 | } 27 | return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { 28 | return RolePage{pagination.LinkedPageBase{PageResult: r}} 29 | }) 30 | } 31 | 32 | func AssignToUserInProject(client *gophercloud.ServiceClient, projectID, userID, roleID string) error { 33 | url := assignToUserInProjectURL(client, projectID, userID, roleID) 34 | 35 | _, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{OkCodes: []int{204}}) 36 | return err 37 | 38 | } 39 | -------------------------------------------------------------------------------- /pkg/client/openstack/roles/results.go: -------------------------------------------------------------------------------- 1 | package roles 2 | 3 | import "github.com/gophercloud/gophercloud/pagination" 4 | 5 | // Role represents a Role in an assignment. 6 | type Role struct { 7 | ID string `json:"id,omitempty"` 8 | Name string `json:"name,omitempty"` 9 | DomainID string `json:"domain_id,omitempty"` 10 | } 11 | 12 | // ExtractRoles returns a slice of Domains contained in a single page of results. 13 | func ExtractRoles(r pagination.Page) ([]Role, error) { 14 | var s struct { 15 | Roles []Role `json:"Roles"` 16 | } 17 | err := (r.(RolePage)).ExtractInto(&s) 18 | return s.Roles, err 19 | } 20 | 21 | // RolePage is a single page of User results. 22 | type RolePage struct { 23 | pagination.LinkedPageBase 24 | } 25 | 26 | // IsEmpty determines whether or not a RolePage contains any results. 27 | func (r RolePage) IsEmpty() (bool, error) { 28 | domains, err := ExtractRoles(r) 29 | return len(domains) == 0, err 30 | } 31 | 32 | // NextPageURL extracts the "next" link from the links section of the result. 33 | func (r RolePage) NextPageURL() (string, error) { 34 | var s struct { 35 | Links struct { 36 | Next string `json:"next"` 37 | Previous string `json:"previous"` 38 | } `json:"links"` 39 | } 40 | err := r.ExtractInto(&s) 41 | if err != nil { 42 | return "", err 43 | } 44 | return s.Links.Next, err 45 | } 46 | -------------------------------------------------------------------------------- /pkg/client/openstack/roles/urls.go: -------------------------------------------------------------------------------- 1 | package roles 2 | 3 | import "github.com/gophercloud/gophercloud" 4 | 5 | func listURL(client *gophercloud.ServiceClient) string { 6 | return client.ServiceURL("roles") 7 | } 8 | func assignToUserInProjectURL(client *gophercloud.ServiceClient, projectID, userID, roleID string) string { 9 | return client.ServiceURL("projects", projectID, "users", userID, "roles", roleID) 10 | } 11 | -------------------------------------------------------------------------------- /pkg/cmd/env.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | 7 | "dario.cat/mergo" 8 | "github.com/joeshaw/envdecode" 9 | ) 10 | 11 | func PopulateFromEnv(obj interface{}) error { 12 | s := reflect.ValueOf(obj) 13 | if s.Kind() != reflect.Ptr { 14 | return errors.New("not a pointer") 15 | } 16 | //Create a zero valued copy of the passed struct 17 | optionsFromEnv := reflect.New(s.Elem().Type()).Interface() 18 | if err := envdecode.Decode(optionsFromEnv); err != nil { 19 | return err 20 | } 21 | //Populate empty fields with values from the environment 22 | if err := mergo.Merge(obj, optionsFromEnv); err != nil { 23 | return err 24 | } 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /pkg/cmd/errors.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func CheckError(err error) { 10 | if err != nil { 11 | if err != context.Canceled { 12 | fmt.Fprintf(os.Stderr, "An error occurred: %v\n", err) 13 | } 14 | os.Exit(1) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikus/certificates.go: -------------------------------------------------------------------------------- 1 | package kubernikus 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/sapcc/kubernikus/pkg/cmd/kubernikus/certificates" 7 | ) 8 | 9 | func NewCertificatesCommand() *cobra.Command { 10 | 11 | c := &cobra.Command{ 12 | Use: "certificates", 13 | Short: "Debug certificates", 14 | } 15 | 16 | c.AddCommand( 17 | certificates.NewFilesCommand(), 18 | certificates.NewPlainCommand(), 19 | ) 20 | 21 | return c 22 | } 23 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikus/certificates/io.go: -------------------------------------------------------------------------------- 1 | package certificates 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | "path/filepath" 8 | ) 9 | 10 | type ConfigPersister interface { 11 | WriteConfig(map[string]string) error 12 | } 13 | 14 | type FilePersister struct { 15 | BaseDir string 16 | } 17 | 18 | func NewFilePersister(basedir string) *FilePersister { 19 | p := &FilePersister{} 20 | p.BaseDir = basedir 21 | return p 22 | } 23 | 24 | func (fp FilePersister) WriteConfig(certificates map[string]string) error { 25 | for filename, contents := range certificates { 26 | if err := write(path.Join(fp.BaseDir, filename), []byte(contents)); err != nil { 27 | return err 28 | } 29 | } 30 | 31 | return nil 32 | } 33 | 34 | func write(certPath string, data []byte) error { 35 | if err := os.MkdirAll(filepath.Dir(certPath), os.FileMode(0755)); err != nil { 36 | return err 37 | } 38 | if err := os.WriteFile(certPath, data, os.FileMode(0644)); err != nil { 39 | return err 40 | } 41 | return nil 42 | } 43 | 44 | type PlainPersister struct{} 45 | 46 | func NewPlainPersister() *PlainPersister { 47 | return &PlainPersister{} 48 | } 49 | 50 | func (fp PlainPersister) WriteConfig(certificates map[string]string) error { 51 | for filename, contents := range certificates { 52 | fmt.Println(filename) 53 | fmt.Println(contents) 54 | } 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikus/kubernikus.go: -------------------------------------------------------------------------------- 1 | package kubernikus 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func NewCommand(name string) *cobra.Command { 10 | c := &cobra.Command{ 11 | Use: name, 12 | Short: "Kubernetes as a Service", 13 | Long: `Kubernikus is a tool for managing Kubernetes clusters on Openstack.`, 14 | } 15 | 16 | c.AddCommand( 17 | NewCertificatesCommand(), 18 | NewHelmCommand(), 19 | NewOperatorCommand(), 20 | NewSeedCommand(), 21 | ) 22 | c.PersistentFlags().AddGoFlagSet(flag.CommandLine) 23 | 24 | return c 25 | } 26 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikus/seed.go: -------------------------------------------------------------------------------- 1 | package kubernikus 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/sapcc/kubernikus/pkg/cmd/kubernikus/seed" 7 | ) 8 | 9 | func NewSeedCommand() *cobra.Command { 10 | 11 | c := &cobra.Command{ 12 | Use: "seed", 13 | Short: "Seed stuff", 14 | } 15 | 16 | c.AddCommand( 17 | seed.NewKubeDNSCommand(), 18 | ) 19 | 20 | return c 21 | } 22 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikusctl/auth.go: -------------------------------------------------------------------------------- 1 | package kubernikusctl 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/sapcc/kubernikus/pkg/cmd/kubernikusctl/auth" 7 | ) 8 | 9 | func NewAuthCommand() *cobra.Command { 10 | c := &cobra.Command{ 11 | Use: "auth", 12 | Short: "Authentication Commands", 13 | } 14 | 15 | c.AddCommand( 16 | auth.NewInitCommand(), 17 | auth.NewRefreshCommand(), 18 | ) 19 | 20 | return c 21 | } 22 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikusctl/common/log.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "os" 5 | 6 | kitLog "github.com/go-kit/log" 7 | "github.com/spf13/pflag" 8 | "k8s.io/klog" 9 | 10 | "github.com/sapcc/kubernikus/pkg/util/log" 11 | ) 12 | 13 | var ( 14 | logLevel int 15 | ) 16 | 17 | func BindLogFlags(flags *pflag.FlagSet) { 18 | flags.IntVarP(&logLevel, "debug", "v", 0, "") 19 | } 20 | 21 | func SetupLogger() { 22 | logger := kitLog.NewLogfmtLogger(kitLog.NewSyncWriter(os.Stderr)) 23 | logger = log.NewTrailingNilFilter(logger) 24 | logger = kitLog.With(logger, "ts", kitLog.DefaultTimestampUTC, "caller", log.Caller(4)) 25 | klog.ClampLevel(klog.Level(logLevel)) 26 | klog.SetLogger(logger) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikusctl/common/util.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | 8 | "k8s.io/klog" 9 | ) 10 | 11 | func CheckError(err error) { 12 | if err != nil { 13 | if err != context.Canceled { 14 | klog.V(3).Infof("%+v", err) 15 | fmt.Fprintf(os.Stderr, "An error occurred: %v\n", err) 16 | } 17 | os.Exit(1) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikusctl/create.go: -------------------------------------------------------------------------------- 1 | package kubernikusctl 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/sapcc/kubernikus/pkg/cmd/kubernikusctl/common" 7 | "github.com/sapcc/kubernikus/pkg/cmd/kubernikusctl/create" 8 | ) 9 | 10 | func createRun(c *cobra.Command, args []string) { 11 | c.Help() 12 | } 13 | 14 | func NewCreateCommand() *cobra.Command { 15 | o := create.CreateOptions{ 16 | Openstack: common.NewOpenstackClient(), 17 | } 18 | 19 | c := &cobra.Command{ 20 | Use: "create [object]", 21 | Short: "Creates an object from a given spec", 22 | PersistentPreRun: o.PersistentPreRun, 23 | Run: createRun, 24 | } 25 | o.BindFlags(c.PersistentFlags()) 26 | c.AddCommand(o.NewClusterCommand()) 27 | return c 28 | } 29 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikusctl/delete.go: -------------------------------------------------------------------------------- 1 | package kubernikusctl 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/sapcc/kubernikus/pkg/cmd/kubernikusctl/common" 7 | "github.com/sapcc/kubernikus/pkg/cmd/kubernikusctl/delete" 8 | ) 9 | 10 | func deleteRun(c *cobra.Command, args []string) { 11 | c.Help() 12 | } 13 | 14 | func NewDeleteCommand() *cobra.Command { 15 | o := delete.DeleteOptions{ 16 | Openstack: common.NewOpenstackClient(), 17 | } 18 | 19 | c := &cobra.Command{ 20 | Use: "delete [object]", 21 | Short: "Deletes an object", 22 | PersistentPreRun: o.PersistentPreRun, 23 | Run: deleteRun, 24 | } 25 | o.BindFlags(c.PersistentFlags()) 26 | c.AddCommand(o.NewClusterCommand()) 27 | return c 28 | } 29 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikusctl/delete/cluster.go: -------------------------------------------------------------------------------- 1 | package delete 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/pkg/errors" 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/sapcc/kubernikus/pkg/cmd" 10 | ) 11 | 12 | func (o *DeleteOptions) NewClusterCommand() *cobra.Command { 13 | c := &cobra.Command{ 14 | Use: "cluster [name]", 15 | Short: "Deletes the cluster with the given name", 16 | Aliases: []string{"clusters"}, 17 | PreRun: o.clusterPreRun, 18 | Run: o.clusterRun, 19 | } 20 | 21 | return c 22 | } 23 | 24 | func (o *DeleteOptions) clusterPreRun(c *cobra.Command, args []string) { 25 | cmd.CheckError(validateClusterCommandArgs(args)) 26 | cmd.CheckError(o.SetupKubernikusClient()) 27 | } 28 | 29 | func (o *DeleteOptions) clusterRun(c *cobra.Command, args []string) { 30 | cmd.CheckError(o.Kubernikus.DeleteCluster(args[0])) 31 | fmt.Printf("Cluster %v scheduled for deletion.", args[0]) 32 | } 33 | 34 | func validateClusterCommandArgs(args []string) error { 35 | if len(args) > 1 { 36 | return errors.Errorf("Surplus arguments to cluster delete.") 37 | } 38 | if len(args) < 1 { 39 | return errors.Errorf("Please supply the name of the cluster to be deleted.") 40 | } 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikusctl/get.go: -------------------------------------------------------------------------------- 1 | package kubernikusctl 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/sapcc/kubernikus/pkg/cmd/kubernikusctl/common" 7 | "github.com/sapcc/kubernikus/pkg/cmd/kubernikusctl/get" 8 | ) 9 | 10 | func getRun(c *cobra.Command, args []string) { 11 | c.Help() 12 | } 13 | 14 | func NewGetCommand() *cobra.Command { 15 | o := &get.GetOptions{ 16 | Openstack: common.NewOpenstackClient(), 17 | } 18 | 19 | c := &cobra.Command{ 20 | Use: "get [object]", 21 | Short: "Retrieves Information about object from the server", 22 | PersistentPreRun: o.PersistentPreRun, 23 | Run: getRun, 24 | } 25 | 26 | o.BindFlags(c.PersistentFlags()) 27 | c.AddCommand(o.NewClusterCommand()) 28 | c.AddCommand(o.NewNodePoolCommand()) 29 | c.AddCommand(o.NewClusterValuesCommand()) 30 | return c 31 | } 32 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikusctl/kubernikusctl.go: -------------------------------------------------------------------------------- 1 | package kubernikusctl 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | func NewCommand(name string) *cobra.Command { 8 | c := &cobra.Command{ 9 | Use: name, 10 | Short: "Kubernikus Kubectl Plugin", 11 | Long: "Plugin that extends kubectl with Kubernikus convenience features", 12 | } 13 | 14 | c.AddCommand( 15 | NewAuthCommand(), 16 | NewGetCommand(), 17 | NewCreateCommand(), 18 | NewDeleteCommand(), 19 | NewVersionCommand(), 20 | ) 21 | 22 | return c 23 | } 24 | -------------------------------------------------------------------------------- /pkg/cmd/kubernikusctl/version.go: -------------------------------------------------------------------------------- 1 | package kubernikusctl 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/sapcc/kubernikus/pkg/version" 9 | ) 10 | 11 | func NewVersionCommand() *cobra.Command { 12 | return &cobra.Command{ 13 | Use: "version", 14 | Short: "Show the version information for the kubernikusctl binary", 15 | Run: func(c *cobra.Command, _ []string) { 16 | fmt.Printf("%s+%s\n", version.VERSION, version.GitCommit) 17 | 18 | }, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pkg/cmd/printers/printable.go: -------------------------------------------------------------------------------- 1 | package printers 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | ) 6 | 7 | type PrintFormat int 8 | 9 | const ( 10 | Human PrintFormat = iota 11 | Table 12 | ) 13 | 14 | type PrintOptions struct { 15 | WithHeaders bool 16 | } 17 | 18 | type Printable interface { 19 | GetFormats() map[PrintFormat]struct{} 20 | Print(format PrintFormat, options PrintOptions) error 21 | } 22 | 23 | func PrintTable(list []Printable) error { 24 | first := true 25 | for _, item := range list { 26 | _, ok := item.GetFormats()[Table] 27 | if !ok { 28 | return errors.Errorf("Unsupported print format table supported formats: %v, %v", item, item.GetFormats()) 29 | } 30 | item.Print(Table, PrintOptions{WithHeaders: first}) 31 | first = false 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /pkg/cmd/runner.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "syscall" 7 | 8 | "github.com/oklog/run" 9 | ) 10 | 11 | func Runner() *run.Group { 12 | var g run.Group 13 | 14 | sigs := make(chan os.Signal, 1) 15 | 16 | g.Add(func() error { 17 | signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) 18 | <-sigs 19 | return nil 20 | }, func(error) { 21 | close(sigs) 22 | }) 23 | return &g 24 | 25 | } 26 | -------------------------------------------------------------------------------- /pkg/cmd/validate.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/asaskevich/govalidator" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | func init() { 9 | //Add cidr validation 10 | govalidator.TagMap["cidr"] = govalidator.IsCIDR 11 | } 12 | 13 | type Validator interface { 14 | Validate(c *cobra.Command, args []string) error 15 | } 16 | 17 | func Validate(obj interface{}, c *cobra.Command, args []string) error { 18 | if err := PopulateFromEnv(obj); err != nil { 19 | return err 20 | } 21 | if _, err := govalidator.ValidateStruct(obj); err != nil { 22 | return err 23 | } 24 | if v, ok := obj.(Validator); ok { 25 | return v.Validate(c, args) 26 | } 27 | return nil 28 | 29 | } 30 | -------------------------------------------------------------------------------- /pkg/cmd/wormhole/wormhole.go: -------------------------------------------------------------------------------- 1 | package wormhole 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func NewCommand(name string) *cobra.Command { 10 | 11 | c := &cobra.Command{ 12 | Use: name, 13 | Short: "Wormhole as a Service", 14 | Long: `Creates node-aware tunnelt connections between API server and Nodes`, 15 | } 16 | 17 | c.AddCommand( 18 | NewServerCommand(), 19 | NewClientCommand(), 20 | ) 21 | c.PersistentFlags().AddGoFlagSet(flag.CommandLine) 22 | 23 | return c 24 | } 25 | -------------------------------------------------------------------------------- /pkg/controller/base/instrumenting.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | 8 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 9 | ) 10 | 11 | type InstrumentingReconciler struct { 12 | Reconciler 13 | 14 | Latency *prometheus.SummaryVec 15 | Total *prometheus.CounterVec 16 | Successful *prometheus.CounterVec 17 | Failed *prometheus.CounterVec 18 | } 19 | 20 | func (ir *InstrumentingReconciler) Reconcile(kluster *v1.Kluster) (requeue bool, err error) { 21 | defer func(begin time.Time) { 22 | ir.Latency.With( 23 | prometheus.Labels{ 24 | "method": "Reconcile", 25 | }).Observe(time.Since(begin).Seconds()) 26 | 27 | ir.Total.With( 28 | prometheus.Labels{ 29 | "method": "Reconcile", 30 | }).Add(1) 31 | 32 | if err != nil { 33 | ir.Failed.With( 34 | prometheus.Labels{ 35 | "method": "Reconcile", 36 | }).Add(1) 37 | } else { 38 | ir.Successful.With( 39 | prometheus.Labels{ 40 | "method": "Reconcile", 41 | }).Add(1) 42 | } 43 | }(time.Now()) 44 | return ir.Reconciler.Reconcile(kluster) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/controller/base/logging.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/go-kit/log" 7 | 8 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 9 | ) 10 | 11 | var RECONCILLIATION_COUNTER = 0 12 | 13 | type LoggingReconciler struct { 14 | Reconciler Reconciler 15 | Logger log.Logger 16 | } 17 | 18 | func (r *LoggingReconciler) Reconcile(kluster *v1.Kluster) (requeue bool, err error) { 19 | defer func(begin time.Time) { 20 | r.Logger.Log( 21 | "msg", "reconciled kluster", 22 | "kluster", kluster.Spec.Name, 23 | "project", kluster.Account(), 24 | "requeue", requeue, 25 | "took", time.Since(begin), 26 | "v", 1, 27 | "err", err) 28 | }(time.Now()) 29 | return r.Reconciler.Reconcile(kluster) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/controller/flight/instance.go: -------------------------------------------------------------------------------- 1 | package flight 2 | 3 | import ( 4 | "time" 5 | 6 | openstack_kluster "github.com/sapcc/kubernikus/pkg/client/openstack/kluster" 7 | ) 8 | 9 | type Instance interface { 10 | GetID() string 11 | GetName() string 12 | GetSecurityGroupNames() []string 13 | GetCreated() time.Time 14 | Erroring() bool 15 | Running() bool 16 | GetPoolName() string 17 | } 18 | 19 | type instance struct { 20 | openstack_kluster.Node 21 | pool string 22 | } 23 | 24 | func (i instance) GetPoolName() string { 25 | return i.pool 26 | } 27 | -------------------------------------------------------------------------------- /pkg/controller/metrics/deorbit.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import "github.com/prometheus/client_golang/prometheus" 4 | 5 | var DeorbitOperationsLatency = prometheus.NewSummaryVec( 6 | prometheus.SummaryOpts{ 7 | Namespace: "kubernikus", 8 | Subsystem: "deorbit", 9 | Name: "operation_latency_seconds", 10 | Help: "Total duration of reconciliation in microseconds.", 11 | }, 12 | []string{"method"}) 13 | 14 | var DeorbitOperationsTotal = prometheus.NewCounterVec( 15 | prometheus.CounterOpts{ 16 | Namespace: "kubernikus", 17 | Subsystem: "deorbit", 18 | Name: "operation_total", 19 | Help: "Number of operations."}, 20 | []string{"method"}) 21 | 22 | var DeorbitSuccessfulOperationsTotal = prometheus.NewCounterVec( 23 | prometheus.CounterOpts{ 24 | Namespace: "kubernikus", 25 | Subsystem: "deorbit", 26 | Name: "successful_operation_total", 27 | Help: "Number of successful operations."}, 28 | []string{"method"}) 29 | 30 | var DeorbitFailedOperationsTotal = prometheus.NewCounterVec( 31 | prometheus.CounterOpts{ 32 | Namespace: "kubernikus", 33 | Subsystem: "deorbit", 34 | Name: "failed_operation_total", 35 | Help: "Number of failed operations."}, 36 | []string{"method"}) 37 | -------------------------------------------------------------------------------- /pkg/controller/metrics/hammertime.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import "github.com/prometheus/client_golang/prometheus" 4 | 5 | func init() { 6 | prometheus.MustRegister( 7 | HammertimeStatus, 8 | ) 9 | HammertimeStatus.With(prometheus.Labels{"kluster": "dummy-for-absent-metrics-operator"}).Set(0) 10 | } 11 | 12 | var HammertimeStatus = prometheus.NewGaugeVec( 13 | prometheus.GaugeOpts{ 14 | Namespace: "kubernikus", 15 | Subsystem: "hammertime", 16 | Name: "status", 17 | Help: "Status of hammertime (controler manager scaled down)", 18 | }, 19 | []string{"kluster"}, 20 | ) 21 | -------------------------------------------------------------------------------- /pkg/controller/metrics/migration.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import "github.com/prometheus/client_golang/prometheus" 4 | 5 | func init() { 6 | prometheus.MustRegister( 7 | MigrationErrorsTotal, 8 | ) 9 | MigrationErrorsTotal.With(prometheus.Labels{"kluster": "dummy-for-absent-metrics-operator"}).Add(0) 10 | } 11 | 12 | var MigrationErrorsTotal = prometheus.NewCounterVec( 13 | prometheus.CounterOpts{ 14 | Namespace: "kubernikus", 15 | Subsystem: "migration", 16 | Name: "errors_total", 17 | Help: "Total number of failed migration operations", 18 | }, 19 | []string{"kluster"}, 20 | ) 21 | -------------------------------------------------------------------------------- /pkg/controller/metrics/routegc.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import "github.com/prometheus/client_golang/prometheus" 4 | 5 | func init() { 6 | prometheus.MustRegister( 7 | OrphanedRoutesTotal, 8 | RouteGCFailedOperationsTotal, 9 | ) 10 | 11 | OrphanedRoutesTotal.With(prometheus.Labels{}).Add(0) 12 | RouteGCFailedOperationsTotal.With(prometheus.Labels{}).Add(0) 13 | } 14 | 15 | var OrphanedRoutesTotal = prometheus.NewCounterVec( 16 | prometheus.CounterOpts{ 17 | Namespace: "kubernikus", 18 | Subsystem: "routegc", 19 | Name: "orphaned_routes_total", 20 | Help: "Number of orphaned routes removed from OpenStack router", 21 | }, 22 | []string{}, 23 | ) 24 | 25 | var RouteGCFailedOperationsTotal = prometheus.NewCounterVec( 26 | prometheus.CounterOpts{ 27 | Namespace: "kubernikus", 28 | Subsystem: "routegc", 29 | Name: "failed_operation_total", 30 | Help: "Number of failed operations.", 31 | }, 32 | []string{}, 33 | ) 34 | -------------------------------------------------------------------------------- /pkg/controller/migration/status.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | "fmt" 5 | 6 | "k8s.io/apimachinery/pkg/labels" 7 | 8 | "github.com/sapcc/kubernikus/pkg/generated/clientset" 9 | listers_kubernikus "github.com/sapcc/kubernikus/pkg/generated/listers/kubernikus/v1" 10 | "github.com/sapcc/kubernikus/pkg/migration" 11 | "github.com/sapcc/kubernikus/pkg/util" 12 | ) 13 | 14 | func UpdateMigrationStatus(client clientset.Interface, lister listers_kubernikus.KlusterLister) error { 15 | klusters, err := lister.List(labels.Everything()) 16 | if err != nil { 17 | return fmt.Errorf("Failed to list klusters: %s", err) 18 | } 19 | for _, kluster := range klusters { 20 | if migration.MigrationsPending(kluster) { 21 | //Update migration status 22 | err := util.UpdateKlusterMigrationStatus(client.KubernikusV1(), kluster, true) 23 | if err != nil { 24 | return err 25 | } 26 | } 27 | } 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /pkg/controller/nodeobservatory/factory.go: -------------------------------------------------------------------------------- 1 | package nodeobservatory 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/go-kit/log" 7 | 8 | kube "github.com/sapcc/kubernikus/pkg/client/kubernetes" 9 | kubernikus_informers_v1 "github.com/sapcc/kubernikus/pkg/generated/informers/externalversions/kubernikus/v1" 10 | ) 11 | 12 | func NewInformerFactory(informer kubernikus_informers_v1.KlusterInformer, clients kube.SharedClientFactory, logger log.Logger) *InformerFactory { 13 | return &InformerFactory{ 14 | informer: informer, 15 | clientFactory: clients, 16 | logger: logger, 17 | } 18 | } 19 | 20 | type InformerFactory struct { 21 | lock sync.Mutex 22 | observatory *NodeObservatory 23 | informer kubernikus_informers_v1.KlusterInformer 24 | clientFactory kube.SharedClientFactory 25 | logger log.Logger 26 | } 27 | 28 | func (f *InformerFactory) Start(stopCh <-chan struct{}) { 29 | f.lock.Lock() 30 | defer f.lock.Unlock() 31 | if f.observatory != nil { 32 | go f.observatory.Run(stopCh) 33 | } 34 | } 35 | 36 | func (f *InformerFactory) NodeInformer() *NodeObservatory { 37 | f.lock.Lock() 38 | defer f.lock.Unlock() 39 | if f.observatory != nil { 40 | return f.observatory 41 | } 42 | f.observatory = NewController(f.informer, f.clientFactory, f.logger, 10) 43 | return f.observatory 44 | } 45 | -------------------------------------------------------------------------------- /pkg/controller/nodeobservatory/interface.go: -------------------------------------------------------------------------------- 1 | package nodeobservatory 2 | 3 | import ( 4 | "sync" 5 | 6 | "k8s.io/client-go/tools/cache" 7 | 8 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 9 | ) 10 | 11 | type Controller interface { 12 | Run(threadiness int, stopCh <-chan struct{}, wg *sync.WaitGroup) 13 | 14 | GetStoreForKluster(*v1.Kluster) cache.Store 15 | GetIndexerForKluster(*v1.Kluster) cache.Indexer 16 | HasSyncedForKluster(*v1.Kluster) bool 17 | List() map[*v1.Kluster]cache.SharedIndexInformer 18 | AddEventHandlerFuncs(AddFunc, UpdateFunc, DeleteFunc) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/controller/nodeobservatory/testing.go: -------------------------------------------------------------------------------- 1 | package nodeobservatory 2 | 3 | import ( 4 | "github.com/go-kit/log" 5 | "k8s.io/apimachinery/pkg/runtime" 6 | kubernetes_fake "k8s.io/client-go/kubernetes/fake" 7 | 8 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 9 | kube "github.com/sapcc/kubernikus/pkg/client/kubernetes" 10 | kubernikus_fake "github.com/sapcc/kubernikus/pkg/generated/clientset/fake" 11 | kubernikus_informers "github.com/sapcc/kubernikus/pkg/generated/informers/externalversions" 12 | ) 13 | 14 | func NewFakeController(kluster *v1.Kluster, nodes ...runtime.Object) *NodeObservatory { 15 | fakeKubernetesClientset := kubernetes_fake.NewSimpleClientset(nodes...) 16 | fakeKubernikusClientset := kubernikus_fake.NewSimpleClientset(kluster) 17 | kubernikusInformerFactory := kubernikus_informers.NewSharedInformerFactory(fakeKubernikusClientset, 0) 18 | 19 | controller := &NodeObservatory{ 20 | klusterInformer: kubernikusInformerFactory.Kubernikus().V1().Klusters(), 21 | clientFactory: &kube.MockSharedClientFactory{Clientset: fakeKubernetesClientset}, 22 | logger: log.NewNopLogger(), 23 | } 24 | 25 | controller.createAndWatchNodeInformerForKluster(kluster) 26 | 27 | return controller 28 | } 29 | -------------------------------------------------------------------------------- /pkg/controller/servicing/drain/logger.go: -------------------------------------------------------------------------------- 1 | package drain 2 | 3 | import ( 4 | "github.com/go-kit/log" 5 | ) 6 | 7 | type LogWriter struct { 8 | Logger log.Logger 9 | } 10 | 11 | func (w LogWriter) Write(p []byte) (n int, err error) { 12 | w.Logger.Log( 13 | "msg", string(p), 14 | "err", err, 15 | ) 16 | return len(p), nil 17 | } 18 | -------------------------------------------------------------------------------- /pkg/controller/servicing/lifecycler_test.go: -------------------------------------------------------------------------------- 1 | package servicing 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | core_v1 "k8s.io/api/core/v1" 6 | 7 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 8 | ) 9 | 10 | type MockLifeCycler struct { 11 | mock.Mock 12 | } 13 | 14 | func (m *MockLifeCycler) Drain(node *core_v1.Node) error { 15 | return m.Called(node).Error(0) 16 | } 17 | 18 | func (m *MockLifeCycler) Reboot(node *core_v1.Node) error { 19 | return m.Called(node).Error(0) 20 | } 21 | 22 | func (m *MockLifeCycler) Replace(node *core_v1.Node) error { 23 | return m.Called(node).Error(0) 24 | } 25 | 26 | func (m *MockLifeCycler) Uncordon(node *core_v1.Node) error { 27 | return m.Called(node).Error(0) 28 | } 29 | 30 | type MockLifeCyclerFactory struct { 31 | mock.Mock 32 | } 33 | 34 | func (m *MockLifeCyclerFactory) Make(k *v1.Kluster) (LifeCycler, error) { 35 | return m.Called(k).Get(0).(LifeCycler), m.Called(k).Error(1) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/generated/clientset/doc.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | // This package has the automatically generated clientset. 4 | package clientset 5 | -------------------------------------------------------------------------------- /pkg/generated/clientset/fake/doc.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | // This package has the automatically generated fake clientset. 4 | package fake 5 | -------------------------------------------------------------------------------- /pkg/generated/clientset/scheme/doc.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | // This package contains the scheme of the automatically generated clientset. 4 | package scheme 5 | -------------------------------------------------------------------------------- /pkg/generated/clientset/typed/kubernikus/v1/doc.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | // This package has the automatically generated typed clients. 4 | package v1 5 | -------------------------------------------------------------------------------- /pkg/generated/clientset/typed/kubernikus/v1/fake/doc.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | // Package fake has the automatically generated clients. 4 | package fake 5 | -------------------------------------------------------------------------------- /pkg/generated/clientset/typed/kubernikus/v1/fake/fake_kubernikus_client.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | package fake 4 | 5 | import ( 6 | v1 "github.com/sapcc/kubernikus/pkg/generated/clientset/typed/kubernikus/v1" 7 | rest "k8s.io/client-go/rest" 8 | testing "k8s.io/client-go/testing" 9 | ) 10 | 11 | type FakeKubernikusV1 struct { 12 | *testing.Fake 13 | } 14 | 15 | func (c *FakeKubernikusV1) Klusters(namespace string) v1.KlusterInterface { 16 | return &FakeKlusters{c, namespace} 17 | } 18 | 19 | // RESTClient returns a RESTClient that is used to communicate 20 | // with API server by this client implementation. 21 | func (c *FakeKubernikusV1) RESTClient() rest.Interface { 22 | var ret *rest.RESTClient 23 | return ret 24 | } 25 | -------------------------------------------------------------------------------- /pkg/generated/clientset/typed/kubernikus/v1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | type KlusterExpansion interface{} 6 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | // Code generated by informer-gen. DO NOT EDIT. 2 | 3 | package internalinterfaces 4 | 5 | import ( 6 | time "time" 7 | 8 | clientset "github.com/sapcc/kubernikus/pkg/generated/clientset" 9 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | runtime "k8s.io/apimachinery/pkg/runtime" 11 | cache "k8s.io/client-go/tools/cache" 12 | ) 13 | 14 | // NewInformerFunc takes clientset.Interface and time.Duration to return a SharedIndexInformer. 15 | type NewInformerFunc func(clientset.Interface, time.Duration) cache.SharedIndexInformer 16 | 17 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 18 | type SharedInformerFactory interface { 19 | Start(stopCh <-chan struct{}) 20 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 21 | } 22 | 23 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions. 24 | type TweakListOptionsFunc func(*v1.ListOptions) 25 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/kubernikus/interface.go: -------------------------------------------------------------------------------- 1 | // Code generated by informer-gen. DO NOT EDIT. 2 | 3 | package kubernikus 4 | 5 | import ( 6 | internalinterfaces "github.com/sapcc/kubernikus/pkg/generated/informers/externalversions/internalinterfaces" 7 | v1 "github.com/sapcc/kubernikus/pkg/generated/informers/externalversions/kubernikus/v1" 8 | ) 9 | 10 | // Interface provides access to each of this group's versions. 11 | type Interface interface { 12 | // V1 provides access to shared informers for resources in V1. 13 | V1() v1.Interface 14 | } 15 | 16 | type group struct { 17 | factory internalinterfaces.SharedInformerFactory 18 | namespace string 19 | tweakListOptions internalinterfaces.TweakListOptionsFunc 20 | } 21 | 22 | // New returns a new Interface. 23 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 24 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 25 | } 26 | 27 | // V1 returns a new v1.Interface. 28 | func (g *group) V1() v1.Interface { 29 | return v1.New(g.factory, g.namespace, g.tweakListOptions) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/kubernikus/v1/interface.go: -------------------------------------------------------------------------------- 1 | // Code generated by informer-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | import ( 6 | internalinterfaces "github.com/sapcc/kubernikus/pkg/generated/informers/externalversions/internalinterfaces" 7 | ) 8 | 9 | // Interface provides access to all the informers in this group version. 10 | type Interface interface { 11 | // Klusters returns a KlusterInformer. 12 | Klusters() KlusterInformer 13 | } 14 | 15 | type version struct { 16 | factory internalinterfaces.SharedInformerFactory 17 | namespace string 18 | tweakListOptions internalinterfaces.TweakListOptionsFunc 19 | } 20 | 21 | // New returns a new Interface. 22 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 23 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 24 | } 25 | 26 | // Klusters returns a KlusterInformer. 27 | func (v *version) Klusters() KlusterInformer { 28 | return &klusterInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 29 | } 30 | -------------------------------------------------------------------------------- /pkg/generated/listers/kubernikus/v1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | // Code generated by lister-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | // KlusterListerExpansion allows custom methods to be added to 6 | // KlusterLister. 7 | type KlusterListerExpansion interface{} 8 | 9 | // KlusterNamespaceListerExpansion allows custom methods to be added to 10 | // KlusterNamespaceLister. 11 | type KlusterNamespaceListerExpansion interface{} 12 | -------------------------------------------------------------------------------- /pkg/migration/01_init.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 5 | "github.com/sapcc/kubernikus/pkg/controller/config" 6 | ) 7 | 8 | // Init is the first migration that only sets the SpecVersion to 1 9 | func Init(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /pkg/migration/03_etcdbr_create_storage_container.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | 9 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 10 | "github.com/sapcc/kubernikus/pkg/controller/config" 11 | etcd_util "github.com/sapcc/kubernikus/pkg/util/etcd" 12 | ) 13 | 14 | func CreateEtcdBackupStorageContainer(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 15 | secret, err := clients.Kubernetes.CoreV1().Secrets(current.GetNamespace()).Get(context.TODO(), current.GetName(), metav1.GetOptions{}) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | username, ok := secret.Data["openstack-username"] 21 | if !ok { 22 | return errors.New("openstack username secret not set") 23 | } 24 | 25 | domain, ok := secret.Data["openstack-domain-name"] 26 | if !ok { 27 | return errors.New("openstack domain name secret not set") 28 | } 29 | 30 | adminClient, err := factories.Openstack.AdminClient() 31 | if err != nil { 32 | return err 33 | } 34 | 35 | err = adminClient.CreateStorageContainer( 36 | current.Account(), 37 | etcd_util.DefaultStorageContainer(current), 38 | string(username), 39 | string(domain), 40 | ) 41 | 42 | return err 43 | } 44 | -------------------------------------------------------------------------------- /pkg/migration/04_migrate_kluster_secret.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | "context" 5 | 6 | meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | 8 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 9 | "github.com/sapcc/kubernikus/pkg/controller/config" 10 | "github.com/sapcc/kubernikus/pkg/util" 11 | ) 12 | 13 | func MigrateKlusterSecret(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 14 | 15 | oldSecret, err := clients.Kubernetes.CoreV1().Secrets(current.Namespace).Get(context.TODO(), current.Name, meta_v1.GetOptions{}) 16 | if err != nil { 17 | return err 18 | } 19 | if _, err := util.EnsureKlusterSecret(clients.Kubernetes, current); err != nil { 20 | return err 21 | } 22 | 23 | newSecret, err := clients.Kubernetes.CoreV1().Secrets(current.Namespace).Get(context.TODO(), current.Name+"-secret", meta_v1.GetOptions{}) 24 | if err != nil { 25 | return err 26 | } 27 | newSecret.Data = oldSecret.Data 28 | 29 | _, err = clients.Kubernetes.CoreV1().Secrets(current.Namespace).Update(context.TODO(), newSecret, meta_v1.UpdateOptions{}) 30 | return err 31 | } 32 | -------------------------------------------------------------------------------- /pkg/migration/06_seed_storage_classes.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 5 | "github.com/sapcc/kubernikus/pkg/controller/config" 6 | "github.com/sapcc/kubernikus/pkg/controller/ground" 7 | ) 8 | 9 | func SeedCinderStorageClasses(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 10 | kubernetes, err := clients.Satellites.ClientFor(current) 11 | if err != nil { 12 | return err 13 | } 14 | 15 | openstack, err := factories.Openstack.ProjectAdminClientFor(current.Account()) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | return ground.SeedCinderStorageClasses(kubernetes, openstack, false) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/migration/07_seed_allow_apiserver_to_access_kubelet.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 5 | "github.com/sapcc/kubernikus/pkg/controller/config" 6 | "github.com/sapcc/kubernikus/pkg/controller/ground" 7 | ) 8 | 9 | func SeedAllowAPIServerToAccessKubelet(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 10 | kubernetes, err := clients.Satellites.ClientFor(current) 11 | if err != nil { 12 | return err 13 | } 14 | 15 | return ground.SeedAllowApiserverToAccessKubeletAPI(kubernetes) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/migration/08_noop.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 5 | "github.com/sapcc/kubernikus/pkg/controller/config" 6 | ) 7 | 8 | func NoOp(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 9 | return nil 10 | } 11 | -------------------------------------------------------------------------------- /pkg/migration/09_reconcile_k8s_version_in_spec.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | "errors" 5 | 6 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 7 | "github.com/sapcc/kubernikus/pkg/controller/config" 8 | ) 9 | 10 | // This migration is intended to run before we enable upgrades. 11 | // It serves to purposes: 12 | // 1. Add Spec.Version for 1.7 Klusters. 13 | // 2. Update Spec.Version to match status for klusters we upgraded manually 14 | func ReconcileK8SVersionInSpec(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 15 | 16 | if current.Spec.Version != current.Status.ApiserverVersion { 17 | if current.Status.ApiserverVersion == "" { 18 | return errors.New("No kubernetes version found in status") 19 | } 20 | current.Spec.Version = current.Status.ApiserverVersion 21 | } 22 | 23 | return 24 | } 25 | -------------------------------------------------------------------------------- /pkg/migration/10_ensure_lb_floating_network_id.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | "fmt" 5 | 6 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 7 | "github.com/sapcc/kubernikus/pkg/controller/config" 8 | ) 9 | 10 | // This migration is intended to run before we enable upgrades. 11 | // It serves to purposes: 12 | // 1. Add Spec.Version for 1.7 Klusters. 13 | // 2. Update Spec.Version to match status for klusters we upgraded manually 14 | func EnsureLBFloatingNetworkID(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 15 | 16 | if current.Spec.Openstack.LBFloatingNetworkID == "" { 17 | client, err := factories.Openstack.ProjectAdminClientFor(current.Account()) 18 | if err != nil { 19 | return err 20 | } 21 | md, err := client.GetMetadata() 22 | if err != nil { 23 | return err 24 | } 25 | for _, router := range md.Routers { 26 | if router.ID == current.Spec.Openstack.RouterID { 27 | if router.ExternalNetworkID == "" { 28 | return fmt.Errorf("Router %s has no external network ID", router.ID) 29 | } 30 | current.Spec.Openstack.LBFloatingNetworkID = router.ExternalNetworkID 31 | return nil 32 | } 33 | } 34 | return fmt.Errorf("Couldn't find router %s", current.Spec.Openstack.RouterID) 35 | } 36 | 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /pkg/migration/11_ensure_security_group_name.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 5 | "github.com/sapcc/kubernikus/pkg/controller/config" 6 | ) 7 | 8 | // This is primarily to fix very old (1.7) clusters 9 | func EnsureSecurityGroupName(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 10 | 11 | if current.Spec.Openstack.SecurityGroupName == "" { 12 | current.Spec.Openstack.SecurityGroupName = "default" 13 | } 14 | 15 | return nil 16 | } 17 | -------------------------------------------------------------------------------- /pkg/migration/14_cleanup_namespaces.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | "context" 5 | "strings" 6 | 7 | "github.com/pkg/errors" 8 | meta "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | 10 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 11 | "github.com/sapcc/kubernikus/pkg/controller/config" 12 | ) 13 | 14 | func CleanupSuppositoryNamespaces(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 15 | client, err := clients.Satellites.ClientFor(current) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | namespaces, err := client.CoreV1().Namespaces().List(context.TODO(), meta.ListOptions{}) 21 | if err != nil { 22 | return errors.Wrap(err, "Failed to list namespaces") 23 | } 24 | for _, n := range namespaces.Items { 25 | if strings.HasPrefix(n.Name, "kubernikus-suppository-") { 26 | if err := client.CoreV1().Namespaces().Delete(context.TODO(), n.Name, meta.DeleteOptions{}); err != nil { 27 | return errors.Wrap(err, "Failed to clean-up leftover suppository namespace") 28 | } 29 | } 30 | } 31 | 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /pkg/migration/15_default_node_pool_options.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | "github.com/sapcc/kubernikus/pkg/api/models" 5 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 6 | "github.com/sapcc/kubernikus/pkg/controller/config" 7 | ) 8 | 9 | func ReconcileNodePoolConfigDefaults(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 10 | for i := range current.Spec.NodePools { 11 | allowReboot := true 12 | allowReplace := true 13 | if current.Spec.NodePools[i].Config == nil { 14 | current.Spec.NodePools[i].Config = &models.NodePoolConfig{ 15 | AllowReboot: &allowReboot, 16 | AllowReplace: &allowReplace, 17 | } 18 | continue 19 | } 20 | 21 | if current.Spec.NodePools[i].Config.AllowReboot == nil { 22 | current.Spec.NodePools[i].Config.AllowReboot = &allowReboot 23 | } 24 | 25 | if current.Spec.NodePools[i].Config.AllowReplace == nil { 26 | current.Spec.NodePools[i].Config.AllowReplace = &allowReplace 27 | } 28 | } 29 | return 30 | } 31 | -------------------------------------------------------------------------------- /pkg/migration/16_fix_update_conf.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 5 | "github.com/sapcc/kubernikus/pkg/controller/config" 6 | ) 7 | 8 | const ( 9 | fixUpdateConf = `#!/bin/bash 10 | cat < /etc/coreos/update.conf 11 | REBOOT_STRATEGY="off" 12 | EOF 13 | 14 | /usr/bin/pkill update_engine 15 | /usr/bin/pkill locksmithd 16 | ` 17 | ) 18 | 19 | func FixUpdateConf(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 20 | client, err := clients.Satellites.ClientFor(current) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | return ApplySuppository(fixUpdateConf, client) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/migration/18_advertisePort.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 5 | "github.com/sapcc/kubernikus/pkg/controller/config" 6 | ) 7 | 8 | func ReconcileAdvertisePortConfigDefault(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 9 | if current.Spec.AdvertisePort == 0 { 10 | current.Spec.AdvertisePort = 6443 11 | } 12 | return 13 | } 14 | -------------------------------------------------------------------------------- /pkg/migration/19_fix_flatcar.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 5 | "github.com/sapcc/kubernikus/pkg/controller/config" 6 | ) 7 | 8 | const ( 9 | fixFlannelOnFlatcar = `#!/bin/bash 10 | mkdir -p /var/lib/coreos 11 | ` 12 | ) 13 | 14 | func FixFlannelOnFlatcar(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) (err error) { 15 | client, err := clients.Satellites.ClientFor(current) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | return ApplySuppository(fixFlannelOnFlatcar, client) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/migration/21_helm_2to3.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 5 | "github.com/sapcc/kubernikus/pkg/controller/config" 6 | ) 7 | 8 | func Helm2to3(rawKluster []byte, current *v1.Kluster, clients config.Clients, factories config.Factories) error { 9 | //this is a noop since we migrated the last existing deployments and ripped out support for helm2 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /pkg/migration/register.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | // In this function we register the migrations for the Kluster CRD 4 | // IMPORTANT: Don't remove migrations, don't reorder them! 5 | // The position in the migrations slice is used for versioning, 6 | // so the only thing that is sane is to append migrations 7 | // to the end of the slice 8 | func init() { 9 | defaultRegistry.migrations = []Migration{ 10 | Init, 11 | AddAggregationLayerCertificates, 12 | CreateEtcdBackupStorageContainer, 13 | MigrateKlusterSecret, 14 | InsertAVZIntoNodePools, 15 | SeedCinderStorageClasses, 16 | SeedAllowAPIServerToAccessKubelet, 17 | NoOp, 18 | ReconcileK8SVersionInSpec, 19 | EnsureLBFloatingNetworkID, 20 | EnsureSecurityGroupName, 21 | NoOp, 22 | FixRootCertificate, 23 | CleanupSuppositoryNamespaces, 24 | ReconcileNodePoolConfigDefaults, 25 | FixUpdateConf, 26 | AddDexSecretAndRoleBindings, 27 | ReconcileAdvertisePortConfigDefault, 28 | FixFlannelOnFlatcar, 29 | KlusterSecretOpenStackIds, 30 | Helm2to3, 31 | // <-- Insert new migrations at the end only! 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pkg/util/bootstrap.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "github.com/aokoli/goutils" 5 | ) 6 | 7 | func GenerateBootstrapToken() string { 8 | token, _ := goutils.Random(16, 32, 127, true, true) 9 | return token 10 | } 11 | -------------------------------------------------------------------------------- /pkg/util/certificates_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestSliceDiff(t *testing.T) { 11 | 12 | cases := []struct { 13 | Old []interface{} 14 | New []interface{} 15 | Result []string 16 | }{ 17 | { 18 | Old: []interface{}{"a", "b", "c"}, 19 | New: []interface{}{"b", "c", "d"}, 20 | Result: []string{"+d", "-a"}, 21 | }, 22 | { 23 | Old: []interface{}{"a", net.IPv4(1, 1, 1, 1)}, 24 | New: []interface{}{"b", net.IPv4(2, 2, 2, 2)}, 25 | Result: []string{"+b", "+2.2.2.2", "-a", "-1.1.1.1"}, 26 | }, 27 | } 28 | 29 | for n, c := range cases { 30 | assert.Equal(t, c.Result, SliceDiff(c.Old, c.New), "Test case %d failed", n) 31 | } 32 | 33 | assert.Equal(t, []string{"+2.2.2.2", "-1.1.1.1"}, IPSliceDiff([]net.IP{net.IPv4(1, 1, 1, 1)}, []net.IP{net.IPv4(2, 2, 2, 2)})) 34 | assert.Equal(t, []string{"+d", "-a"}, StringSliceDiff([]string{"a", "b", "c"}, []string{"b", "c", "d"})) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/util/constants.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | const ( 4 | CA_ISSUER_KUBERNIKUS_IDENTIFIER_0 = "SAP Converged Cloud" 5 | CA_ISSUER_KUBERNIKUS_IDENTIFIER_1 = "Kubernikus" 6 | ) 7 | -------------------------------------------------------------------------------- /pkg/util/etcd/etcd.go: -------------------------------------------------------------------------------- 1 | package etcd 2 | 3 | import ( 4 | "fmt" 5 | 6 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 7 | ) 8 | 9 | const ( 10 | BackupStorageContainerBase = "kubernikus-etcd-backup" 11 | ) 12 | 13 | func DefaultStorageContainer(kluster *v1.Kluster) string { 14 | return fmt.Sprintf("%s-%s-%s", BackupStorageContainerBase, kluster.Spec.Name, kluster.GetUID()) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/util/helm/helm_test.go: -------------------------------------------------------------------------------- 1 | package helm 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 9 | ) 10 | 11 | func TestKlusterToHelmValues(t *testing.T) { 12 | kluster := &v1.Kluster{} 13 | secret := &v1.Secret{} 14 | secret.ExtraValues = "a: a\n" + 15 | "etcd:\n" + 16 | " backup: b\n" 17 | b, err := KlusterToHelmValues(kluster, secret, "v1.10.4", nil, "doof") 18 | // There should be no error converting this 19 | assert.NoError(t, err) 20 | // Make sure easy stuff is in 21 | assert.Equal(t, b["a"], "a") 22 | bck := b["etcd"] 23 | // The etcd entry should stay an object 24 | assert.IsType(t, map[string]interface{}{}, bck) 25 | mbck := bck.(map[string]interface{}) 26 | // Make sure the object was overwritten 27 | assert.Equal(t, mbck["backup"], "b") 28 | } 29 | -------------------------------------------------------------------------------- /pkg/util/helm/merge_map.go: -------------------------------------------------------------------------------- 1 | package helm 2 | 3 | // This is taken from helm functionality of merging helm values 4 | func MergeMaps(a, b map[string]interface{}) map[string]interface{} { 5 | out := make(map[string]interface{}, len(a)) 6 | for k, v := range a { 7 | out[k] = v 8 | } 9 | for k, v := range b { 10 | if v, ok := v.(map[string]interface{}); ok { 11 | if bv, ok := out[k]; ok { 12 | if bv, ok := bv.(map[string]interface{}); ok { 13 | out[k] = MergeMaps(bv, v) 14 | continue 15 | } 16 | } 17 | } 18 | out[k] = v 19 | } 20 | return out 21 | } 22 | -------------------------------------------------------------------------------- /pkg/util/icmp/configure_darwin.go: -------------------------------------------------------------------------------- 1 | package icmp 2 | 3 | func configureSocket(fd int, address string) error { 4 | return nil 5 | } 6 | -------------------------------------------------------------------------------- /pkg/util/icmp/configure_linux.go: -------------------------------------------------------------------------------- 1 | package icmp 2 | 3 | import ( 4 | "net" 5 | "strings" 6 | "syscall" 7 | ) 8 | 9 | func configureSocket(fd int, address string) error { 10 | interfaces, err := net.Interfaces() 11 | if err != nil { 12 | return err 13 | } 14 | for _, intf := range interfaces { 15 | addrs, err := intf.Addrs() 16 | if err != nil { 17 | continue 18 | } 19 | for _, addr := range addrs { 20 | if strings.HasPrefix(addr.String(), address) { 21 | return syscall.BindToDevice(fd, intf.Name) 22 | } 23 | } 24 | } 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /pkg/util/icmp/icmp_linux_test.go: -------------------------------------------------------------------------------- 1 | //go:build integration && linux 2 | // +build integration,linux 3 | 4 | package icmp 5 | 6 | import ( 7 | "net" 8 | "testing" 9 | "time" 10 | 11 | "github.com/stretchr/testify/require" 12 | "golang.org/x/net/ipv4" 13 | 14 | "github.com/sapcc/kubernikus/pkg/util/netutil" 15 | ) 16 | 17 | const defaultInterface = "ens192" 18 | const defaultTarget = "cbr0" 19 | 20 | func interfaceAddress(t *testing.T, networkInterface string) net.IP { 21 | t.Helper() 22 | ip, err := netutil.InterfaceAddress(networkInterface) 23 | require.NoError(t, err) 24 | return ip 25 | } 26 | 27 | func TestRedirect(t *testing.T) { 28 | 29 | sourceIP := interfaceAddress(t, defaultInterface) 30 | 31 | listener, err := NewListener(sourceIP.String()) 32 | require.NoError(t, err) 33 | require.NoError(t, listener.SendEcho(&net.IPAddr{IP: interfaceAddress(t, defaultTarget)}, 1)) 34 | 35 | msg, err := listener.Read(1 * time.Second) 36 | require.NoError(t, err) 37 | require.Equal(t, ipv4.ICMPTypeRedirect, msg.Type) 38 | require.Equal(t, sourceIP.String(), msg.Body.(*Redirect).NextHop.String()) 39 | } 40 | -------------------------------------------------------------------------------- /pkg/util/icmp/icmp_test.go: -------------------------------------------------------------------------------- 1 | //go:build integration 2 | // +build integration 3 | 4 | package icmp 5 | 6 | import ( 7 | "net" 8 | "testing" 9 | "time" 10 | 11 | "github.com/stretchr/testify/require" 12 | "golang.org/x/net/icmp" 13 | "golang.org/x/net/ipv4" 14 | ) 15 | 16 | func TestPing(t *testing.T) { 17 | listener, err := NewListener("0.0.0.0") 18 | require.NoError(t, err) 19 | 20 | require.NoError(t, listener.SendEcho(&net.IPAddr{IP: net.ParseIP("8.8.8.8")}, 23)) 21 | 22 | msg, err := listener.Read(1 * time.Second) 23 | require.NoError(t, err) 24 | 25 | require.Equal(t, ipv4.ICMPTypeEchoReply, msg.Type) 26 | require.Equal(t, listener.ID, msg.Body.(*icmp.Echo).ID) 27 | require.Equal(t, 23, msg.Body.(*icmp.Echo).Seq) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/util/icmp/redirect.go: -------------------------------------------------------------------------------- 1 | package icmp 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | 7 | "golang.org/x/net/icmp" 8 | "golang.org/x/net/ipv4" 9 | ) 10 | 11 | var errMessageTooShort = errors.New("message too short") 12 | 13 | type Redirect struct { 14 | NextHop net.IP 15 | Header *ipv4.Header 16 | Data []byte // data 17 | } 18 | 19 | func (r *Redirect) Len(proto int) int { 20 | if r == nil { 21 | return 0 22 | } 23 | return 4 + r.Header.Len + len(r.Data) 24 | } 25 | 26 | func (r *Redirect) Marshal(proto int) ([]byte, error) { 27 | return nil, errors.New("not implemented") 28 | } 29 | 30 | func parseRedirectMessageBody(proto int, b []byte) (icmp.MessageBody, error) { 31 | bodyLen := len(b) 32 | if bodyLen < 4 { 33 | return nil, errMessageTooShort 34 | } 35 | 36 | p := &Redirect{NextHop: net.IPv4(b[0], b[1], b[2], b[3])} 37 | header, err := ipv4.ParseHeader(b[4:]) 38 | if err != nil { 39 | return nil, err 40 | } 41 | p.Header = header 42 | p.Data = b[4+header.Len:] 43 | 44 | return p, nil 45 | } 46 | -------------------------------------------------------------------------------- /pkg/util/ip/cidr.go: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // adapted from k8s.io/pkg/controller/route 8 | func CIDROverlap(cidr1, cidr2 *net.IPNet) bool { 9 | lastIP1 := make([]byte, len(cidr1.IP)) 10 | for i := range lastIP1 { 11 | lastIP1[i] = cidr1.IP[i] | ^cidr1.Mask[i] 12 | } 13 | lastIP2 := make([]byte, len(cidr2.IP)) 14 | for i := range lastIP2 { 15 | lastIP2[i] = cidr2.IP[i] | ^cidr2.Mask[i] 16 | } 17 | 18 | if cidr2.Contains(cidr1.IP) || cidr2.Contains(lastIP1) || cidr1.Contains(cidr2.IP) || cidr1.Contains(lastIP2) { 19 | return true 20 | } 21 | return false 22 | } 23 | -------------------------------------------------------------------------------- /pkg/util/ip/cidr_test.go: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCIDROverlap(t *testing.T) { 11 | 12 | testCases := []struct { 13 | cidr1 string 14 | cidr2 string 15 | output bool 16 | }{ 17 | {"10.0.0.0/8", "192.168.0.0/24", false}, 18 | {"10.0.0.0/8", "10.0.0.0/8", true}, 19 | {"10.0.0.0/8", "10.0.1.0/24", true}, 20 | {"10.0.1.0/24", "10.0.0.0/8", true}, 21 | } 22 | for n, c := range testCases { 23 | _, cidr1, _ := net.ParseCIDR(c.cidr1) 24 | _, cidr2, _ := net.ParseCIDR(c.cidr2) 25 | result := CIDROverlap(cidr1, cidr2) 26 | assert.Equal(t, c.output, result, "case number %d failed", n+1) 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /pkg/util/ip/ip.go: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "net" 7 | ) 8 | 9 | //This is copied from k8s.io/kubernetes/pkg/registry/core/service/ipallocator 10 | 11 | // GetIndexedIP returns a net.IP that is subnet.IP + index in the contiguous IP space. 12 | func GetIndexedIP(subnet *net.IPNet, index int) (net.IP, error) { 13 | ip := addIPOffset(bigForIP(subnet.IP), index) 14 | if !subnet.Contains(ip) { 15 | return nil, fmt.Errorf("can't generate IP with index %d from subnet. subnet too small. subnet: %q", index, subnet) 16 | } 17 | return ip, nil 18 | } 19 | 20 | // bigForIP creates a big.Int based on the provided net.IP 21 | func bigForIP(ip net.IP) *big.Int { 22 | b := ip.To4() 23 | if b == nil { 24 | b = ip.To16() 25 | } 26 | return big.NewInt(0).SetBytes(b) 27 | } 28 | 29 | // addIPOffset adds the provided integer offset to a base big.Int representing a 30 | // net.IP 31 | func addIPOffset(base *big.Int, offset int) net.IP { 32 | return net.IP(big.NewInt(0).Add(base, big.NewInt(int64(offset))).Bytes()) 33 | } 34 | -------------------------------------------------------------------------------- /pkg/util/ip_or_dnsname.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net" 7 | ) 8 | 9 | type IPOrDNSName struct { 10 | Type IPOrDNSNameType `json:"-"` 11 | IPVal net.IP `json:"ip"` 12 | DNSNameVal string `json:"name"` 13 | } 14 | 15 | type IPOrDNSNameType int 16 | 17 | const ( 18 | IPType IPOrDNSNameType = iota 19 | DNSNameType 20 | ) 21 | 22 | func (s *IPOrDNSName) UnmarshalJSON(value []byte) error { 23 | if len(value) < 3 { 24 | return fmt.Errorf(`Parse error: insufficient length for value [%s]`, string(value)) 25 | } 26 | if s.IPVal = net.ParseIP(string(value[1 : len(value)-1])); s.IPVal != nil { 27 | s.Type = IPType 28 | return nil 29 | } 30 | s.Type = DNSNameType 31 | return json.Unmarshal(value, &s.DNSNameVal) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/util/ip_or_dnsname_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/json" 5 | "net" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestIPOrDNSName(t *testing.T) { 13 | 14 | var result []IPOrDNSName 15 | 16 | require.NoError(t, json.Unmarshal([]byte(`["example.com", "1.1.1.1"]`), &result)) 17 | 18 | assert.Equal(t, 19 | []IPOrDNSName{ 20 | {Type: DNSNameType, DNSNameVal: "example.com"}, 21 | {Type: IPType, IPVal: net.ParseIP("1.1.1.1")}, 22 | }, 23 | result) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/util/k8s/namespace.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "context" 5 | 6 | api_v1 "k8s.io/api/core/v1" 7 | apierrors "k8s.io/apimachinery/pkg/api/errors" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "k8s.io/client-go/kubernetes" 10 | ) 11 | 12 | func EnsureNamespace(client kubernetes.Interface, ns string) error { 13 | if _, err := client.CoreV1().Namespaces().Get(context.TODO(), ns, metav1.GetOptions{}); err == nil { 14 | return nil 15 | } 16 | newNs := &api_v1.Namespace{ 17 | ObjectMeta: metav1.ObjectMeta{ 18 | Name: ns, 19 | }, 20 | } 21 | if _, err := client.CoreV1().Namespaces().Create(context.TODO(), newNs, metav1.CreateOptions{}); !apierrors.IsAlreadyExists(err) { 22 | return err 23 | } 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /pkg/util/kluster_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "testing" 5 | 6 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 7 | ) 8 | 9 | func TestKlusterNeedsUpgrade(t *testing.T) { 10 | 11 | cases := []struct { 12 | from string 13 | to string 14 | expected bool 15 | }{ 16 | {from: "1.10.1", to: "1.10.2", expected: true}, 17 | {from: "1.10.1", to: "1.11.2", expected: true}, 18 | {from: "1.10.1", to: "1.12.2", expected: false}, 19 | {from: "1.10.2", to: "1.10.1", expected: true}, 20 | {from: "1.10.1", to: "1.9.2", expected: false}, 21 | {from: "1.10.1", to: "1.10.1", expected: false}, 22 | } 23 | 24 | for _, c := range cases { 25 | k := new(v1.Kluster) 26 | k.Spec.Version = c.to 27 | k.Status.ApiserverVersion = c.from 28 | if result, err := KlusterNeedsUpgrade(k); err != nil || result != c.expected { 29 | t.Errorf("expected %t for %s --> %s, err: %s", c.expected, c.from, c.to, err) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pkg/util/log/error_origin.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | 6 | kitlog "github.com/go-kit/log" 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | type errorOrigin struct { 11 | next kitlog.Logger 12 | } 13 | 14 | // NewTrailingNilFilter removes key values pairs at the end with a nil value 15 | // This mainly for getting rid of err=null trailers 16 | func NewErrorOrigin(logger kitlog.Logger) kitlog.Logger { 17 | return &errorOrigin{next: logger} 18 | } 19 | 20 | func (l errorOrigin) Log(keyvals ...interface{}) error { 21 | for i := len(keyvals) - 2; i >= 0; i -= 2 { 22 | if err, ok := keyvals[i+1].(error); ok { 23 | if st := originalStackTrace(err); st != nil && len(st) > 0 { 24 | keyvals = append(keyvals, "origin", fmt.Sprintf("%v", st[0])) 25 | break 26 | } 27 | } 28 | } 29 | return l.next.Log(keyvals...) 30 | } 31 | 32 | func originalStackTrace(err error) (st errors.StackTrace) { 33 | type causer interface { 34 | Cause() error 35 | } 36 | type stackTracer interface { 37 | StackTrace() errors.StackTrace 38 | } 39 | 40 | for err != nil { 41 | if sTracer, ok := err.(stackTracer); ok { 42 | st = sTracer.StackTrace() 43 | } 44 | cause, ok := err.(causer) 45 | 46 | if !ok { 47 | break 48 | } 49 | err = cause.Cause() 50 | } 51 | return st 52 | } 53 | -------------------------------------------------------------------------------- /pkg/util/log/levelfilter.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "errors" 5 | 6 | kitlog "github.com/go-kit/log" 7 | ) 8 | 9 | type levelFilter struct { 10 | threshold int 11 | next kitlog.Logger 12 | } 13 | 14 | var levelKey interface{} = "v" 15 | 16 | // NewLevelFilter filters log messages based on a level key. 17 | // It discards log messages 18 | func NewLevelFilter(level int, logger kitlog.Logger) kitlog.Logger { 19 | return &levelFilter{threshold: level, next: logger} 20 | } 21 | 22 | func (l levelFilter) Log(keyvals ...interface{}) error { 23 | for i := len(keyvals) - 2; i >= 0; i -= 2 { 24 | if keyvals[i] == levelKey { 25 | var lvl int 26 | switch n := keyvals[i+1].(type) { 27 | case int: 28 | lvl = n 29 | case int32: 30 | lvl = int(n) 31 | case int64: 32 | lvl = int(n) 33 | default: 34 | return errors.New("Level value is not of expected type (int)") 35 | } 36 | if lvl <= l.threshold { 37 | return l.next.Log(keyvals...) 38 | } 39 | return nil // filter log message 40 | } 41 | } 42 | //ALways log lines without a level 43 | return l.next.Log(keyvals...) 44 | } 45 | -------------------------------------------------------------------------------- /pkg/util/log/levelfilter_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | kitlog "github.com/go-kit/log" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestLevelFilter(t *testing.T) { 12 | var buf bytes.Buffer 13 | logger := kitlog.NewLogfmtLogger(&buf) 14 | logger = NewLevelFilter(2, logger) 15 | assert.NoError(t, logger.Log("key", "val")) 16 | assert.Equal(t, "key=val\n", buf.String()) 17 | 18 | buf.Reset() 19 | assert.NoError(t, logger.Log("v", 2)) 20 | assert.Equal(t, "v=2\n", buf.String()) 21 | 22 | buf.Reset() 23 | assert.NoError(t, logger.Log("v", 3)) 24 | assert.Equal(t, "", buf.String()) 25 | 26 | buf.Reset() 27 | assert.NoError(t, logger.Log("key", "val", "v", 3)) 28 | assert.Equal(t, "", buf.String()) 29 | 30 | buf.Reset() 31 | assert.NoError(t, logger.Log("key", "val", "v", 3, "key2", "val2")) 32 | assert.Equal(t, "", buf.String()) 33 | 34 | assert.Error(t, logger.Log("v", "noint")) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /pkg/util/log/logging.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | kitLog "github.com/go-kit/log" 8 | "github.com/go-stack/stack" 9 | "k8s.io/klog" 10 | ) 11 | 12 | func NewLogger(level int) kitLog.Logger { 13 | var logger kitLog.Logger 14 | 15 | logger = kitLog.NewLogfmtLogger(kitLog.NewSyncWriter(os.Stderr)) 16 | logger = NewTrailingNilFilter(logger) 17 | logger = NewLevelFilter(level, logger) 18 | logger = NewErrorOrigin(logger) 19 | logger = kitLog.With(logger, "ts", kitLog.DefaultTimestampUTC) 20 | //pass go-kit logger to klog replacment simonpasquier/klog-gokit 21 | klog.SetLogger(kitLog.With(logger, "caller", Caller(4))) 22 | klog.ClampLevel(klog.Level(level)) 23 | 24 | return kitLog.With(logger, "caller", Caller(3)) 25 | 26 | } 27 | 28 | func Caller(depth int) kitLog.Valuer { 29 | return func() interface{} { return fmt.Sprintf("%+v", stack.Caller(depth)) } 30 | } 31 | -------------------------------------------------------------------------------- /pkg/util/log/nilfilter.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | kitlog "github.com/go-kit/log" 5 | ) 6 | 7 | type nilFilter struct { 8 | next kitlog.Logger 9 | } 10 | 11 | // NewTrailingNilFilter removes key values pairs at the end with a nil value 12 | // This mainly for getting rid of err=null trailers 13 | func NewTrailingNilFilter(logger kitlog.Logger) kitlog.Logger { 14 | return &nilFilter{next: logger} 15 | } 16 | 17 | func (l nilFilter) Log(keyvals ...interface{}) error { 18 | for i := len(keyvals) - 1; i > 0; i -= 2 { 19 | if keyvals[i] != nil { 20 | return l.next.Log(keyvals[:i+1]...) 21 | } 22 | } 23 | return l.next.Log(keyvals...) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/util/netutil/interface.go: -------------------------------------------------------------------------------- 1 | package netutil 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | ) 7 | 8 | var NotFound = errors.New("Not found") 9 | 10 | // InterfaceAddress returns the first ipv4 address of the given interface 11 | func InterfaceAddress(name string) (net.IP, error) { 12 | intf, err := net.InterfaceByName(name) 13 | if err != nil { 14 | return nil, err 15 | } 16 | addrs, err := intf.Addrs() 17 | if err != nil { 18 | return nil, err 19 | } 20 | for _, addr := range addrs { 21 | if ipnet, ok := addr.(*net.IPNet); ok { 22 | if v4 := ipnet.IP.To4(); v4 != nil { 23 | return v4, nil 24 | } 25 | } 26 | } 27 | return nil, NotFound 28 | } 29 | 30 | // PrimaryIP returns the first ipv4 Address of the interface which is used by the default route 31 | func PrimaryIP() (net.IP, error) { 32 | intf, err := DefaultInterfaceName() 33 | if err != nil { 34 | return nil, err 35 | } 36 | return InterfaceAddress(intf) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/util/netutil/interface_darwin.go: -------------------------------------------------------------------------------- 1 | package netutil 2 | 3 | import ( 4 | "os/exec" 5 | "strings" 6 | ) 7 | 8 | // DefaultInterfaceName returns the name of the interface used by the default route 9 | func DefaultInterfaceName() (string, error) { 10 | routeCmd := exec.Command("/sbin/route", "-n", "get", "0.0.0.0") 11 | output, err := routeCmd.CombinedOutput() 12 | if err != nil { 13 | return "", err 14 | } 15 | // Darwin route out format is always like this: 16 | // route to: default 17 | // destination: default 18 | // mask: default 19 | // gateway: 192.168.1.1 20 | // interface: en5 21 | lines := strings.Split(string(output), "\n") 22 | for _, line := range lines { 23 | fields := strings.Fields(line) 24 | if len(fields) >= 2 && fields[0] == "interface:" { 25 | return fields[1], nil 26 | } 27 | } 28 | 29 | return "", NotFound 30 | } 31 | -------------------------------------------------------------------------------- /pkg/util/netutil/interface_windows.go: -------------------------------------------------------------------------------- 1 | package netutil 2 | 3 | import "errors" 4 | 5 | func DefaultInterfaceName() (string, error) { 6 | return "", errors.New("Not implemented") 7 | } 8 | -------------------------------------------------------------------------------- /pkg/util/owner.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | func NewOwnerRef(owner meta_v1.Object, gvk schema.GroupVersionKind) *meta_v1.OwnerReference { 9 | return &meta_v1.OwnerReference{ 10 | APIVersion: gvk.GroupVersion().String(), 11 | Kind: gvk.Kind, 12 | Name: owner.GetName(), 13 | UID: owner.GetUID(), 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /pkg/util/pod/pod.go: -------------------------------------------------------------------------------- 1 | package pod 2 | 3 | import v1 "k8s.io/api/core/v1" 4 | 5 | //taken from https://github.com/kubernetes/kubernetes/blob/master/pkg/api/v1/pod/util.go 6 | 7 | // IsPodReady returns true if a pod is ready; false otherwise. 8 | func IsPodReady(pod *v1.Pod) bool { 9 | return IsPodReadyConditionTrue(pod.Status) 10 | } 11 | 12 | // IsPodReady retruns true if a pod is ready; false otherwise. 13 | func IsPodReadyConditionTrue(status v1.PodStatus) bool { 14 | condition := GetPodReadyCondition(status) 15 | return condition != nil && condition.Status == v1.ConditionTrue 16 | } 17 | 18 | // Extracts the pod ready condition from the given status and returns that. 19 | // Returns nil if the condition is not present. 20 | func GetPodReadyCondition(status v1.PodStatus) *v1.PodCondition { 21 | _, condition := GetPodCondition(&status, v1.PodReady) 22 | return condition 23 | } 24 | 25 | // GetPodCondition extracts the provided condition from the given status and returns that. 26 | // Returns nil and -1 if the condition is not present, and the index of the located condition. 27 | func GetPodCondition(status *v1.PodStatus, conditionType v1.PodConditionType) (int, *v1.PodCondition) { 28 | if status == nil { 29 | return -1, nil 30 | } 31 | for i := range status.Conditions { 32 | if status.Conditions[i].Type == conditionType { 33 | return i, &status.Conditions[i] 34 | } 35 | } 36 | return -1, nil 37 | } 38 | -------------------------------------------------------------------------------- /pkg/util/strings.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | func DisabledValue(value string) bool { 4 | for _, s := range []string{"false", "False", "FALSE", "off", "Off", "OFF", "Disabled", "disabled", "no", "No", "NO"} { 5 | if value == s { 6 | return true 7 | } 8 | } 9 | return false 10 | } 11 | 12 | func EnabledValue(value string) bool { 13 | for _, s := range []string{"true", "True", "TRUE", "on", "On", "ON", "enabled", "Enabled", "yes", "Yes", "YES"} { 14 | if value == s { 15 | return true 16 | } 17 | } 18 | return false 19 | } 20 | -------------------------------------------------------------------------------- /pkg/util/wait/wait.go: -------------------------------------------------------------------------------- 1 | package wait 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "k8s.io/apimachinery/pkg/util/wait" 8 | "k8s.io/client-go/tools/cache" 9 | 10 | v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1" 11 | ) 12 | 13 | var DoesNotExist = errors.New("The object was not found in the cache") 14 | 15 | func WaitForKluster(kluster *v1.Kluster, c cache.Indexer, condition func(cache *v1.Kluster) (bool, error)) error { 16 | key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(kluster) 17 | if err != nil { 18 | return err 19 | } 20 | //Wait for up to 5 seconds for the local cache to reflect the phase change 21 | return wait.Poll(50*time.Millisecond, 5*time.Second, func() (bool, error) { //nolint:staticcheck 22 | o, exists, err := c.GetByKey(key) 23 | if !exists { 24 | return false, DoesNotExist 25 | } 26 | if err != nil { 27 | return false, err 28 | } 29 | return condition(o.(*v1.Kluster)) 30 | }) 31 | } 32 | 33 | func WaitForKlusterDeletion(kluster *v1.Kluster, c cache.Indexer) error { 34 | if err := WaitForKluster(kluster, c, func(_ *v1.Kluster) (bool, error) { return false, nil }); err != DoesNotExist { 35 | return err 36 | } 37 | return nil 38 | 39 | } 40 | -------------------------------------------------------------------------------- /pkg/version/base.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | const VERSION = "1.0.0" 4 | 5 | var GitCommit = "HEAD" 6 | -------------------------------------------------------------------------------- /pkg/version/images_test.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestRegionReplacement(t *testing.T) { 10 | 11 | registry := ImageRegistry{ 12 | Versions: map[string]KlusterVersion{ 13 | "1.0.0": { 14 | Hyperkube: ImageVersion{ 15 | Repository: "something.$REGION.cloud.sap", 16 | Tag: "1.0.0", 17 | }, 18 | Kubelet: ImageVersion{ 19 | Repository: "no.region.cloud.sap", 20 | Tag: "2.0.0", 21 | }, 22 | }, 23 | }, 24 | } 25 | 26 | replaceRegionVarInRepositoryField(registry.Versions, "test") 27 | 28 | require.Equal(t, 29 | ImageVersion{Repository: "something.test.cloud.sap", Tag: "1.0.0"}, 30 | registry.Versions["1.0.0"].Hyperkube, 31 | ) 32 | require.Equal(t, 33 | ImageVersion{Repository: "no.region.cloud.sap", Tag: "2.0.0"}, 34 | registry.Versions["1.0.0"].Kubelet, 35 | ) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /pkg/wormhole/server/options.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "github.com/go-kit/log" 4 | 5 | type Options struct { 6 | Logger log.Logger 7 | 8 | //Used by the controller 9 | KubeConfig string 10 | Context string 11 | ServiceCIDR string 12 | 13 | //Used by the tunnel 14 | ClientCA string 15 | Certificate string 16 | PrivateKey string 17 | 18 | ApiPort int 19 | } 20 | -------------------------------------------------------------------------------- /scripts/coredns/clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | labels: 5 | addonmanager.kubernetes.io/mode: Reconcile 6 | kubernetes.io/bootstrapping: rbac-defaults 7 | name: system:coredns 8 | rules: 9 | - apiGroups: 10 | - "" 11 | resources: 12 | - endpoints 13 | - services 14 | - pods 15 | - namespaces 16 | verbs: 17 | - list 18 | - watch 19 | - apiGroups: 20 | - "" 21 | resources: 22 | - nodes 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /scripts/coredns/clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | addonmanager.kubernetes.io/mode: EnsureExists 6 | kubernetes.io/bootstrapping: rbac-defaults 7 | name: system:coredns 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: ClusterRole 11 | name: system:coredns 12 | subjects: 13 | - kind: ServiceAccount 14 | name: coredns 15 | namespace: kube-system 16 | -------------------------------------------------------------------------------- /scripts/coredns/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | Corefile: |2- 4 | 5 | .:53 { 6 | errors 7 | health 8 | kubernetes cluster.local in-addr.arpa ip6.arpa { 9 | pods insecure 10 | fallthrough in-addr.arpa ip6.arpa 11 | ttl 30 12 | } 13 | prometheus :9153 14 | forward . /etc/resolv.conf 15 | cache 30 16 | loop 17 | reload 18 | loadbalance 19 | } 20 | kind: ConfigMap 21 | metadata: 22 | labels: 23 | addonmanager.kubernetes.io/mode: EnsureExists 24 | name: coredns 25 | namespace: kube-system 26 | -------------------------------------------------------------------------------- /scripts/coredns/delete-kube-dns.sh: -------------------------------------------------------------------------------- 1 | kubectl -n kube-system delete svc kube-dns 2 | kubectl -n kube-system delete sa kube-dns 3 | kubectl -n kube-system delete cm kube-dns 4 | kubectl -n kube-system delete deployment kube-dns 5 | -------------------------------------------------------------------------------- /scripts/coredns/dnsutils.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: dnsutils 5 | spec: 6 | containers: 7 | - name: dnsutils 8 | image: gcr.io/kubernetes-e2e-test-images/dnsutils:1.3 9 | command: 10 | - sleep 11 | - "3600" 12 | imagePullPolicy: IfNotPresent 13 | restartPolicy: Always 14 | -------------------------------------------------------------------------------- /scripts/coredns/install-coredns.sh: -------------------------------------------------------------------------------- 1 | kubectl -n kube-system apply -f clusterrolebinding.yaml -f clusterrole.yaml -f configmap.yaml -f deployment.yaml -f serviceaccount.yaml -f service.yaml 2 | 3 | -------------------------------------------------------------------------------- /scripts/coredns/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | prometheus.io/port: "9153" 6 | prometheus.io/scrape: "true" 7 | labels: 8 | addonmanager.kubernetes.io/mode: Reconcile 9 | k8s-app: kube-dns 10 | kubernetes.io/cluster-service: "true" 11 | kubernetes.io/name: CoreDNS 12 | name: kube-dns 13 | namespace: kube-system 14 | spec: 15 | clusterIP: 198.18.128.2 16 | ports: 17 | - name: dns 18 | port: 53 19 | protocol: UDP 20 | targetPort: 53 21 | - name: dns-tcp 22 | port: 53 23 | protocol: TCP 24 | targetPort: 53 25 | - name: metrics 26 | port: 9153 27 | protocol: TCP 28 | targetPort: 9153 29 | selector: 30 | k8s-app: kube-dns 31 | sessionAffinity: None 32 | type: ClusterIP 33 | -------------------------------------------------------------------------------- /scripts/coredns/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | addonmanager.kubernetes.io/mode: Reconcile 6 | kubernetes.io/cluster-service: "true" 7 | name: coredns 8 | namespace: kube-system 9 | -------------------------------------------------------------------------------- /scripts/coredns/test-dns.sh: -------------------------------------------------------------------------------- 1 | kubectl -n default apply -f dnsutils.yaml 2 | sleep 30 3 | kubectl -n default exec -i -t dnsutils -- nslookup kubernetes.default 4 | 5 | -------------------------------------------------------------------------------- /scripts/fix-br-netfilter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | 4 | if [ "$2" == "" ]; then 5 | echo usage $0 PARENT_CONTEXT CLUSTER_FQDN 6 | exit 7 | fi 8 | unset KUBECONTEXT 9 | unset KUBENAMESPACE 10 | 11 | PARENT_CONTEXT=$1 12 | CLUSTER=$2 13 | CLUSTER_NAME=${2%-*} 14 | CLUSTER_CONFIG=$TMPDIR$CLUSTER 15 | 16 | KUBECONTEXT=$PARENT_CONTEXT ./kubeconfig.sh $2 >$CLUSTER_CONFIG 17 | echo Check status with: 18 | echo env KUBECONFIG=$CLUSTER_CONFIG kubectl --context=$CLUSTER -n default get pods 19 | ./load-br-netfilter.sh $CLUSTER $CLUSTER_CONFIG 20 | 21 | 22 | rm $CLUSTER_CONFIG 23 | -------------------------------------------------------------------------------- /scripts/fix-flatcar-update-reset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | 4 | if [ "$2" == "" ]; then 5 | echo usage $0 PARENT_CONTEXT CLUSTER_FQDN 6 | exit 7 | fi 8 | unset KUBECONTEXT 9 | unset KUBENAMESPACE 10 | 11 | PARENT_CONTEXT=$1 12 | CLUSTER=$2 13 | CLUSTER_NAME=${2%-*} 14 | CLUSTER_CONFIG=$TMPDIR$CLUSTER 15 | 16 | KUBECONTEXT=$PARENT_CONTEXT ./kubeconfig.sh $2 >$CLUSTER_CONFIG 17 | echo Check status with: 18 | echo env KUBECONFIG=$CLUSTER_CONFIG kubectl --context=$CLUSTER -n default get pods 19 | ./flatcar-update-reset.sh $CLUSTER $CLUSTER_CONFIG 20 | 21 | rm $CLUSTER_CONFIG 22 | -------------------------------------------------------------------------------- /scripts/fix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | 4 | if [ "$2" == "" ]; then 5 | echo usage $0 PARENT_CONTEXT CLUSTER_FQDN 6 | exit 7 | fi 8 | unset KUBECONTEXT 9 | unset KUBENAMESPACE 10 | 11 | PARENT_CONTEXT=$1 12 | CLUSTER=$2 13 | CLUSTER_NAME=${2%-*} 14 | CLUSTER_CONFIG=$TMPDIR$CLUSTER 15 | 16 | KUBECONTEXT=$PARENT_CONTEXT ./kubeconfig.sh $2 >$CLUSTER_CONFIG 17 | 18 | ./wormhole-fixer.sh $CLUSTER $CLUSTER_CONFIG 19 | ./kube-proxy-fixer.sh $PARENT_CONTEXT $CLUSTER $CLUSTER $CLUSTER_CONFIG 20 | 21 | echo Check status with: 22 | echo env KUBECONFIG=$CLUSTER_CONFIG kubectl --context=$CLUSTER -n default get pods 23 | KUBECONFIG=$CLUSTER_CONFIG kubectl rollout status -n default daemonset/replace-proxy-certs 24 | KUBECONFIG=$CLUSTER_CONFIG kubectl rollout status -n default daemonset/restart-wormhole 25 | 26 | KUBECONFIG=$CLUSTER_CONFIG kubectl delete -n default ds replace-proxy-certs restart-wormhole 27 | KUBECONFIG=$CLUSTER_CONFIG kubectl delete -n default secret new-proxy-certs 28 | 29 | rm $CLUSTER_CONFIG 30 | -------------------------------------------------------------------------------- /test/charts/charts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/ash 2 | 3 | pwd=$(pwd) 4 | for chart in $pwd/charts/*; do 5 | if [ -d "$chart" ]; then 6 | echo "Rendering chart in $chart ..." 7 | cd $chart 8 | # fix cross device move of overlay fs 9 | if [ -d "./charts" ]; then 10 | cp -a ./charts ./charts.bak 11 | rm -rf ./charts 12 | mv ./charts.bak ./charts 13 | fi 14 | helm dependency build --debug 15 | if [ -f test-values.yaml ]; then 16 | helm template --debug -f test-values.yaml . > /tmp/chart.yaml 17 | else 18 | helm template --debug . > /tmp/chart.yaml 19 | fi 20 | retval=$? 21 | rm -f ./charts/*.tgz 22 | if [ $retval -ne 0 ]; then 23 | echo "Rendering of template failed." 24 | exit $retval 25 | fi 26 | cd .. 27 | echo "Done." 28 | fi 29 | done 30 | -------------------------------------------------------------------------------- /test/gofmt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | VIOLATING_FILES=$(goimports -local github.com/sapcc/kubernikus -l $@ | sed /generated/d) 5 | if [ -n "$VIOLATING_FILES" ]; then 6 | echo "Goimports code is not formatted in these files:" 7 | echo "$VIOLATING_FILES" 8 | echo "Offending lines:" 9 | goimports -local github.com/sapcc/kubernikus -e -d $VIOLATING_FILES 10 | exit 1 11 | fi 12 | 13 | #Run gofmt to check for possible simplifications (-s flag) 14 | VIOLATING_FILES=$(gofmt -s -l $@ | sed -e /server.go/d -e /zz_generated.deepcopy.go/d) 15 | if [ -n "$VIOLATING_FILES" ]; then 16 | echo "Gofmt code is not formatted in these files:" 17 | echo "$VIOLATING_FILES" 18 | echo "Offending lines:" 19 | gofmt -s -d $VIOLATING_FILES 20 | exit 1 21 | fi 22 | -------------------------------------------------------------------------------- /test/patch-node-annotations/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | 8 | kitlog "github.com/go-kit/log" 9 | 10 | "github.com/sapcc/kubernikus/pkg/client/kubernetes" 11 | "github.com/sapcc/kubernikus/pkg/util" 12 | ) 13 | 14 | func main() { 15 | 16 | kubeconfig := flag.String("kubeconfig", "", "") 17 | context := flag.String("context", "", "") 18 | node := flag.String("node", "", "") 19 | key := flag.String("key", "", "") 20 | val := flag.String("val", "", "") 21 | flag.Parse() 22 | 23 | client, err := kubernetes.NewClient(*kubeconfig, *context, kitlog.NewNopLogger()) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | if *val == "" { 29 | fmt.Printf("Removing annotation %s from node %s\n", *key, *node) 30 | err := util.RemoveNodeAnnotation(*node, *key, client) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | } else { 35 | fmt.Printf("Adding/Updating annoation %s=%s on node %s\n", *key, *val, *node) 36 | err := util.AddNodeAnnotation(*node, *key, *val, client) 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/volume-test/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /test/volume-test/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "1.0" 3 | description: A Helm chart for Kubernetes 4 | name: volume-test 5 | version: 0.1.0 6 | -------------------------------------------------------------------------------- /test/volume-test/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "volume-test.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "volume-test.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "volume-test.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | -------------------------------------------------------------------------------- /test/volume-test/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for volume-test. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: busybox 9 | tag: latest 10 | pullPolicy: IfNotPresent 11 | 12 | nameOverride: "" 13 | fullnameOverride: "" 14 | 15 | --------------------------------------------------------------------------------