diff --git a/hcloud/cloud.go b/hcloud/cloud.go index 0de19b5b..32eaf9a8 100644 --- a/hcloud/cloud.go +++ b/hcloud/cloud.go @@ -51,9 +51,10 @@ type cloud struct { cfg config.HCCMConfiguration recorder record.EventRecorder networkID int64 + cidr string } -func NewCloud() (cloudprovider.Interface, error) { +func NewCloud(cidr string) (cloudprovider.Interface, error) { const op = "hcloud/newCloud" metrics.OperationCalled.WithLabelValues(op).Inc() @@ -131,6 +132,7 @@ func NewCloud() (cloudprovider.Interface, error) { robotClient: robotClient, cfg: cfg, networkID: networkID, + cidr: cidr, }, nil } @@ -192,7 +194,12 @@ func (c *cloud) Routes() (cloudprovider.Routes, bool) { return nil, false } - r, err := newRoutes(c.client, c.networkID) + r, err := newRoutes( + c.client, + c.networkID, + c.cidr, + c.recorder, + ) if err != nil { klog.ErrorS(err, "create routes provider", "networkID", c.networkID) return nil, false diff --git a/hcloud/cloud_test.go b/hcloud/cloud_test.go index 7b2af7c5..d27e944e 100644 --- a/hcloud/cloud_test.go +++ b/hcloud/cloud_test.go @@ -92,7 +92,7 @@ func TestNewCloud(t *testing.T) { ) }) - _, err := NewCloud() + _, err := NewCloud(DefaultClusterCIDR) assert.NoError(t, err) } @@ -104,7 +104,7 @@ func TestNewCloudConnectionNotPossible(t *testing.T) { ) defer resetEnv() - _, err := NewCloud() + _, err := NewCloud(DefaultClusterCIDR) assert.EqualError(t, err, `hcloud/newCloud: Get "http://127.0.0.1:4711/v1/servers?": dial tcp 127.0.0.1:4711: connect: connection refused`) } @@ -132,7 +132,7 @@ func TestNewCloudInvalidToken(t *testing.T) { ) }) - _, err := NewCloud() + _, err := NewCloud(DefaultClusterCIDR) assert.EqualError(t, err, "hcloud/newCloud: unable to authenticate (unauthorized)") } @@ -195,7 +195,7 @@ func TestCloud(t *testing.T) { ) }) - cloud, err := NewCloud() + cloud, err := NewCloud(DefaultClusterCIDR) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -252,7 +252,7 @@ func TestCloud(t *testing.T) { ) defer resetEnv() - c, err := NewCloud() + c, err := NewCloud(DefaultClusterCIDR) if err != nil { t.Errorf("%s", err) } diff --git a/hcloud/routes.go b/hcloud/routes.go index 0aa52041..d575a807 100644 --- a/hcloud/routes.go +++ b/hcloud/routes.go @@ -8,7 +8,10 @@ import ( "time" "golang.org/x/time/rate" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" cloudprovider "k8s.io/cloud-provider" "k8s.io/klog/v2" @@ -17,6 +20,8 @@ import ( "github.com/hetznercloud/hcloud-go/v2/hcloud" ) +const DefaultClusterCIDR = "10.244.0.0/16" + var ( serversCacheMissRefreshRate = rate.Every(30 * time.Second) ) @@ -25,9 +30,11 @@ type routes struct { client *hcloud.Client network *hcloud.Network serverCache *hcops.AllServersCache + clusterCIDR *net.IPNet + recorder record.EventRecorder } -func newRoutes(client *hcloud.Client, networkID int64) (*routes, error) { +func newRoutes(client *hcloud.Client, networkID int64, clusterCIDR string, recorder record.EventRecorder) (*routes, error) { const op = "hcloud/newRoutes" metrics.OperationCalled.WithLabelValues(op).Inc() @@ -39,6 +46,11 @@ func newRoutes(client *hcloud.Client, networkID int64) (*routes, error) { return nil, fmt.Errorf("network not found: %d", networkID) } + _, cidr, err := net.ParseCIDR(clusterCIDR) + if err != nil { + return nil, err + } + return &routes{ client: client, network: networkObj, @@ -49,6 +61,8 @@ func newRoutes(client *hcloud.Client, networkID int64) (*routes, error) { Network: networkObj, CacheMissRefreshLimiter: rate.NewLimiter(serversCacheMissRefreshRate, 1), }, + clusterCIDR: cidr, + recorder: recorder, }, nil } @@ -119,15 +133,31 @@ func (r *routes) CreateRoute(ctx context.Context, clusterName string, nameHint s return fmt.Errorf("%s: %w", op, err) } - hNetSize, _ := r.network.IPRange.Mask.Size() + clusterNetSize, _ := r.clusterCIDR.Mask.Size() destNetSize, _ := cidr.Mask.Size() - if !(r.network.IPRange.Contains(cidr.IP) && destNetSize >= hNetSize) { - return fmt.Errorf( - "route CIDR %s is not contained within CIDR %s of hcloud network %d", + if !(r.clusterCIDR.Contains(cidr.IP) && destNetSize >= clusterNetSize) { + node := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: string(route.TargetNode), + Namespace: "", + }, + } + // Event is only visible via `kubectl get events` and not `kubectl describe node`, + // as we do not have the UID here and `kubectl describe node` filters by UID. + // Because of this behavior we are also dispatching a log message. + r.recorder.Eventf( + node, + corev1.EventTypeWarning, + "ClusterCIDRMisconfigured", + "route CIDR %s is not contained within cluster CIDR %s", + route.DestinationCIDR, + r.clusterCIDR.String(), + ) + klog.Warningf( + "route CIDR %s is not contained within cluster CIDR %s", route.DestinationCIDR, - r.network.IPRange.String(), - r.network.ID, + r.clusterCIDR.String(), ) } diff --git a/hcloud/routes_test.go b/hcloud/routes_test.go index fd7bd032..3da0e176 100644 --- a/hcloud/routes_test.go +++ b/hcloud/routes_test.go @@ -70,7 +70,7 @@ func TestRoutes_CreateRoute(t *testing.T) { }, }) }) - routes, err := newRoutes(env.Client, 1) + routes, err := newRoutes(env.Client, 1, DefaultClusterCIDR, env.Recorder) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -83,15 +83,6 @@ func TestRoutes_CreateRoute(t *testing.T) { if err != nil { t.Fatalf("Unexpected error: %v", err) } - - err = routes.CreateRoute(context.TODO(), "my-cluster", "routeFail", &cloudprovider.Route{ - Name: "route", - TargetNode: "node15", - DestinationCIDR: "172.16.0.0/16", - }) - if err == nil { - t.Fatalf("Expected an error because DestinationCIDR is not within the cluster CIDR, but received nil instead") - } } func TestRoutes_ListRoutes(t *testing.T) { @@ -128,7 +119,7 @@ func TestRoutes_ListRoutes(t *testing.T) { }, }) }) - routes, err := newRoutes(env.Client, 1) + routes, err := newRoutes(env.Client, 1, DefaultClusterCIDR, env.Recorder) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -196,7 +187,7 @@ func TestRoutes_DeleteRoute(t *testing.T) { }, }) }) - routes, err := newRoutes(env.Client, 1) + routes, err := newRoutes(env.Client, 1, DefaultClusterCIDR, env.Recorder) if err != nil { t.Fatalf("Unexpected error: %v", err) } diff --git a/main.go b/main.go index 30578593..ac86dd00 100644 --- a/main.go +++ b/main.go @@ -59,7 +59,7 @@ func main() { } func cloudInitializer(config *config.CompletedConfig) cloudprovider.Interface { - cloud, err := hcloud.NewCloud() + cloud, err := hcloud.NewCloud(config.ComponentConfig.KubeCloudShared.ClusterCIDR) if err != nil { klog.Fatalf("Cloud provider could not be initialized: %v", err) }