Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions deploy/releases/v0.0.1/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,17 @@ spec:
livenessProbe:
httpGet:
path: /healthz
port: 10268
scheme: HTTP
port: 10258
scheme: HTTPS
initialDelaySeconds: 15
failureThreshold: 60
periodSeconds: 60
readinessProbe:
httpGet:
path: /readyz
port: 10268
scheme: HTTP
initialDelaySeconds: 5
# The ccm only exposes healthz. Should work fine for indicating a functional state
path: /healthz
port: 10258
scheme: HTTPS
periodSeconds: 60
resources:
requests:
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
k8s.io/client-go v0.34.1
k8s.io/cloud-provider v0.34.1
k8s.io/component-base v0.34.1
k8s.io/controller-manager v0.34.1
k8s.io/klog/v2 v2.130.1
sigs.k8s.io/controller-runtime v0.22.4
)
Expand Down Expand Up @@ -160,7 +161,6 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiserver v0.34.1 // indirect
k8s.io/component-helpers v0.34.1 // indirect
k8s.io/controller-manager v0.34.1 // indirect
k8s.io/kms v0.34.1 // indirect
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
Expand Down
22 changes: 20 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
package main

import (
"context"
"flag"
"io"
"os"

"k8s.io/apimachinery/pkg/util/wait"
cloudprovider "k8s.io/cloud-provider"
"k8s.io/cloud-provider/app"
cloudcontrollerconfig "k8s.io/cloud-provider/app/config"
"k8s.io/cloud-provider/names"
"k8s.io/cloud-provider/options"

cloudcontrollerconfig "k8s.io/cloud-provider/app/config"

"k8s.io/component-base/cli"
cliflag "k8s.io/component-base/cli/flag"

_ "k8s.io/component-base/metrics/prometheus/clientgo" // load all the prometheus client-go plugins
_ "k8s.io/component-base/metrics/prometheus/version" // for version metric registration
genericcontrollermanager "k8s.io/controller-manager/app"
"k8s.io/controller-manager/controller"
"k8s.io/klog/v2"

"github.com/metal-stack/metal-ccm/metal"
"github.com/metal-stack/metal-ccm/pkg/controllers/health"
"github.com/metal-stack/metal-ccm/pkg/resources/constants"
"github.com/metal-stack/v"
"github.com/spf13/pflag"
Expand All @@ -34,6 +37,21 @@ func main() {
opts.KubeCloudShared.CloudProvider.Name = constants.ProviderName

controllerInitializers := app.DefaultInitFuncConstructors
controllerInitializers["metal-health"] = app.ControllerInitFuncConstructor{
InitContext: app.ControllerInitContext{
ClientName: "metal-health-controller",
},
Constructor: func(initContext app.ControllerInitContext, completedConfig *cloudcontrollerconfig.CompletedConfig, cloud cloudprovider.Interface) app.InitFunc {
return func(ctx context.Context, controllerContext genericcontrollermanager.ControllerContext) (controller.Interface, bool, error) {
m, err := metal.NewMetalClient()
if err != nil {
return nil, false, err
}

return health.New(m), true, nil
}
},
}
fss := cliflag.NamedFlagSets{
NormalizeNameFunc: cliflag.WordSepNormalizeFunc,
}
Expand Down
59 changes: 37 additions & 22 deletions metal/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,13 @@ type cloud struct {
}

func NewCloud(_ io.Reader) (cloudprovider.Interface, error) {
url := os.Getenv(constants.MetalAPIUrlEnvVar)
token := os.Getenv(constants.MetalAuthTokenEnvVar)
hmac := os.Getenv(constants.MetalAuthHMACEnvVar)
hmacAuthType := os.Getenv(constants.MetalAuthHMACAuthTypeEnvVar)
projectID := os.Getenv(constants.MetalProjectIDEnvVar)
partitionID := os.Getenv(constants.MetalPartitionIDEnvVar)
clusterID := os.Getenv(constants.MetalClusterIDEnvVar)
defaultExternalNetworkID := os.Getenv(constants.MetalDefaultExternalNetworkEnvVar)
var (
projectID = os.Getenv(constants.MetalProjectIDEnvVar)
partitionID = os.Getenv(constants.MetalPartitionIDEnvVar)
clusterID = os.Getenv(constants.MetalClusterIDEnvVar)
defaultExternalNetworkID = os.Getenv(constants.MetalDefaultExternalNetworkEnvVar)
)

loadbalancerType, err := config.LoadBalancerTypeFromString(os.Getenv(constants.Loadbalancer))
if err != nil {
return nil, err
Expand Down Expand Up @@ -76,21 +75,9 @@ func NewCloud(_ io.Reader) (cloudprovider.Interface, error) {
return nil, fmt.Errorf("environment variable %q is required", constants.MetalClusterIDEnvVar)
}

if url == "" {
return nil, fmt.Errorf("environment variable %q is required", constants.MetalAPIUrlEnvVar)
}

if (token == "") == (hmac == "") {
return nil, fmt.Errorf("environment variable %q or %q is required", constants.MetalAuthTokenEnvVar, constants.MetalAuthHMACEnvVar)
}

if hmacAuthType == "" {
hmacAuthType = "Metal-Admin"
}

metalclient, err = metalgo.NewDriver(url, token, hmac, metalgo.AuthType(hmacAuthType))
metalclient, err = NewMetalClient()
if err != nil {
return nil, fmt.Errorf("unable to initialize metal ccm:%w", err)
return nil, err
}

resp, err := metalclient.Health().Health(nil, nil)
Expand All @@ -113,6 +100,34 @@ func NewCloud(_ io.Reader) (cloudprovider.Interface, error) {
}, nil
}

func NewMetalClient() (metalgo.Client, error) {
var (
url = os.Getenv(constants.MetalAPIUrlEnvVar)
token = os.Getenv(constants.MetalAuthTokenEnvVar)
hmac = os.Getenv(constants.MetalAuthHMACEnvVar)
hmacAuthType = os.Getenv(constants.MetalAuthHMACAuthTypeEnvVar)
)

if url == "" {
return nil, fmt.Errorf("environment variable %q is required", constants.MetalAPIUrlEnvVar)
}

if (token == "") == (hmac == "") {
return nil, fmt.Errorf("environment variable %q or %q is required", constants.MetalAuthTokenEnvVar, constants.MetalAuthHMACEnvVar)
}

if hmacAuthType == "" {
hmacAuthType = "Metal-Admin"
}

m, err := metalgo.NewDriver(url, token, hmac, metalgo.AuthType(hmacAuthType))
if err != nil {
return nil, fmt.Errorf("unable to initialize metal ccm:%w", err)
}

return m, nil
}

// Initialize provides the cloud with a kubernetes client builder and may spawn goroutines
// to perform housekeeping activities within the cloud provider.
func (c *cloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stop <-chan struct{}) {
Expand Down
43 changes: 43 additions & 0 deletions pkg/controllers/health/health.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package health

import (
"fmt"
"net/http"

metalgo "github.com/metal-stack/metal-go"
"github.com/metal-stack/metal-lib/pkg/healthstatus"
"k8s.io/controller-manager/pkg/healthz"
"k8s.io/klog/v2"
)

// HealthCheck is a controller that exposes metal-api health via the CCM /healthz endpoint.
type HealthCheck struct {
client metalgo.Client
}

func New(client metalgo.Client) *HealthCheck {
return &HealthCheck{client: client}
}

func (h *HealthCheck) Name() string {
return "metal-api-health"
}

func (h *HealthCheck) HealthChecker() healthz.UnnamedHealthChecker {
return h
}

func (h *HealthCheck) Check(_ *http.Request) error {
resp, err := h.client.Health().Health(nil, nil)
if err != nil {
return fmt.Errorf("metal-api health endpoint not reachable: %w", err)
}

if resp.Payload == nil || resp.Payload.Status == nil || *resp.Payload.Status != string(healthstatus.HealthStatusHealthy) {
return fmt.Errorf("metal-api not healthy")
}

klog.Info("external health check: metal-api is healthy")

return nil
}
67 changes: 0 additions & 67 deletions pkg/controllers/housekeeping/api_probe.go

This file was deleted.

2 changes: 0 additions & 2 deletions pkg/controllers/housekeeping/housekeeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ func New(metalClient metalgo.Client, stop <-chan struct{}, lbController *loadbal

// Run runs the housekeeper...
func (h *Housekeeper) Run() error {
h.startProbeServer()

h.startTagSynching()
h.startLoadBalancerConfigSynching()
h.startSSHKeysSynching()
Expand Down