diff --git a/src/k8s/cmd/k8s/k8s.go b/src/k8s/cmd/k8s/k8s.go index 921c66e6c..65f92c25b 100644 --- a/src/k8s/cmd/k8s/k8s.go +++ b/src/k8s/cmd/k8s/k8s.go @@ -4,11 +4,19 @@ import ( "time" cmdutil "github.com/canonical/k8s/cmd/util" + "github.com/canonical/k8s/pkg/k8sd/features" "github.com/spf13/cobra" ) var ( - featureList = []string{"network", "dns", "gateway", "ingress", "local-storage", "load-balancer"} + featureList = []string{ + string(features.Network), + string(features.DNS), + string(features.Gateway), + string(features.Ingress), + string(features.LocalStorage), + string(features.LoadBalancer), + } outputFormatter cmdutil.Formatter ) diff --git a/src/k8s/cmd/k8s/k8s_bootstrap.go b/src/k8s/cmd/k8s/k8s_bootstrap.go index 41dddbc96..968ca82f2 100644 --- a/src/k8s/cmd/k8s/k8s_bootstrap.go +++ b/src/k8s/cmd/k8s/k8s_bootstrap.go @@ -14,6 +14,7 @@ import ( apiv1 "github.com/canonical/k8s/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/canonical/k8s/pkg/config" + "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/utils" "github.com/spf13/cobra" "gopkg.in/yaml.v2" @@ -185,22 +186,22 @@ func getConfigInteractively(stdin io.Reader, stdout io.Writer, stderr io.Writer) stdin, stdout, stderr, "Which features would you like to enable?", featureList, - "network, dns, gateway, local-storage", + fmt.Sprintf("%s, %s, %s, %s", features.Network, features.DNS, features.Gateway, features.LocalStorage), nil, ) for _, component := range strings.FieldsFunc(components, func(r rune) bool { return unicode.IsSpace(r) || r == ',' }) { switch component { - case "network": + case string(features.Network): config.ClusterConfig.Network.Enabled = utils.Pointer(true) - case "dns": + case string(features.DNS): config.ClusterConfig.DNS.Enabled = utils.Pointer(true) - case "ingress": + case string(features.Ingress): config.ClusterConfig.Ingress.Enabled = utils.Pointer(true) - case "load-balancer": + case string(features.LoadBalancer): config.ClusterConfig.LoadBalancer.Enabled = utils.Pointer(true) - case "gateway": + case string(features.Gateway): config.ClusterConfig.Gateway.Enabled = utils.Pointer(true) - case "local-storage": + case string(features.LocalStorage): config.ClusterConfig.LocalStorage.Enabled = utils.Pointer(true) } } diff --git a/src/k8s/cmd/k8s/k8s_disable.go b/src/k8s/cmd/k8s/k8s_disable.go index ddd7889c0..8f8f227fd 100644 --- a/src/k8s/cmd/k8s/k8s_disable.go +++ b/src/k8s/cmd/k8s/k8s_disable.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/utils" api "github.com/canonical/k8s/api/v1" @@ -42,31 +43,31 @@ func newDisableCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { for _, feature := range args { switch feature { - case "network": + case string(features.Network): config.Network = api.NetworkConfig{ Enabled: utils.Pointer(false), } - case "dns": + case string(features.DNS): config.DNS = api.DNSConfig{ Enabled: utils.Pointer(false), } - case "gateway": + case string(features.Gateway): config.Gateway = api.GatewayConfig{ Enabled: utils.Pointer(false), } - case "ingress": + case string(features.Ingress): config.Ingress = api.IngressConfig{ Enabled: utils.Pointer(false), } - case "local-storage": + case string(features.LocalStorage): config.LocalStorage = api.LocalStorageConfig{ Enabled: utils.Pointer(false), } - case "load-balancer": + case string(features.LoadBalancer): config.LoadBalancer = api.LoadBalancerConfig{ Enabled: utils.Pointer(false), } - case "metrics-server": + case string(features.MetricsServer): config.MetricsServer = api.MetricsServerConfig{ Enabled: utils.Pointer(false), } diff --git a/src/k8s/cmd/k8s/k8s_disable_test.go b/src/k8s/cmd/k8s/k8s_disable_test.go index ee424459a..e03903469 100644 --- a/src/k8s/cmd/k8s/k8s_disable_test.go +++ b/src/k8s/cmd/k8s/k8s_disable_test.go @@ -8,6 +8,7 @@ import ( "github.com/canonical/k8s/cmd/k8s" cmdutil "github.com/canonical/k8s/cmd/util" k8sdmock "github.com/canonical/k8s/pkg/client/k8sd/mock" + "github.com/canonical/k8s/pkg/k8sd/features" snapmock "github.com/canonical/k8s/pkg/snap/mock" "github.com/canonical/k8s/pkg/utils" . "github.com/onsi/gomega" @@ -30,7 +31,7 @@ func TestDisableCmd(t *testing.T) { }, { name: "one", - funcs: []string{"gateway"}, + funcs: []string{string(features.Gateway)}, expectedCall: apiv1.UpdateClusterConfigRequest{ Config: apiv1.UserFacingClusterConfig{ Gateway: apiv1.GatewayConfig{Enabled: utils.Pointer(false)}, @@ -40,7 +41,7 @@ func TestDisableCmd(t *testing.T) { }, { name: "multiple", - funcs: []string{"load-balancer", "gateway"}, + funcs: []string{string(features.LoadBalancer), string(features.Gateway)}, expectedCall: apiv1.UpdateClusterConfigRequest{ Config: apiv1.UserFacingClusterConfig{ Gateway: apiv1.GatewayConfig{Enabled: utils.Pointer(false)}, diff --git a/src/k8s/cmd/k8s/k8s_enable.go b/src/k8s/cmd/k8s/k8s_enable.go index 8d9b56dbc..fbe5cbbb4 100644 --- a/src/k8s/cmd/k8s/k8s_enable.go +++ b/src/k8s/cmd/k8s/k8s_enable.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/utils" api "github.com/canonical/k8s/api/v1" @@ -42,31 +43,31 @@ func newEnableCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { for _, feature := range args { switch feature { - case "network": + case string(features.Network): config.Network = api.NetworkConfig{ Enabled: utils.Pointer(true), } - case "dns": + case string(features.DNS): config.DNS = api.DNSConfig{ Enabled: utils.Pointer(true), } - case "gateway": + case string(features.Gateway): config.Gateway = api.GatewayConfig{ Enabled: utils.Pointer(true), } - case "ingress": + case string(features.Ingress): config.Ingress = api.IngressConfig{ Enabled: utils.Pointer(true), } - case "local-storage": + case string(features.LocalStorage): config.LocalStorage = api.LocalStorageConfig{ Enabled: utils.Pointer(true), } - case "load-balancer": + case string(features.LoadBalancer): config.LoadBalancer = api.LoadBalancerConfig{ Enabled: utils.Pointer(true), } - case "metrics-server": + case string(features.MetricsServer): config.MetricsServer = api.MetricsServerConfig{ Enabled: utils.Pointer(true), } diff --git a/src/k8s/cmd/k8s/k8s_enable_test.go b/src/k8s/cmd/k8s/k8s_enable_test.go index 59df399bb..efba1b262 100644 --- a/src/k8s/cmd/k8s/k8s_enable_test.go +++ b/src/k8s/cmd/k8s/k8s_enable_test.go @@ -8,6 +8,7 @@ import ( "github.com/canonical/k8s/cmd/k8s" cmdutil "github.com/canonical/k8s/cmd/util" k8sdmock "github.com/canonical/k8s/pkg/client/k8sd/mock" + "github.com/canonical/k8s/pkg/k8sd/features" snapmock "github.com/canonical/k8s/pkg/snap/mock" "github.com/canonical/k8s/pkg/utils" . "github.com/onsi/gomega" @@ -30,7 +31,7 @@ func TestK8sEnableCmd(t *testing.T) { }, { name: "one", - funcs: []string{"gateway"}, + funcs: []string{string(features.Gateway)}, expectedCall: apiv1.UpdateClusterConfigRequest{ Config: apiv1.UserFacingClusterConfig{ Gateway: apiv1.GatewayConfig{Enabled: utils.Pointer(true)}, @@ -40,7 +41,7 @@ func TestK8sEnableCmd(t *testing.T) { }, { name: "multiple", - funcs: []string{"load-balancer", "gateway"}, + funcs: []string{string(features.LoadBalancer), string(features.Gateway)}, expectedCall: apiv1.UpdateClusterConfigRequest{ Config: apiv1.UserFacingClusterConfig{ Gateway: apiv1.GatewayConfig{Enabled: utils.Pointer(true)}, diff --git a/src/k8s/cmd/k8s/k8s_get.go b/src/k8s/cmd/k8s/k8s_get.go index 20eb1422e..9e20aea7f 100644 --- a/src/k8s/cmd/k8s/k8s_get.go +++ b/src/k8s/cmd/k8s/k8s_get.go @@ -8,6 +8,7 @@ import ( apiv1 "github.com/canonical/k8s/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" + "github.com/canonical/k8s/pkg/k8sd/features" "github.com/spf13/cobra" ) @@ -58,61 +59,61 @@ func newGetCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { switch key { case "": output = config - case "network": + case string(features.Network): output = config.Network - case "dns": + case string(features.DNS): output = config.DNS - case "gateway": + case string(features.Gateway): output = config.Gateway - case "ingress": + case string(features.Ingress): output = config.Ingress - case "local-storage": + case string(features.LocalStorage): output = config.LocalStorage - case "load-balancer": + case string(features.LoadBalancer): output = config.LoadBalancer - case "network.enabled": + case fmt.Sprintf("%s.enabled", features.Network): output = config.Network.GetEnabled() - case "dns.enabled": + case fmt.Sprintf("%s.enabled", features.DNS): output = config.DNS.GetEnabled() - case "dns.upstream-nameservers": + case fmt.Sprintf("%s.upstream-nameservers", features.DNS): output = config.DNS.GetUpstreamNameservers() - case "dns.cluster-domain": + case fmt.Sprintf("%s.cluster-domain", features.DNS): output = config.DNS.GetClusterDomain() - case "dns.service-ip": + case fmt.Sprintf("%s.service-ip", features.DNS): output = config.DNS.GetServiceIP() - case "gateway.enabled": + case fmt.Sprintf("%s.enabled", features.Gateway): output = config.Gateway.GetEnabled() - case "ingress.enabled": + case fmt.Sprintf("%s.enabled", features.Ingress): output = config.Ingress.GetEnabled() - case "ingress.default-tls-secret": + case fmt.Sprintf("%s.default-tls-secret", features.Ingress): output = config.Ingress.GetDefaultTLSSecret() - case "ingress.enable-proxy-protocol": + case fmt.Sprintf("%s.enable-proxy-protocol", features.Ingress): output = config.Ingress.GetEnableProxyProtocol() - case "local-storage.enabled": + case fmt.Sprintf("%s.enabled", features.LocalStorage): output = config.LocalStorage.GetEnabled() - case "local-storage.local-path": + case fmt.Sprintf("%s.local-path", features.LocalStorage): output = config.LocalStorage.GetLocalPath() - case "local-storage.reclaim-policy": + case fmt.Sprintf("%s.reclaim-policy", features.LocalStorage): output = config.LocalStorage.GetReclaimPolicy() - case "local-storage.default": + case fmt.Sprintf("%s.default", features.LocalStorage): output = config.LocalStorage.GetDefault() - case "load-balancer.enabled": + case fmt.Sprintf("%s.enabled", features.LoadBalancer): output = config.LoadBalancer.GetEnabled() - case "load-balancer.cidrs": + case fmt.Sprintf("%s.cidrs", features.LoadBalancer): output = config.LoadBalancer.GetCIDRs() - case "load-balancer.l2-mode": + case fmt.Sprintf("%s.l2-mode", features.LoadBalancer): output = config.LoadBalancer.GetL2Mode() - case "load-balancer.l2-interfaces": + case fmt.Sprintf("%s.l2-interfaces", features.LoadBalancer): output = config.LoadBalancer.GetL2Interfaces() - case "load-balancer.bgp-mode": + case fmt.Sprintf("%s.bgp-mode", features.LoadBalancer): output = config.LoadBalancer.GetBGPMode() - case "load-balancer.bgp-local-asn": + case fmt.Sprintf("%s.bgp-local-asn", features.LoadBalancer): output = config.LoadBalancer.GetBGPLocalASN() - case "load-balancer.bgp-peer-address": + case fmt.Sprintf("%s.bgp-peer-address", features.LoadBalancer): output = config.LoadBalancer.GetBGPPeerAddress() - case "load-balancer.bgp-peer-port": + case fmt.Sprintf("%s.bgp-peer-port", features.LoadBalancer): output = config.LoadBalancer.GetBGPPeerPort() - case "load-balancer.bgp-peer-asn": + case fmt.Sprintf("%s.bgp-peer-asn", features.LoadBalancer): output = config.LoadBalancer.GetBGPPeerASN() default: cmd.PrintErrf("Error: Unknown config key %q.\n", key) diff --git a/src/k8s/cmd/k8s/k8s_set.go b/src/k8s/cmd/k8s/k8s_set.go index 212100aad..659444a21 100644 --- a/src/k8s/cmd/k8s/k8s_set.go +++ b/src/k8s/cmd/k8s/k8s_set.go @@ -8,6 +8,7 @@ import ( apiv1 "github.com/canonical/k8s/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" + "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/utils" "github.com/mitchellh/mapstructure" "github.com/spf13/cobra" @@ -78,31 +79,31 @@ func newSetCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { } var knownSetKeys = map[string]struct{}{ - "annotations": {}, - "cloud-provider": {}, - "dns.cluster-domain": {}, - "dns.enabled": {}, - "dns.service-ip": {}, - "dns.upstream-nameservers": {}, - "gateway.enabled": {}, - "ingress.default-tls-secret": {}, - "ingress.enable-proxy-protocol": {}, - "ingress.enabled": {}, - "load-balancer.bgp-local-asn": {}, - "load-balancer.bgp-mode": {}, - "load-balancer.bgp-peer-address": {}, - "load-balancer.bgp-peer-asn": {}, - "load-balancer.bgp-peer-port": {}, - "load-balancer.cidrs": {}, - "load-balancer.enabled": {}, - "load-balancer.l2-interfaces": {}, - "load-balancer.l2-mode": {}, - "local-storage.default": {}, - "local-storage.enabled": {}, - "local-storage.local-path": {}, - "local-storage.reclaim-policy": {}, - "metrics-server.enabled": {}, - "network.enabled": {}, + "annotations": {}, + "cloud-provider": {}, + fmt.Sprintf("%s.cluster-domain", features.DNS): {}, + fmt.Sprintf("%s.enabled", features.DNS): {}, + fmt.Sprintf("%s.service-ip", features.DNS): {}, + fmt.Sprintf("%s.upstream-nameservers", features.DNS): {}, + fmt.Sprintf("%s.enabled", features.Gateway): {}, + fmt.Sprintf("%s.default-tls-secret", features.Ingress): {}, + fmt.Sprintf("%s.enable-proxy-protocol", features.Ingress): {}, + fmt.Sprintf("%s.enabled", features.Ingress): {}, + fmt.Sprintf("%s.bgp-local-asn", features.LoadBalancer): {}, + fmt.Sprintf("%s.bgp-mode", features.LoadBalancer): {}, + fmt.Sprintf("%s.bgp-peer-address", features.LoadBalancer): {}, + fmt.Sprintf("%s.bgp-peer-asn", features.LoadBalancer): {}, + fmt.Sprintf("%s.bgp-peer-port", features.LoadBalancer): {}, + fmt.Sprintf("%s.cidrs", features.LoadBalancer): {}, + fmt.Sprintf("%s.enabled", features.LoadBalancer): {}, + fmt.Sprintf("%s.l2-interfaces", features.LoadBalancer): {}, + fmt.Sprintf("%s.l2-mode", features.LoadBalancer): {}, + fmt.Sprintf("%s.default", features.LocalStorage): {}, + fmt.Sprintf("%s.enabled", features.LocalStorage): {}, + fmt.Sprintf("%s.local-path", features.LocalStorage): {}, + fmt.Sprintf("%s.reclaim-policy", features.LocalStorage): {}, + fmt.Sprintf("%s.enabled", features.MetricsServer): {}, + fmt.Sprintf("%s.enabled", features.Network): {}, } func updateConfigMapstructure(config *apiv1.UserFacingClusterConfig, arg string) error { diff --git a/src/k8s/cmd/k8s/k8s_x_cleanup.go b/src/k8s/cmd/k8s/k8s_x_cleanup.go index f448e3b0f..f11b4782b 100644 --- a/src/k8s/cmd/k8s/k8s_x_cleanup.go +++ b/src/k8s/cmd/k8s/k8s_x_cleanup.go @@ -15,7 +15,7 @@ func newXCleanupCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { } cleanupNetworkCmd := &cobra.Command{ - Use: "network", + Use: string(features.Network), Short: "Cleanup left-over network resources", Run: func(cmd *cobra.Command, args []string) { ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout) diff --git a/src/k8s/cmd/k8s/k8s_x_wait_for.go b/src/k8s/cmd/k8s/k8s_x_wait_for.go index 17f8fc1b2..4d09973c6 100644 --- a/src/k8s/cmd/k8s/k8s_x_wait_for.go +++ b/src/k8s/cmd/k8s/k8s_x_wait_for.go @@ -15,7 +15,7 @@ func newXWaitForCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { timeout time.Duration } waitForDNSCmd := &cobra.Command{ - Use: "dns", + Use: string(features.DNS), Short: "Wait for DNS to be ready", Run: func(cmd *cobra.Command, args []string) { ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout) @@ -35,7 +35,7 @@ func newXWaitForCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { waitForDNSCmd.Flags().DurationVar(&opts.timeout, "timeout", 5*time.Minute, "maximum time to wait") waitForNetworkCmd := &cobra.Command{ - Use: "network", + Use: string(features.Network), Short: "Wait for Network to be ready", Run: func(cmd *cobra.Command, args []string) { ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout) diff --git a/src/k8s/pkg/k8sd/api/cluster.go b/src/k8s/pkg/k8sd/api/cluster.go index ae899961b..67b4749d1 100644 --- a/src/k8s/pkg/k8sd/api/cluster.go +++ b/src/k8s/pkg/k8sd/api/cluster.go @@ -10,6 +10,7 @@ import ( "github.com/canonical/k8s/pkg/k8sd/api/impl" "github.com/canonical/k8s/pkg/k8sd/database" databaseutil "github.com/canonical/k8s/pkg/k8sd/database/util" + "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/k8sd/types" "github.com/canonical/lxd/lxd/response" "github.com/canonical/microcluster/v2/state" @@ -40,7 +41,7 @@ func (e *Endpoints) getClusterStatus(s state.State, r *http.Request) response.Re return response.InternalError(fmt.Errorf("failed to check if cluster has ready nodes: %w", err)) } - var statuses map[string]types.FeatureStatus + var statuses map[types.FeatureName]types.FeatureStatus if err := s.Database().Transaction(r.Context(), func(ctx context.Context, tx *sql.Tx) error { var err error statuses, err = database.GetFeatureStatuses(r.Context(), tx) @@ -61,13 +62,13 @@ func (e *Endpoints) getClusterStatus(s state.State, r *http.Request) response.Re Type: config.Datastore.GetType(), Servers: config.Datastore.GetExternalServers(), }, - DNS: statuses["dns"].ToAPI(), - Network: statuses["network"].ToAPI(), - LoadBalancer: statuses["load-balancer"].ToAPI(), - Ingress: statuses["ingress"].ToAPI(), - Gateway: statuses["gateway"].ToAPI(), - MetricsServer: statuses["metrics-server"].ToAPI(), - LocalStorage: statuses["local-storage"].ToAPI(), + DNS: statuses[features.DNS].ToAPI(), + Network: statuses[features.Network].ToAPI(), + LoadBalancer: statuses[features.LoadBalancer].ToAPI(), + Ingress: statuses[features.Ingress].ToAPI(), + Gateway: statuses[features.Gateway].ToAPI(), + MetricsServer: statuses[features.MetricsServer].ToAPI(), + LocalStorage: statuses[features.LocalStorage].ToAPI(), }, } diff --git a/src/k8s/pkg/k8sd/app/hooks_start.go b/src/k8s/pkg/k8sd/app/hooks_start.go index 6ae775ec2..0277f74f0 100644 --- a/src/k8s/pkg/k8sd/app/hooks_start.go +++ b/src/k8s/pkg/k8sd/app/hooks_start.go @@ -73,7 +73,7 @@ func (a *App) onStart(ctx context.Context, s state.State) error { return nil }, - func(ctx context.Context, name string, featureStatus types.FeatureStatus) error { + func(ctx context.Context, name types.FeatureName, featureStatus types.FeatureStatus) error { if err := s.Database().Transaction(ctx, func(ctx context.Context, tx *sql.Tx) error { // we set timestamp here in order to reduce the clutter. otherwise we will need to // set .UpdatedAt field in a lot of places for every event/error. diff --git a/src/k8s/pkg/k8sd/controllers/feature.go b/src/k8s/pkg/k8sd/controllers/feature.go index 35dbd9255..909a43bc3 100644 --- a/src/k8s/pkg/k8sd/controllers/feature.go +++ b/src/k8s/pkg/k8sd/controllers/feature.go @@ -73,36 +73,36 @@ func (c *FeatureController) Run( ctx context.Context, getClusterConfig func(context.Context) (types.ClusterConfig, error), notifyDNSChangedIP func(ctx context.Context, dnsIP string) error, - setFeatureStatus func(ctx context.Context, name string, featureStatus types.FeatureStatus) error, + setFeatureStatus func(ctx context.Context, name types.FeatureName, featureStatus types.FeatureStatus) error, ) { c.waitReady() ctx = log.NewContext(ctx, log.FromContext(ctx).WithValues("controller", "feature")) - go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, "network", c.triggerNetworkCh, c.reconciledNetworkCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { + go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, features.Network, c.triggerNetworkCh, c.reconciledNetworkCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { return features.Implementation.ApplyNetwork(ctx, c.snap, cfg.Network, cfg.Annotations) }) - go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, "gateway", c.triggerGatewayCh, c.reconciledGatewayCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { + go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, features.Gateway, c.triggerGatewayCh, c.reconciledGatewayCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { return features.Implementation.ApplyGateway(ctx, c.snap, cfg.Gateway, cfg.Network, cfg.Annotations) }) - go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, "ingress", c.triggerIngressCh, c.reconciledIngressCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { + go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, features.Ingress, c.triggerIngressCh, c.reconciledIngressCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { return features.Implementation.ApplyIngress(ctx, c.snap, cfg.Ingress, cfg.Network, cfg.Annotations) }) - go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, "load-balancer", c.triggerLoadBalancerCh, c.reconciledLoadBalancerCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { + go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, features.LoadBalancer, c.triggerLoadBalancerCh, c.reconciledLoadBalancerCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { return features.Implementation.ApplyLoadBalancer(ctx, c.snap, cfg.LoadBalancer, cfg.Network, cfg.Annotations) }) - go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, "local-storage", c.triggerLocalStorageCh, c.reconciledLocalStorageCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { + go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, features.LocalStorage, c.triggerLocalStorageCh, c.reconciledLocalStorageCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { return features.Implementation.ApplyLocalStorage(ctx, c.snap, cfg.LocalStorage, cfg.Annotations) }) - go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, "metrics-server", c.triggerMetricsServerCh, c.reconciledMetricsServerCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { + go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, features.MetricsServer, c.triggerMetricsServerCh, c.reconciledMetricsServerCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { return features.Implementation.ApplyMetricsServer(ctx, c.snap, cfg.MetricsServer, cfg.Annotations) }) - go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, "dns", c.triggerDNSCh, c.reconciledDNSCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { + go c.reconcileLoop(ctx, getClusterConfig, setFeatureStatus, features.DNS, c.triggerDNSCh, c.reconciledDNSCh, func(cfg types.ClusterConfig) (types.FeatureStatus, error) { featureStatus, dnsIP, err := features.Implementation.ApplyDNS(ctx, c.snap, cfg.DNS, cfg.Kubelet, cfg.Annotations) if err != nil { @@ -147,8 +147,8 @@ func (c *FeatureController) reconcile( func (c *FeatureController) reconcileLoop( ctx context.Context, getClusterConfig func(context.Context) (types.ClusterConfig, error), - setFeatureStatus func(ctx context.Context, name string, status types.FeatureStatus) error, - componentName string, + setFeatureStatus func(ctx context.Context, name types.FeatureName, status types.FeatureStatus) error, + featureName types.FeatureName, triggerCh chan struct{}, reconciledCh chan<- struct{}, apply func(cfg types.ClusterConfig) (types.FeatureStatus, error), @@ -159,9 +159,9 @@ func (c *FeatureController) reconcileLoop( return case <-triggerCh: if err := c.reconcile(ctx, getClusterConfig, apply, func(ctx context.Context, status types.FeatureStatus) error { - return setFeatureStatus(ctx, componentName, status) + return setFeatureStatus(ctx, featureName, status) }); err != nil { - log.FromContext(ctx).WithValues("feature", componentName).Error(err, "Failed to apply feature configuration") + log.FromContext(ctx).WithValues("feature", featureName).Error(err, "Failed to apply feature configuration") // notify triggerCh after 5 seconds to retry time.AfterFunc(5*time.Second, func() { utils.MaybeNotify(triggerCh) }) diff --git a/src/k8s/pkg/k8sd/database/feature_status.go b/src/k8s/pkg/k8sd/database/feature_status.go index 26b77d8f9..fd608ce6f 100644 --- a/src/k8s/pkg/k8sd/database/feature_status.go +++ b/src/k8s/pkg/k8sd/database/feature_status.go @@ -17,7 +17,7 @@ var featureStatusStmts = map[string]int{ } // SetFeatureStatus updates the status of the given feature. -func SetFeatureStatus(ctx context.Context, tx *sql.Tx, name string, status types.FeatureStatus) error { +func SetFeatureStatus(ctx context.Context, tx *sql.Tx, name types.FeatureName, status types.FeatureStatus) error { upsertTxStmt, err := cluster.Stmt(tx, featureStatusStmts["upsert"]) if err != nil { return fmt.Errorf("failed to prepare upsert statement: %w", err) @@ -37,7 +37,7 @@ func SetFeatureStatus(ctx context.Context, tx *sql.Tx, name string, status types } // GetFeatureStatuses returns a map of feature names to their status. -func GetFeatureStatuses(ctx context.Context, tx *sql.Tx) (map[string]types.FeatureStatus, error) { +func GetFeatureStatuses(ctx context.Context, tx *sql.Tx) (map[types.FeatureName]types.FeatureStatus, error) { selectTxStmt, err := cluster.Stmt(tx, featureStatusStmts["select"]) if err != nil { return nil, fmt.Errorf("failed to prepare select statement: %w", err) @@ -48,7 +48,7 @@ func GetFeatureStatuses(ctx context.Context, tx *sql.Tx) (map[string]types.Featu return nil, fmt.Errorf("failed to execute select statement: %w", err) } - result := make(map[string]types.FeatureStatus) + result := make(map[types.FeatureName]types.FeatureStatus) for rows.Next() { var ( @@ -65,7 +65,7 @@ func GetFeatureStatuses(ctx context.Context, tx *sql.Tx) (map[string]types.Featu log.FromContext(ctx).Error(err, "failed to parse time", "original", ts) } - result[name] = status + result[types.FeatureName(name)] = status } if rows.Err() != nil { diff --git a/src/k8s/pkg/k8sd/database/feature_status_test.go b/src/k8s/pkg/k8sd/database/feature_status_test.go index f18df401e..61e380f98 100644 --- a/src/k8s/pkg/k8sd/database/feature_status_test.go +++ b/src/k8s/pkg/k8sd/database/feature_status_test.go @@ -9,6 +9,7 @@ import ( . "github.com/onsi/gomega" "github.com/canonical/k8s/pkg/k8sd/database" + "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/k8sd/types" ) @@ -52,40 +53,40 @@ func TestFeatureStatus(t *testing.T) { t.Run("SettingNewStatus", func(t *testing.T) { g := NewWithT(t) - err := database.SetFeatureStatus(ctx, tx, "network", networkStatus) + err := database.SetFeatureStatus(ctx, tx, features.Network, networkStatus) g.Expect(err).To(BeNil()) - err = database.SetFeatureStatus(ctx, tx, "dns", dnsStatus) + err = database.SetFeatureStatus(ctx, tx, features.DNS, dnsStatus) g.Expect(err).To(BeNil()) ss, err := database.GetFeatureStatuses(ctx, tx) g.Expect(err).To(BeNil()) g.Expect(ss).To(HaveLen(2)) - g.Expect(ss["network"].Enabled).To(Equal(networkStatus.Enabled)) - g.Expect(ss["network"].Message).To(Equal(networkStatus.Message)) - g.Expect(ss["network"].Version).To(Equal(networkStatus.Version)) - g.Expect(ss["network"].UpdatedAt).To(Equal(networkStatus.UpdatedAt)) + g.Expect(ss[features.Network].Enabled).To(Equal(networkStatus.Enabled)) + g.Expect(ss[features.Network].Message).To(Equal(networkStatus.Message)) + g.Expect(ss[features.Network].Version).To(Equal(networkStatus.Version)) + g.Expect(ss[features.Network].UpdatedAt).To(Equal(networkStatus.UpdatedAt)) - g.Expect(ss["dns"].Enabled).To(Equal(dnsStatus.Enabled)) - g.Expect(ss["dns"].Message).To(Equal(dnsStatus.Message)) - g.Expect(ss["dns"].Version).To(Equal(dnsStatus.Version)) - g.Expect(ss["dns"].UpdatedAt).To(Equal(dnsStatus.UpdatedAt)) + g.Expect(ss[features.DNS].Enabled).To(Equal(dnsStatus.Enabled)) + g.Expect(ss[features.DNS].Message).To(Equal(dnsStatus.Message)) + g.Expect(ss[features.DNS].Version).To(Equal(dnsStatus.Version)) + g.Expect(ss[features.DNS].UpdatedAt).To(Equal(dnsStatus.UpdatedAt)) }) t.Run("UpdatingStatus", func(t *testing.T) { g := NewWithT(t) - err := database.SetFeatureStatus(ctx, tx, "network", networkStatus) + err := database.SetFeatureStatus(ctx, tx, features.Network, networkStatus) g.Expect(err).To(BeNil()) - err = database.SetFeatureStatus(ctx, tx, "dns", dnsStatus) + err = database.SetFeatureStatus(ctx, tx, features.DNS, dnsStatus) g.Expect(err).To(BeNil()) // set and update - err = database.SetFeatureStatus(ctx, tx, "network", networkStatus) + err = database.SetFeatureStatus(ctx, tx, features.Network, networkStatus) g.Expect(err).To(BeNil()) - err = database.SetFeatureStatus(ctx, tx, "dns", dnsStatus2) + err = database.SetFeatureStatus(ctx, tx, features.DNS, dnsStatus2) g.Expect(err).To(BeNil()) - err = database.SetFeatureStatus(ctx, tx, "gateway", gatewayStatus) + err = database.SetFeatureStatus(ctx, tx, features.Gateway, gatewayStatus) g.Expect(err).To(BeNil()) ss, err := database.GetFeatureStatuses(ctx, tx) @@ -93,22 +94,22 @@ func TestFeatureStatus(t *testing.T) { g.Expect(ss).To(HaveLen(3)) // network stayed the same - g.Expect(ss["network"].Enabled).To(Equal(networkStatus.Enabled)) - g.Expect(ss["network"].Message).To(Equal(networkStatus.Message)) - g.Expect(ss["network"].Version).To(Equal(networkStatus.Version)) - g.Expect(ss["network"].UpdatedAt).To(Equal(networkStatus.UpdatedAt)) + g.Expect(ss[features.Network].Enabled).To(Equal(networkStatus.Enabled)) + g.Expect(ss[features.Network].Message).To(Equal(networkStatus.Message)) + g.Expect(ss[features.Network].Version).To(Equal(networkStatus.Version)) + g.Expect(ss[features.Network].UpdatedAt).To(Equal(networkStatus.UpdatedAt)) // dns is updated - g.Expect(ss["dns"].Enabled).To(Equal(dnsStatus2.Enabled)) - g.Expect(ss["dns"].Message).To(Equal(dnsStatus2.Message)) - g.Expect(ss["dns"].Version).To(Equal(dnsStatus2.Version)) - g.Expect(ss["dns"].UpdatedAt).To(Equal(dnsStatus2.UpdatedAt)) + g.Expect(ss[features.DNS].Enabled).To(Equal(dnsStatus2.Enabled)) + g.Expect(ss[features.DNS].Message).To(Equal(dnsStatus2.Message)) + g.Expect(ss[features.DNS].Version).To(Equal(dnsStatus2.Version)) + g.Expect(ss[features.DNS].UpdatedAt).To(Equal(dnsStatus2.UpdatedAt)) // gateway is added - g.Expect(ss["gateway"].Enabled).To(Equal(gatewayStatus.Enabled)) - g.Expect(ss["gateway"].Message).To(Equal(gatewayStatus.Message)) - g.Expect(ss["gateway"].Version).To(Equal(gatewayStatus.Version)) - g.Expect(ss["gateway"].UpdatedAt).To(Equal(gatewayStatus.UpdatedAt)) + g.Expect(ss[features.Gateway].Enabled).To(Equal(gatewayStatus.Enabled)) + g.Expect(ss[features.Gateway].Message).To(Equal(gatewayStatus.Message)) + g.Expect(ss[features.Gateway].Version).To(Equal(gatewayStatus.Version)) + g.Expect(ss[features.Gateway].UpdatedAt).To(Equal(gatewayStatus.UpdatedAt)) }) return nil diff --git a/src/k8s/pkg/k8sd/features/features.go b/src/k8s/pkg/k8sd/features/features.go new file mode 100644 index 000000000..69085f56c --- /dev/null +++ b/src/k8s/pkg/k8sd/features/features.go @@ -0,0 +1,13 @@ +package features + +import "github.com/canonical/k8s/pkg/k8sd/types" + +const ( + DNS types.FeatureName = "dns" + Network types.FeatureName = "network" + Gateway types.FeatureName = "gateway" + Ingress types.FeatureName = "ingress" + LoadBalancer types.FeatureName = "load-balancer" + LocalStorage types.FeatureName = "local-storage" + MetricsServer types.FeatureName = "metrics-server" +) diff --git a/src/k8s/pkg/k8sd/types/feature_name.go b/src/k8s/pkg/k8sd/types/feature_name.go new file mode 100644 index 000000000..680d87a18 --- /dev/null +++ b/src/k8s/pkg/k8sd/types/feature_name.go @@ -0,0 +1,3 @@ +package types + +type FeatureName string diff --git a/src/k8s/pkg/utils/experimental/snapdconfig/disable.go b/src/k8s/pkg/utils/experimental/snapdconfig/disable.go index 154c51251..d2f510beb 100644 --- a/src/k8s/pkg/utils/experimental/snapdconfig/disable.go +++ b/src/k8s/pkg/utils/experimental/snapdconfig/disable.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" + "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/snap" ) @@ -13,7 +14,7 @@ func Disable(ctx context.Context, s snap.Snap) error { if err != nil { return fmt.Errorf("failed to marshal config: %w", err) } - if err := s.SnapctlSet(ctx, fmt.Sprintf("meta=%s", string(b)), "dns!", "network!", "gateway!", "ingress!", "load-balancer!", "local-storage!"); err != nil { + if err := s.SnapctlSet(ctx, fmt.Sprintf("meta=%s", string(b)), fmt.Sprintf("%s!", features.DNS), fmt.Sprintf("%s!", features.Network), fmt.Sprintf("%s!", features.Gateway), fmt.Sprintf("%s!", features.Ingress), fmt.Sprintf("%s!", features.LoadBalancer), fmt.Sprintf("%s!", features.LocalStorage)); err != nil { return fmt.Errorf("failed to snapctl set: %w", err) } return nil diff --git a/src/k8s/pkg/utils/experimental/snapdconfig/k8s_to_snapd.go b/src/k8s/pkg/utils/experimental/snapdconfig/k8s_to_snapd.go index b871b5e4b..ce22c71ff 100644 --- a/src/k8s/pkg/utils/experimental/snapdconfig/k8s_to_snapd.go +++ b/src/k8s/pkg/utils/experimental/snapdconfig/k8s_to_snapd.go @@ -6,20 +6,23 @@ import ( "fmt" apiv1 "github.com/canonical/k8s/api/v1" + "github.com/canonical/k8s/pkg/k8sd/features" + "github.com/canonical/k8s/pkg/k8sd/types" "github.com/canonical/k8s/pkg/snap" ) // SetSnapdFromK8sd uses snapctl to update the local snapd configuration with the new k8sd cluster configuration. func SetSnapdFromK8sd(ctx context.Context, config apiv1.UserFacingClusterConfig, snap snap.Snap) error { var sets []string - for key, cfg := range map[string]any{ - "meta": Meta{Orb: "snapd", APIVersion: "1.30"}, - "dns": config.DNS, - "network": config.Network, - "local-storage": config.LocalStorage, - "load-balancer": config.LoadBalancer, - "ingress": config.Ingress, - "gateway": config.Gateway, + + for key, cfg := range map[types.FeatureName]any{ + "meta": Meta{Orb: "snapd", APIVersion: "1.30"}, + features.DNS: config.DNS, + features.Network: config.Network, + features.LocalStorage: config.LocalStorage, + features.LoadBalancer: config.LoadBalancer, + features.Ingress: config.Ingress, + features.Gateway: config.Gateway, } { b, err := json.Marshal(cfg) if err != nil { diff --git a/src/k8s/pkg/utils/experimental/snapdconfig/snapd_to_k8s.go b/src/k8s/pkg/utils/experimental/snapdconfig/snapd_to_k8s.go index 255165bea..2cec720f4 100644 --- a/src/k8s/pkg/utils/experimental/snapdconfig/snapd_to_k8s.go +++ b/src/k8s/pkg/utils/experimental/snapdconfig/snapd_to_k8s.go @@ -7,12 +7,21 @@ import ( apiv1 "github.com/canonical/k8s/api/v1" "github.com/canonical/k8s/pkg/client/k8sd" + "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/snap" ) // SetK8sdFromSnapd updates the k8sd cluster configuration from the current local snapd configuration. func SetK8sdFromSnapd(ctx context.Context, client k8sd.Client, snap snap.Snap) error { - b, err := snap.SnapctlGet(ctx, "-d", "dns", "network", "local-storage", "load-balancer", "ingress", "gateway") + b, err := snap.SnapctlGet( + ctx, "-d", + string(features.DNS), + string(features.Network), + string(features.LocalStorage), + string(features.LoadBalancer), + string(features.Ingress), + string(features.Gateway), + ) if err != nil { return fmt.Errorf("failed to retrieve snapd configuration: %w", err) }