From a253df90b780c58d3fd8c12ff4f7c46f08c9dde7 Mon Sep 17 00:00:00 2001
From: Philipp Matthes
Date: Mon, 15 Sep 2025 15:27:07 +0200
Subject: [PATCH 1/2] First steps to get a multi-cluster hypervisor crd client
---
hypervisors/client.go | 76 ++++++++++++++++++
hypervisors/conf.go | 25 ++++++
hypervisors/go.mod | 54 +++++++++++++
hypervisors/go.sum | 178 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 333 insertions(+)
create mode 100644 hypervisors/client.go
create mode 100644 hypervisors/conf.go
create mode 100644 hypervisors/go.mod
create mode 100644 hypervisors/go.sum
diff --git a/hypervisors/client.go b/hypervisors/client.go
new file mode 100644
index 00000000..85e9d128
--- /dev/null
+++ b/hypervisors/client.go
@@ -0,0 +1,76 @@
+// Copyright 2025 SAP SE
+// SPDX-License-Identifier: Apache-2.0
+
+package hypervisors
+
+import (
+ "context"
+
+ "github.com/cobaltcore-dev/cortex/internal/conf"
+ v1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
+ "github.com/sapcc/go-bits/must"
+ "k8s.io/client-go/rest"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+// Client that can scrape the given hypervisor API servers.
+type HypervisorsClient interface {
+ // Create clients for all api servers in the config if not already done.
+ Init()
+ // List all hypervisors in the infrastructure.
+ ListHypervisors(ctx context.Context) ([]v1.Hypervisor, error)
+}
+
+// Default implementation of the HypervisorsClient interface.
+type hypervisorsClient struct {
+ // Config containing credentials to contact the api servers.
+ Config Conf
+ // Authenticated kubernetes clients for each api server in the config.
+ clients []client.Client
+}
+
+// Create a new client for scraping hypervisors with configuration loaded
+// from /etc/config/conf.json overridden by /etc/secrets/secrets.json
+func NewHypervisorsClient() HypervisorsClient {
+ config := conf.NewConfig[Conf]()
+ return &hypervisorsClient{Config: config}
+}
+
+// Initialize the clients if there are none yet.
+func (c *hypervisorsClient) Init() {
+ if c.clients != nil {
+ return
+ }
+ for _, apiServer := range c.Config.HypervisorAPIServers {
+ scheme := must.Return(v1.SchemeBuilder.Build())
+ clientConfig := &rest.Config{
+ Host: apiServer.Host,
+ APIPath: apiServer.APIPath,
+ BearerToken: apiServer.BearerToken,
+ TLSClientConfig: rest.TLSClientConfig{
+ CAData: []byte(apiServer.CACrt),
+ },
+ }
+ cl, err := client.New(clientConfig, client.Options{Scheme: scheme})
+ if err != nil {
+ continue
+ }
+ c.clients = append(c.clients, cl)
+ }
+}
+
+// List all hypervisors in the infrastructure.
+func (c *hypervisorsClient) ListHypervisors(ctx context.Context) ([]v1.Hypervisor, error) {
+ if c.clients == nil {
+ c.Init()
+ }
+ var hypervisors []v1.Hypervisor
+ for _, cl := range c.clients {
+ var hvList v1.HypervisorList
+ if err := cl.List(ctx, &hvList); err != nil {
+ return nil, err
+ }
+ hypervisors = append(hypervisors, hvList.Items...)
+ }
+ return hypervisors, nil
+}
diff --git a/hypervisors/conf.go b/hypervisors/conf.go
new file mode 100644
index 00000000..9747ce53
--- /dev/null
+++ b/hypervisors/conf.go
@@ -0,0 +1,25 @@
+// Copyright 2025 SAP SE
+// SPDX-License-Identifier: Apache-2.0
+
+package hypervisors
+
+type Conf struct {
+ // The remote API servers to scrape hypervisors from.
+ HypervisorAPIServers []Kubeconfig `json:"hypervisorAPIServers,omitempty"`
+}
+
+// Kubeconfig for connecting to the kubernetes apiserver.
+// See: https://pkg.go.dev/k8s.io/client-go/rest#Config
+type Kubeconfig struct {
+ // The base path to the kubernetes apiserver. Can be retrieved with:
+ // `kubectl config view --minify -o jsonpath="{.clusters[0].cluster.server}"`
+ Host string `json:"host,omitempty"`
+ // The API path that points to an API root. Defaults to `/api` if empty.
+ APIPath string `json:"apiPath,omitempty"`
+ // A valid bearer token for authentication. Can be generated with:
+ // `kubectl create token -n --duration 10m -o json`
+ BearerToken string `json:"bearerToken,omitempty"`
+ // The CA certificate for the kubernetes apiserver. Can be retrieved with:
+ // `kubectl get cm kube-root-ca.crt -o jsonpath="{['data']['ca\.crt']}"``
+ CACrt string `json:"caCrt,omitempty"`
+}
diff --git a/hypervisors/go.mod b/hypervisors/go.mod
new file mode 100644
index 00000000..59abd211
--- /dev/null
+++ b/hypervisors/go.mod
@@ -0,0 +1,54 @@
+module github.com/cobaltcore-dev/cortex/reservations
+
+go 1.25.0
+
+replace github.com/cobaltcore-dev/cortex => ../
+
+require (
+ github.com/cobaltcore-dev/cortex v0.0.0-00010101000000-000000000000
+ github.com/cobaltcore-dev/openstack-hypervisor-operator v0.0.0-20250910084739-09a2101e23e7
+ github.com/sapcc/go-bits v0.0.0-20250911190512-4d3226a57454
+ k8s.io/client-go v0.34.0
+ sigs.k8s.io/controller-runtime v0.22.1
+)
+
+require (
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+ github.com/emicklei/go-restful/v3 v3.12.2 // indirect
+ github.com/evanphx/json-patch/v5 v5.9.11 // indirect
+ github.com/fxamacker/cbor/v2 v2.9.0 // indirect
+ github.com/go-logr/logr v1.4.3 // indirect
+ github.com/go-openapi/jsonpointer v0.21.0 // indirect
+ github.com/go-openapi/jsonreference v0.21.0 // indirect
+ github.com/go-openapi/swag v0.23.1 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/google/gnostic-models v0.7.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/mailru/easyjson v0.9.0 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/x448/float16 v0.8.4 // indirect
+ go.yaml.in/yaml/v2 v2.4.2 // indirect
+ go.yaml.in/yaml/v3 v3.0.4 // indirect
+ golang.org/x/net v0.43.0 // indirect
+ golang.org/x/oauth2 v0.30.0 // indirect
+ golang.org/x/sys v0.35.0 // indirect
+ golang.org/x/term v0.34.0 // indirect
+ golang.org/x/text v0.28.0 // indirect
+ golang.org/x/time v0.12.0 // indirect
+ google.golang.org/protobuf v1.36.8 // indirect
+ gopkg.in/inf.v0 v0.9.1 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ k8s.io/api v0.34.0 // indirect
+ k8s.io/apimachinery v0.34.1 // indirect
+ k8s.io/klog/v2 v2.130.1 // indirect
+ k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
+ k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
+ sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
+ sigs.k8s.io/randfill v1.0.0 // indirect
+ sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
+ sigs.k8s.io/yaml v1.6.0 // indirect
+)
diff --git a/hypervisors/go.sum b/hypervisors/go.sum
new file mode 100644
index 00000000..a2c703d9
--- /dev/null
+++ b/hypervisors/go.sum
@@ -0,0 +1,178 @@
+github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
+github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cobaltcore-dev/openstack-hypervisor-operator v0.0.0-20250910084739-09a2101e23e7 h1:mNM3oDIHoapanAuAACGJpgnlczkV0xoulKLZNAmx8l8=
+github.com/cobaltcore-dev/openstack-hypervisor-operator v0.0.0-20250910084739-09a2101e23e7/go.mod h1:VQI3HFNt9UZC+94JDN0QAlUAO05tSdhxHSf8VEFNGkI=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
+github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
+github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
+github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
+github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
+github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
+github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
+github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
+github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
+github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
+github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
+github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
+github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
+github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw=
+github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE=
+github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
+github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
+github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
+github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
+github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+github.com/sapcc/go-bits v0.0.0-20250911190512-4d3226a57454 h1:S8LtNT93egXvEa+15yKb5ZMChuXKpd0J7iXfd4uoNlc=
+github.com/sapcc/go-bits v0.0.0-20250911190512-4d3226a57454/go.mod h1:YfKAF5oNv8UD/zkOjK+dOVft8DUZq5KphjPy2IEI+QU=
+github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
+github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
+go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
+go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
+golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
+golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
+golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
+golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
+golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
+golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
+golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
+golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
+google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
+gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE=
+k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug=
+k8s.io/apiextensions-apiserver v0.34.0 h1:B3hiB32jV7BcyKcMU5fDaDxk882YrJ1KU+ZSkA9Qxoc=
+k8s.io/apiextensions-apiserver v0.34.0/go.mod h1:hLI4GxE1BDBy9adJKxUxCEHBGZtGfIg98Q+JmTD7+g0=
+k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
+k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
+k8s.io/client-go v0.34.0 h1:YoWv5r7bsBfb0Hs2jh8SOvFbKzzxyNo0nSb0zC19KZo=
+k8s.io/client-go v0.34.0/go.mod h1:ozgMnEKXkRjeMvBZdV1AijMHLTh3pbACPvK7zFR+QQY=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
+k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
+k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
+k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/controller-runtime v0.22.1 h1:Ah1T7I+0A7ize291nJZdS1CabF/lB4E++WizgV24Eqg=
+sigs.k8s.io/controller-runtime v0.22.1/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
+sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
+sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
From 8f8aafb9c0d2ddc3cb9c69bb831ee21e7c4d16b8 Mon Sep 17 00:00:00 2001
From: Philipp Matthes
Date: Mon, 15 Sep 2025 16:28:07 +0200
Subject: [PATCH 2/2] Add functioning demo
---
Dockerfile.kubebuilder | 4 +-
Tiltfile | 10 +++
hypervisors/demo/chart/.helmignore | 25 +++++++
hypervisors/demo/chart/Chart.lock | 6 ++
hypervisors/demo/chart/Chart.yaml | 14 ++++
.../demo/chart/charts/owner-info-1.0.0.tgz | Bin 0 -> 2139 bytes
hypervisors/demo/chart/templates/_helpers.tpl | 50 ++++++++++++++
hypervisors/demo/chart/templates/demo.yaml | 63 ++++++++++++++++++
hypervisors/demo/chart/values.yaml | 25 +++++++
hypervisors/{ => library}/client.go | 17 ++---
hypervisors/{ => library}/conf.go | 4 +-
hypervisors/main.go | 24 +++++++
12 files changed, 231 insertions(+), 11 deletions(-)
create mode 100644 hypervisors/demo/chart/.helmignore
create mode 100644 hypervisors/demo/chart/Chart.lock
create mode 100644 hypervisors/demo/chart/Chart.yaml
create mode 100644 hypervisors/demo/chart/charts/owner-info-1.0.0.tgz
create mode 100644 hypervisors/demo/chart/templates/_helpers.tpl
create mode 100644 hypervisors/demo/chart/templates/demo.yaml
create mode 100644 hypervisors/demo/chart/values.yaml
rename hypervisors/{ => library}/client.go (88%)
rename hypervisors/{ => library}/conf.go (91%)
create mode 100644 hypervisors/main.go
diff --git a/Dockerfile.kubebuilder b/Dockerfile.kubebuilder
index 16d562ea..0fa41894 100644
--- a/Dockerfile.kubebuilder
+++ b/Dockerfile.kubebuilder
@@ -4,6 +4,8 @@ ARG TARGETOS
ARG TARGETARCH
# Path of our go.mod
ARG GO_MOD_PATH=.
+ARG GO_MAIN_PATH=cmd/main.go
+ENV GO_MAIN_PATH=${GO_MAIN_PATH}
ARG GOCACHE=/root/.cache/go-build
ENV GOCACHE=${GOCACHE}
@@ -28,7 +30,7 @@ ENV GOOS=${TARGETOS:-linux}
ENV GOARCH=${TARGETARCH}
RUN --mount=type=cache,target=/go/pkg/mod/ \
--mount=type=cache,target=${GOCACHE} \
- go build -a -o manager cmd/main.go
+ go build -a -o manager ${GO_MAIN_PATH}
# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
diff --git a/Tiltfile b/Tiltfile
index da22cf74..cc1ab702 100644
--- a/Tiltfile
+++ b/Tiltfile
@@ -27,6 +27,16 @@ def kubebuilder_binary_files(path):
"""
return [path + '/cmd', path + '/api', path + '/internal', path + '/go.mod', path + '/go.sum']
+########### Hypervisors kubernetes client demo
+docker_build('ghcr.io/cobaltcore-dev/cortex-hypervisors-demo', '.',
+ dockerfile='Dockerfile.kubebuilder',
+ build_args={'GO_MOD_PATH': 'hypervisors', 'GO_MAIN_PATH': '.'},
+ only=['hypervisors/', 'internal/', 'go.mod', 'go.sum'],
+)
+local('sh helm/sync.sh hypervisors/demo/chart')
+k8s_yaml(helm('hypervisors/demo/chart', name='cortex-hypervisors-demo', values=[tilt_values]))
+k8s_resource('demo', labels=['Demo'])
+
########### Reservations Operator & CRDs
docker_build('ghcr.io/cobaltcore-dev/cortex-reservations-operator', '.',
dockerfile='Dockerfile.kubebuilder',
diff --git a/hypervisors/demo/chart/.helmignore b/hypervisors/demo/chart/.helmignore
new file mode 100644
index 00000000..7d92f7fb
--- /dev/null
+++ b/hypervisors/demo/chart/.helmignore
@@ -0,0 +1,25 @@
+# Patterns to ignore when building Helm packages.
+# Operating system files
+.DS_Store
+
+# Version control directories
+.git/
+.gitignore
+.bzr/
+.hg/
+.hgignore
+.svn/
+
+# Backup and temporary files
+*.swp
+*.tmp
+*.bak
+*.orig
+*~
+
+# IDE and editor-related files
+.idea/
+.vscode/
+
+# Helm chart artifacts
+dist/chart/*.tgz
diff --git a/hypervisors/demo/chart/Chart.lock b/hypervisors/demo/chart/Chart.lock
new file mode 100644
index 00000000..db4c5823
--- /dev/null
+++ b/hypervisors/demo/chart/Chart.lock
@@ -0,0 +1,6 @@
+dependencies:
+- name: owner-info
+ repository: oci://ghcr.io/sapcc/helm-charts
+ version: 1.0.0
+digest: sha256:7643f231cc4ebda347fd12ec62fe4445c280e2b71d27eec555f3025290f5038f
+generated: "2025-08-26T10:55:05.888651+02:00"
diff --git a/hypervisors/demo/chart/Chart.yaml b/hypervisors/demo/chart/Chart.yaml
new file mode 100644
index 00000000..f2f54869
--- /dev/null
+++ b/hypervisors/demo/chart/Chart.yaml
@@ -0,0 +1,14 @@
+apiVersion: v2
+name: cortex-hypervisors-client-demo
+description: A Helm chart to distribute the cortex hypervisors client demo.
+type: application
+version: 0.1.0
+appVersion: "latest"
+icon: "https://example.com/icon.png"
+dependencies:
+ # Owner info adds a configmap to the kubernetes cluster with information on
+ # the service owner. This makes it easier to find out who to contact in case
+ # of issues. See: https://github.com/sapcc/helm-charts/pkgs/container/helm-charts%2Fowner-info
+ - name: owner-info
+ repository: oci://ghcr.io/sapcc/helm-charts
+ version: 1.0.0
diff --git a/hypervisors/demo/chart/charts/owner-info-1.0.0.tgz b/hypervisors/demo/chart/charts/owner-info-1.0.0.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..2032ead97387e7374750a71b0ffc726645bc5b7e
GIT binary patch
literal 2139
zcmV-h2&DHPiwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc
zVQyr3R8em|NM&qo0PI=abKAHP_cQ;B9jP<6Rq;bUN15v7CXH{JwC5({`qGC?Is(b1
zh%*Q<04Rl(?tkw9ASH^j>$>MlE*ay+qy_93_T#rpvEnFw$}N{~Mh>e|9uEpH9!BXVd8%l8IUYftP}3ba6>a&^ghN2(Ag`3hXu#i%LUc9O+oO
z^MMEOS_4&3AmJLcfEb}tm;y2MQY?|D$del48idYKIsz*2I4Q6zhPP}*odi)3d0*Oi
z-@xj`|B|o@)o+jh?uq}&B%1c)|Jmdz{y)na!XQUh_%`JHO0oeImC`0g35QqL*Kbe$
zaSA_O{Rl2K#FQ^!m7_!x$rLl>?sn6i}*c-y2Ak^j!
zlxkpd1K}WS)*%cSRT@MFCi<0mAdEqYqGSlGTtIua$2fr3nKy)@Vw#$*&549?$dQ$>
zqB;kvRD}v8E{IHxDu#wVK86gDK_<~^MtQ=j6itpQ2(O(m-q5_)q5?*5Fz`BB%;5b8
z&uFHqvJ_JL3n{A7>L&4HMJ1+?2?|k+8xW+D)J+Uoh;GMy{I9Vn8PTZ1#MbzNl#f#k?#ch(olhsd{6C#M;lIzb)@vVV1}BuK_z8k{
z76TmCgpJ8i`00}9B!|Jja;;1C{VWX4FI?zT@PGNm12EJwLKzGiO2N**f_<765mKdW
zCX$8-kZ!#hSdxYy@^t%ri>SSX+mrM?@&6t9fAT;4@edD?b&qII)DGlb5=K*7KS-i&
zExSwTmPeB{?-Xoo*d$dD)u
z{!R)6D{4Bm0f|HjPGVL=
zRr*@^?vxYHR3mMv+k$WT?X2N%yMy%_K2|~_1g@uTv@^nbtzvy#vR=czG2n=9uZ3pT
zZBOtcL~z28w`YS6FgQJR2llYSdJR7d%3&~u!EJ|N1Da}vhdj+C8Z`iVwf|85gTOcx
zzwedyw{2lJmwn*2cgW-W{=*z=2>(RjXxzizXj^ic2Ct<
z;u;7Pl_nO;tT4`nxG~7o;up)nHIpL#853lPDV8vLi)?8hxknU4N>ptAqcpet2P@3@6Crof0H>v%;8KHMDBlJe2mt=`V&qgD7!%ebO2=kp*bK7^Dysky*
zGh8BTL_47Y2D=G>Q?C})u|&@aN%`V5fY+Jr`I`b{s1aW8graUrQHx3*u+XL+nZql}
z7%WhG+xf)Stgi32MFtW^R33QVa0ovsvOv#!k*53F^rxm;IM(gdb-tV9&mp4>XP!5o
z&&|8nmDyxg9}Y&WCUHwFg`!%>dIln)voO5DvP2eO<)`RRf`o}G4HPNE#6J2a)7zul
z!ZWn?+7UihkW^Bdn%B>ZM<8%$+
zI!8VRlDfH6XN28?k3)lX2A>BO+Yz`8G#=YVXcmSGs`Dxi5>bSTlt~iqcB)WI#IPVr
zqYPE8G!`L#B1OsYxCaca34Qo0;RqK3o7YS72`(#hydaz(7Q}{_d+nCB_J`&l&cN3>
zLM9j!D+8P|iY#>)g_I={*&PDy`JKWtLd+o~#@56AVsJa*FB1e_y~
zBBM*R6fWMpc?(M_m;>EoyV>4{D{3b47OFLvBeM0J7oe)x6bMB#npg_17+Iah&`fd1
zZBxf|$VhUnu5rYYT
z3BIt7EOyb*c55fG&^e@3Vxn1Xc~RcJo;qtbS&VX+2W`p^Tvzsf;Hp))-!FVFq%4iZQ~{~9D2S#BVXTG{WhP|d;%8TlEOno9qp=+)6{Xx*Ciqm=QfZ>?*7T2Jd~J*}^Q
R{U-nb|Nj_?q>KP4004X22~_|9
literal 0
HcmV?d00001
diff --git a/hypervisors/demo/chart/templates/_helpers.tpl b/hypervisors/demo/chart/templates/_helpers.tpl
new file mode 100644
index 00000000..8ab463a1
--- /dev/null
+++ b/hypervisors/demo/chart/templates/_helpers.tpl
@@ -0,0 +1,50 @@
+{{- define "chart.name" -}}
+{{- if .Chart }}
+ {{- if .Chart.Name }}
+ {{- .Chart.Name | trunc 63 | trimSuffix "-" }}
+ {{- else if .Values.nameOverride }}
+ {{ .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+ {{- else }}
+ reservations
+ {{- end }}
+{{- else }}
+ reservations
+{{- end }}
+{{- end }}
+
+
+{{- define "chart.labels" -}}
+{{- if .Chart.AppVersion -}}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+{{- if .Chart.Version }}
+helm.sh/chart: {{ .Chart.Version | quote }}
+{{- end }}
+app.kubernetes.io/name: {{ include "chart.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+
+{{- define "chart.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "chart.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+
+{{- define "chart.hasMutatingWebhooks" -}}
+{{- $hasMutating := false }}
+{{- range . }}
+ {{- if eq .type "mutating" }}
+ $hasMutating = true }}{{- end }}
+{{- end }}
+{{ $hasMutating }}}}{{- end }}
+
+
+{{- define "chart.hasValidatingWebhooks" -}}
+{{- $hasValidating := false }}
+{{- range . }}
+ {{- if eq .type "validating" }}
+ $hasValidating = true }}{{- end }}
+{{- end }}
+{{ $hasValidating }}}}{{- end }}
diff --git a/hypervisors/demo/chart/templates/demo.yaml b/hypervisors/demo/chart/templates/demo.yaml
new file mode 100644
index 00000000..54cca0b4
--- /dev/null
+++ b/hypervisors/demo/chart/templates/demo.yaml
@@ -0,0 +1,63 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: demo
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "chart.labels" . | nindent 4 }}
+spec:
+ selector:
+ matchLabels:
+ {{- include "chart.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ annotations:
+ kubectl.kubernetes.io/default-container: demo
+ labels:
+ {{- include "chart.labels" . | nindent 8 }}
+ spec:
+ containers:
+ - name: demo
+ command:
+ # Created by kubebuilder Dockerfile
+ - /manager
+ image: {{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}
+ volumeMounts:
+ - name: demo-config-volume
+ mountPath: /etc/config
+ - name: demo-secrets-volume
+ mountPath: /etc/secrets
+ readOnly: true
+ volumes:
+ # Custom values to configure the demo.
+ - name: demo-config-volume
+ configMap:
+ name: demo-config
+ - name: demo-secrets-volume
+ secret:
+ secretName: demo-secrets
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: demo-config
+data:
+ conf.json: |-
+ {{- if .Values.demo.conf }}
+ {{ toJson .Values.demo.conf }}
+ {{- else }}
+ {}
+ {{- end }}
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: demo-secrets
+type: Opaque
+data:
+ secrets.json: |-
+ {{- if .Values.demo.secrets }}
+ {{ toJson .Values.demo.secrets | b64enc }}
+ {{- else }}
+ {{ "{}" | b64enc }}
+ {{- end }}
\ No newline at end of file
diff --git a/hypervisors/demo/chart/values.yaml b/hypervisors/demo/chart/values.yaml
new file mode 100644
index 00000000..6ca92350
--- /dev/null
+++ b/hypervisors/demo/chart/values.yaml
@@ -0,0 +1,25 @@
+# This file is safe from kubebuilder edit --plugins=helm/v1-alpha
+# If you want to re-generate, add the --force flag.
+
+owner-info:
+ enabled: true
+ helm-chart-url: "https://github.com/cobaltcore-dev/cortex/hypervisors/demo/dist/chart"
+ maintainers:
+ - "p.matthes@sap.com"
+ - "markus.wieland@sap.com"
+ - "arno.uhlig@sap.com"
+ support-group: "workload-management"
+ service: "cortex-hypervisors-demo"
+
+image:
+ repository: ghcr.io/cobaltcore-dev/cortex-hypervisors-demo
+
+demo:
+ # Default configuration.
+ conf: {}
+ # Config provided here will override the config provided above.
+ secrets:
+ apiservers:
+ - host:
+ bearerToken:
+ caCrt:
diff --git a/hypervisors/client.go b/hypervisors/library/client.go
similarity index 88%
rename from hypervisors/client.go
rename to hypervisors/library/client.go
index 85e9d128..7fa9db7e 100644
--- a/hypervisors/client.go
+++ b/hypervisors/library/client.go
@@ -1,7 +1,7 @@
// Copyright 2025 SAP SE
// SPDX-License-Identifier: Apache-2.0
-package hypervisors
+package library
import (
"context"
@@ -41,20 +41,21 @@ func (c *hypervisorsClient) Init() {
if c.clients != nil {
return
}
- for _, apiServer := range c.Config.HypervisorAPIServers {
+ for _, apiServer := range c.Config.APIServers {
scheme := must.Return(v1.SchemeBuilder.Build())
- clientConfig := &rest.Config{
+ apiPath := apiServer.APIPath
+ if apiPath == "" {
+ apiPath = "/api"
+ }
+ rc := &rest.Config{
Host: apiServer.Host,
- APIPath: apiServer.APIPath,
+ APIPath: apiPath,
BearerToken: apiServer.BearerToken,
TLSClientConfig: rest.TLSClientConfig{
CAData: []byte(apiServer.CACrt),
},
}
- cl, err := client.New(clientConfig, client.Options{Scheme: scheme})
- if err != nil {
- continue
- }
+ cl := must.Return(client.New(rc, client.Options{Scheme: scheme}))
c.clients = append(c.clients, cl)
}
}
diff --git a/hypervisors/conf.go b/hypervisors/library/conf.go
similarity index 91%
rename from hypervisors/conf.go
rename to hypervisors/library/conf.go
index 9747ce53..f4774f27 100644
--- a/hypervisors/conf.go
+++ b/hypervisors/library/conf.go
@@ -1,11 +1,11 @@
// Copyright 2025 SAP SE
// SPDX-License-Identifier: Apache-2.0
-package hypervisors
+package library
type Conf struct {
// The remote API servers to scrape hypervisors from.
- HypervisorAPIServers []Kubeconfig `json:"hypervisorAPIServers,omitempty"`
+ APIServers []Kubeconfig `json:"apiservers,omitempty"`
}
// Kubeconfig for connecting to the kubernetes apiserver.
diff --git a/hypervisors/main.go b/hypervisors/main.go
new file mode 100644
index 00000000..fce79fdd
--- /dev/null
+++ b/hypervisors/main.go
@@ -0,0 +1,24 @@
+// Copyright 2025 SAP SE
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/cobaltcore-dev/cortex/reservations/library"
+ "github.com/sapcc/go-bits/must"
+)
+
+func main() {
+ fmt.Println("Starting hypervisors client demo...")
+ client := library.NewHypervisorsClient()
+ client.Init()
+ for {
+ hypervisors := must.Return(client.ListHypervisors(context.Background()))
+ fmt.Printf("Found %d hypervisors\n", len(hypervisors))
+ time.Sleep(10 * time.Second)
+ }
+}