diff --git a/src/k8s/Makefile b/src/k8s/Makefile index 42ad1def1..4e7d33154 100644 --- a/src/k8s/Makefile +++ b/src/k8s/Makefile @@ -11,7 +11,7 @@ go.vet: $(DQLITE_BUILD_SCRIPTS_DIR)/static-go-vet.sh ./... go.unit: - $(DQLITE_BUILD_SCRIPTS_DIR)/static-go-test.sh -v ./pkg/... ./cmd/... ./api/... -coverprofile=coverage.txt --cover + $(DQLITE_BUILD_SCRIPTS_DIR)/static-go-test.sh -v ./pkg/... ./cmd/... -coverprofile=coverage.txt --cover go.doc: bin/static/k8s bin/static/k8s generate-docs --output-dir ../../docs/src/_parts/commands/ diff --git a/src/k8s/api/v1/annotations.go b/src/k8s/api/v1/annotations.go deleted file mode 100644 index 3597c0680..000000000 --- a/src/k8s/api/v1/annotations.go +++ /dev/null @@ -1,8 +0,0 @@ -package apiv1 - -const ( - // AnnotationSkipCleanupKubernetesNodeOnRemove if set, only the microcluster & file cleanup is done. - // This is useful, if an external controller (e.g. CAPI) is responsible for the Kubernetes node life cycle. - // By default, the Kubernetes node is removed by k8sd if a node is removed from the cluster. - AnnotationSkipCleanupKubernetesNodeOnRemove = "k8sd/v1alpha/lifecycle/skip-cleanup-kubernetes-node-on-remove" -) diff --git a/src/k8s/api/v1/bootstrap_config.go b/src/k8s/api/v1/bootstrap_config.go deleted file mode 100644 index 74e2bf15f..000000000 --- a/src/k8s/api/v1/bootstrap_config.go +++ /dev/null @@ -1,136 +0,0 @@ -package apiv1 - -import ( - "encoding/json" - "fmt" -) - -// BootstrapConfig is used to seed cluster configuration when bootstrapping a new cluster. -type BootstrapConfig struct { - // ClusterConfig - ClusterConfig UserFacingClusterConfig `json:"cluster-config,omitempty" yaml:"cluster-config,omitempty"` - - // Seed configuration for the control plane (flat on purpose). Empty values are ignored - ControlPlaneTaints []string `json:"control-plane-taints,omitempty" yaml:"control-plane-taints,omitempty"` - PodCIDR *string `json:"pod-cidr,omitempty" yaml:"pod-cidr,omitempty"` - ServiceCIDR *string `json:"service-cidr,omitempty" yaml:"service-cidr,omitempty"` - DisableRBAC *bool `json:"disable-rbac,omitempty" yaml:"disable-rbac,omitempty"` - SecurePort *int `json:"secure-port,omitempty" yaml:"secure-port,omitempty"` - K8sDqlitePort *int `json:"k8s-dqlite-port,omitempty" yaml:"k8s-dqlite-port,omitempty"` - DatastoreType *string `json:"datastore-type,omitempty" yaml:"datastore-type,omitempty"` - DatastoreServers []string `json:"datastore-servers,omitempty" yaml:"datastore-servers,omitempty"` - DatastoreCACert *string `json:"datastore-ca-crt,omitempty" yaml:"datastore-ca-crt,omitempty"` - DatastoreClientCert *string `json:"datastore-client-crt,omitempty" yaml:"datastore-client-crt,omitempty"` - DatastoreClientKey *string `json:"datastore-client-key,omitempty" yaml:"datastore-client-key,omitempty"` - - // Seed configuration for certificates - ExtraSANs []string `json:"extra-sans,omitempty" yaml:"extra-sans,omitempty"` - - // Seed configuration for external certificates (cluster-wide) - CACert *string `json:"ca-crt,omitempty" yaml:"ca-crt,omitempty"` - CAKey *string `json:"ca-key,omitempty" yaml:"ca-key,omitempty"` - ClientCACert *string `json:"client-ca-crt,omitempty" yaml:"client-ca-crt,omitempty"` - ClientCAKey *string `json:"client-ca-key,omitempty" yaml:"client-ca-key,omitempty"` - FrontProxyCACert *string `json:"front-proxy-ca-crt,omitempty" yaml:"front-proxy-ca-crt,omitempty"` - FrontProxyCAKey *string `json:"front-proxy-ca-key,omitempty" yaml:"front-proxy-ca-key,omitempty"` - FrontProxyClientCert *string `json:"front-proxy-client-crt,omitempty" yaml:"front-proxy-client-crt,omitempty"` - FrontProxyClientKey *string `json:"front-proxy-client-key,omitempty" yaml:"front-proxy-client-key,omitempty"` - APIServerKubeletClientCert *string `json:"apiserver-kubelet-client-crt,omitempty" yaml:"apiserver-kubelet-client-crt,omitempty"` - APIServerKubeletClientKey *string `json:"apiserver-kubelet-client-key,omitempty" yaml:"apiserver-kubelet-client-key,omitempty"` - AdminClientCert *string `json:"admin-client-crt,omitempty" yaml:"admin-client-crt,omitempty"` - AdminClientKey *string `json:"admin-client-key,omitempty" yaml:"admin-client-key,omitempty"` - KubeProxyClientCert *string `json:"kube-proxy-client-crt,omitempty" yaml:"kube-proxy-client-crt,omitempty"` - KubeProxyClientKey *string `json:"kube-proxy-client-key,omitempty" yaml:"kube-proxy-client-key,omitempty"` - KubeSchedulerClientCert *string `json:"kube-scheduler-client-crt,omitempty" yaml:"kube-scheduler-client-crt,omitempty"` - KubeSchedulerClientKey *string `json:"kube-scheduler-client-key,omitempty" yaml:"kube-scheduler-client-key,omitempty"` - KubeControllerManagerClientCert *string `json:"kube-controller-manager-client-crt,omitempty" yaml:"kube-controller-manager-client-crt,omitempty"` - KubeControllerManagerClientKey *string `json:"kube-controller-manager-client-key,omitempty" yaml:"kube-ControllerManager-client-key,omitempty"` - ServiceAccountKey *string `json:"service-account-key,omitempty" yaml:"service-account-key,omitempty"` - - // Seed configuration for external certificates (node-specific) - APIServerCert *string `json:"apiserver-crt,omitempty" yaml:"apiserver-crt,omitempty"` - APIServerKey *string `json:"apiserver-key,omitempty" yaml:"apiserver-key,omitempty"` - KubeletCert *string `json:"kubelet-crt,omitempty" yaml:"kubelet-crt,omitempty"` - KubeletKey *string `json:"kubelet-key,omitempty" yaml:"kubelet-key,omitempty"` - KubeletClientCert *string `json:"kubelet-client-crt,omitempty" yaml:"kubelet-client-crt,omitempty"` - KubeletClientKey *string `json:"kubelet-client-key,omitempty" yaml:"kubelet-client-key,omitempty"` - - // ExtraNodeConfigFiles will be written to /var/snap/k8s/common/args/conf.d - ExtraNodeConfigFiles map[string]string `json:"extra-node-config-files,omitempty" yaml:"extra-node-config-files,omitempty"` - - // Extra args to add to individual services (set any arg to null to delete) - ExtraNodeKubeAPIServerArgs map[string]*string `json:"extra-node-kube-apiserver-args,omitempty" yaml:"extra-node-kube-apiserver-args,omitempty"` - ExtraNodeKubeControllerManagerArgs map[string]*string `json:"extra-node-kube-controller-manager-args,omitempty" yaml:"extra-node-kube-controller-manager-args,omitempty"` - ExtraNodeKubeSchedulerArgs map[string]*string `json:"extra-node-kube-scheduler-args,omitempty" yaml:"extra-node-kube-scheduler-args,omitempty"` - ExtraNodeKubeProxyArgs map[string]*string `json:"extra-node-kube-proxy-args,omitempty" yaml:"extra-node-kube-proxy-args,omitempty"` - ExtraNodeKubeletArgs map[string]*string `json:"extra-node-kubelet-args,omitempty" yaml:"extra-node-kubelet-args,omitempty"` - ExtraNodeContainerdArgs map[string]*string `json:"extra-node-containerd-args,omitempty" yaml:"extra-node-containerd-args,omitempty"` - ExtraNodeK8sDqliteArgs map[string]*string `json:"extra-node-k8s-dqlite-args,omitempty" yaml:"extra-node-k8s-dqlite-args,omitempty"` - - // Extra configuration for the containerd config.toml - ExtraNodeContainerdConfig map[string]any `json:"extra-node-containerd-config,omitempty" yaml:"extra-node-containerd-config,omitempty"` -} - -func (b *BootstrapConfig) GetDatastoreType() string { return getField(b.DatastoreType) } -func (b *BootstrapConfig) GetDatastoreCACert() string { return getField(b.DatastoreCACert) } -func (b *BootstrapConfig) GetDatastoreClientCert() string { return getField(b.DatastoreClientCert) } -func (b *BootstrapConfig) GetDatastoreClientKey() string { return getField(b.DatastoreClientKey) } -func (b *BootstrapConfig) GetK8sDqlitePort() int { return getField(b.K8sDqlitePort) } -func (b *BootstrapConfig) GetCACert() string { return getField(b.CACert) } -func (b *BootstrapConfig) GetCAKey() string { return getField(b.CAKey) } -func (b *BootstrapConfig) GetClientCACert() string { return getField(b.ClientCACert) } -func (b *BootstrapConfig) GetClientCAKey() string { return getField(b.ClientCAKey) } -func (b *BootstrapConfig) GetFrontProxyCACert() string { return getField(b.FrontProxyCACert) } -func (b *BootstrapConfig) GetFrontProxyCAKey() string { return getField(b.FrontProxyCAKey) } -func (b *BootstrapConfig) GetFrontProxyClientCert() string { return getField(b.FrontProxyClientCert) } -func (b *BootstrapConfig) GetFrontProxyClientKey() string { return getField(b.FrontProxyClientKey) } -func (b *BootstrapConfig) GetAPIServerKubeletClientCert() string { - return getField(b.APIServerKubeletClientCert) -} -func (b *BootstrapConfig) GetAPIServerKubeletClientKey() string { - return getField(b.APIServerKubeletClientKey) -} -func (b *BootstrapConfig) GetAdminClientCert() string { return getField(b.AdminClientCert) } -func (b *BootstrapConfig) GetAdminClientKey() string { return getField(b.AdminClientKey) } -func (b *BootstrapConfig) GetKubeProxyClientCert() string { return getField(b.KubeProxyClientCert) } -func (b *BootstrapConfig) GetKubeProxyClientKey() string { return getField(b.KubeProxyClientKey) } -func (b *BootstrapConfig) GetKubeSchedulerClientCert() string { - return getField(b.KubeSchedulerClientCert) -} -func (b *BootstrapConfig) GetKubeSchedulerClientKey() string { - return getField(b.KubeSchedulerClientKey) -} -func (b *BootstrapConfig) GetKubeControllerManagerClientCert() string { - return getField(b.KubeControllerManagerClientCert) -} -func (b *BootstrapConfig) GetKubeControllerManagerClientKey() string { - return getField(b.KubeControllerManagerClientKey) -} -func (b *BootstrapConfig) GetServiceAccountKey() string { return getField(b.ServiceAccountKey) } -func (b *BootstrapConfig) GetAPIServerCert() string { return getField(b.APIServerCert) } -func (b *BootstrapConfig) GetAPIServerKey() string { return getField(b.APIServerKey) } -func (b *BootstrapConfig) GetKubeletCert() string { return getField(b.KubeletCert) } -func (b *BootstrapConfig) GetKubeletKey() string { return getField(b.KubeletKey) } -func (b *BootstrapConfig) GetKubeletClientCert() string { return getField(b.KubeletClientCert) } -func (b *BootstrapConfig) GetKubeletClientKey() string { return getField(b.KubeletClientKey) } - -// ToMicrocluster converts a BootstrapConfig to a map[string]string for use in microcluster. -func (b *BootstrapConfig) ToMicrocluster() (map[string]string, error) { - config, err := json.Marshal(b) - if err != nil { - return nil, fmt.Errorf("failed to marshal bootstrap config: %w", err) - } - - return map[string]string{ - "bootstrapConfig": string(config), - }, nil -} - -// BootstrapConfigFromMicrocluster parses a microcluster map[string]string and retrieves the BootstrapConfig. -func BootstrapConfigFromMicrocluster(m map[string]string) (BootstrapConfig, error) { - config := BootstrapConfig{} - if err := json.Unmarshal([]byte(m["bootstrapConfig"]), &config); err != nil { - return BootstrapConfig{}, fmt.Errorf("failed to unmarshal bootstrap config: %w", err) - } - return config, nil -} diff --git a/src/k8s/api/v1/bootstrap_config_test.go b/src/k8s/api/v1/bootstrap_config_test.go deleted file mode 100644 index 02cc2522d..000000000 --- a/src/k8s/api/v1/bootstrap_config_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package apiv1_test - -import ( - "testing" - - apiv1 "github.com/canonical/k8s/api/v1" - "github.com/canonical/k8s/pkg/utils" - . "github.com/onsi/gomega" -) - -func TestBootstrapConfigToMicrocluster(t *testing.T) { - g := NewWithT(t) - - cfg := apiv1.BootstrapConfig{ - ClusterConfig: apiv1.UserFacingClusterConfig{ - Network: apiv1.NetworkConfig{ - Enabled: utils.Pointer(true), - }, - DNS: apiv1.DNSConfig{ - Enabled: utils.Pointer(true), - ClusterDomain: utils.Pointer("cluster.local"), - }, - Ingress: apiv1.IngressConfig{ - Enabled: utils.Pointer(true), - }, - LoadBalancer: apiv1.LoadBalancerConfig{ - Enabled: utils.Pointer(true), - L2Mode: utils.Pointer(true), - CIDRs: utils.Pointer([]string{"10.0.0.0/24", "10.1.0.10-10.1.0.20"}), - }, - LocalStorage: apiv1.LocalStorageConfig{ - Enabled: utils.Pointer(true), - LocalPath: utils.Pointer("/storage/path"), - Default: utils.Pointer(false), - }, - Gateway: apiv1.GatewayConfig{ - Enabled: utils.Pointer(true), - }, - MetricsServer: apiv1.MetricsServerConfig{ - Enabled: utils.Pointer(true), - }, - CloudProvider: utils.Pointer("external"), - }, - PodCIDR: utils.Pointer("10.100.0.0/16"), - ServiceCIDR: utils.Pointer("10.200.0.0/16"), - DisableRBAC: utils.Pointer(false), - SecurePort: utils.Pointer(6443), - K8sDqlitePort: utils.Pointer(9090), - DatastoreType: utils.Pointer("k8s-dqlite"), - ExtraSANs: []string{"custom.kubernetes"}, - ExtraNodeConfigFiles: map[string]string{"extra-node-config-file": "file-content"}, - ExtraNodeKubeAPIServerArgs: map[string]*string{"--extra-kube-apiserver-arg": utils.Pointer("extra-kube-apiserver-value")}, - ExtraNodeKubeControllerManagerArgs: map[string]*string{"--extra-kube-controller-manager-arg": utils.Pointer("extra-kube-controller-manager-value")}, - ExtraNodeKubeSchedulerArgs: map[string]*string{"--extra-kube-scheduler-arg": utils.Pointer("extra-kube-scheduler-value")}, - ExtraNodeKubeProxyArgs: map[string]*string{"--extra-kube-proxy-arg": utils.Pointer("extra-kube-proxy-value")}, - ExtraNodeKubeletArgs: map[string]*string{"--extra-kubelet-arg": utils.Pointer("extra-kubelet-value")}, - ExtraNodeContainerdArgs: map[string]*string{"--extra-containerd-arg": utils.Pointer("extra-containerd-value")}, - ExtraNodeK8sDqliteArgs: map[string]*string{"--extra-k8s-dqlite-arg": utils.Pointer("extra-k8s-dqlite-value")}, - } - - microclusterConfig, err := cfg.ToMicrocluster() - g.Expect(err).To(BeNil()) - - fromMicrocluster, err := apiv1.BootstrapConfigFromMicrocluster(microclusterConfig) - g.Expect(err).To(BeNil()) - g.Expect(fromMicrocluster).To(Equal(cfg)) -} diff --git a/src/k8s/api/v1/capi_config.go b/src/k8s/api/v1/capi_config.go deleted file mode 100644 index c00fbd00f..000000000 --- a/src/k8s/api/v1/capi_config.go +++ /dev/null @@ -1,6 +0,0 @@ -package apiv1 - -// SetClusterAPIAuthTokenRequest is used to request to set the auth token for ClusterAPI. -type SetClusterAPIAuthTokenRequest struct { - Token string `json:"token"` -} diff --git a/src/k8s/api/v1/certificates_refresh.go b/src/k8s/api/v1/certificates_refresh.go deleted file mode 100644 index 38d6499af..000000000 --- a/src/k8s/api/v1/certificates_refresh.go +++ /dev/null @@ -1,15 +0,0 @@ -package apiv1 - -type RefreshCertificatesPlanResponse struct { - Seed int `json:"seed"` - CertificatesSigningRequests []string `json:"certificates-signing-requests"` -} - -type RefreshCertificatesRunRequest struct { - Seed int `json:"seed"` - ExpirationSeconds int `json:"expiration-seconds"` -} - -type RefreshCertificatesRunResponse struct { - ExpirationSeconds int `json:"expiration-seconds"` -} diff --git a/src/k8s/api/v1/cluster.go b/src/k8s/api/v1/cluster.go deleted file mode 100644 index 84d00dddd..000000000 --- a/src/k8s/api/v1/cluster.go +++ /dev/null @@ -1,29 +0,0 @@ -package apiv1 - -import "time" - -// GetClusterStatusRequest is used to request the current status of the cluster. -type GetClusterStatusRequest struct{} - -// GetClusterStatusResponse is the response for "GET 1.0/k8sd/cluster". -type GetClusterStatusResponse struct { - ClusterStatus ClusterStatus `json:"status"` -} - -// PostClusterBootstrapRequest is used to bootstrap the cluster by using the endpoint "POST 1.0/k8sd/cluster". -type PostClusterBootstrapRequest struct { - Name string `json:"name"` - Address string `json:"address"` - Config BootstrapConfig `json:"config"` - Timeout time.Duration `json:"timeout"` -} - -// GetKubeConfigRequest is used to ask for the admin kubeconfig -type GetKubeConfigRequest struct { - Server string `json:"server"` -} - -// GetKubeConfigResponse is the response for "GET 1.0/k8sd/cluster/config". -type GetKubeConfigResponse struct { - KubeConfig string `json:"kubeconfig"` -} diff --git a/src/k8s/api/v1/cluster_config.go b/src/k8s/api/v1/cluster_config.go deleted file mode 100644 index 0e13e3270..000000000 --- a/src/k8s/api/v1/cluster_config.go +++ /dev/null @@ -1,210 +0,0 @@ -package apiv1 - -import ( - "fmt" - - "gopkg.in/yaml.v2" -) - -type GetClusterConfigRequest struct{} - -type GetClusterConfigResponse struct { - Config UserFacingClusterConfig -} - -type UpdateClusterConfigRequest struct { - Config UserFacingClusterConfig `json:"config,omitempty" yaml:"config,omitempty"` - Datastore UserFacingDatastoreConfig `json:"datastore,omitempty" yaml:"datastore,omitempty"` -} - -type UpdateClusterConfigResponse struct { -} - -type UserFacingClusterConfig struct { - Network NetworkConfig `json:"network,omitempty" yaml:"network,omitempty"` - DNS DNSConfig `json:"dns,omitempty" yaml:"dns,omitempty"` - Ingress IngressConfig `json:"ingress,omitempty" yaml:"ingress,omitempty"` - LoadBalancer LoadBalancerConfig `json:"load-balancer,omitempty" yaml:"load-balancer,omitempty"` - LocalStorage LocalStorageConfig `json:"local-storage,omitempty" yaml:"local-storage,omitempty"` - Gateway GatewayConfig `json:"gateway,omitempty" yaml:"gateway,omitempty"` - MetricsServer MetricsServerConfig `json:"metrics-server,omitempty" yaml:"metrics-server,omitempty"` - CloudProvider *string `json:"cloud-provider,omitempty" yaml:"cloud-provider,omitempty"` - Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` -} - -func (c UserFacingClusterConfig) Empty() bool { - switch { - case c.Network != NetworkConfig{}: - return false - case c.DNS != DNSConfig{}: - return false - case c.Ingress != IngressConfig{}: - return false - case c.LoadBalancer != LoadBalancerConfig{}: - return false - case c.LocalStorage != LocalStorageConfig{}: - return false - case c.Gateway != GatewayConfig{}: - return false - case c.MetricsServer != MetricsServerConfig{}: - return false - case getField(c.CloudProvider) != "": - return false - case len(c.Annotations) > 0: - return false - } - return true -} - -type DNSConfig struct { - Enabled *bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` - ClusterDomain *string `json:"cluster-domain,omitempty" yaml:"cluster-domain,omitempty"` - ServiceIP *string `json:"service-ip,omitempty" yaml:"service-ip,omitempty"` - UpstreamNameservers *[]string `json:"upstream-nameservers,omitempty" yaml:"upstream-nameservers,omitempty"` -} - -func (c DNSConfig) GetEnabled() bool { return getField(c.Enabled) } -func (c DNSConfig) GetClusterDomain() string { return getField(c.ClusterDomain) } -func (c DNSConfig) GetServiceIP() string { return getField(c.ServiceIP) } -func (c DNSConfig) GetUpstreamNameservers() []string { return getField(c.UpstreamNameservers) } - -type IngressConfig struct { - Enabled *bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` - DefaultTLSSecret *string `json:"default-tls-secret,omitempty" yaml:"default-tls-secret,omitempty"` - EnableProxyProtocol *bool `json:"enable-proxy-protocol,omitempty" yaml:"enable-proxy-protocol,omitempty"` -} - -func (c IngressConfig) GetEnabled() bool { return getField(c.Enabled) } -func (c IngressConfig) GetDefaultTLSSecret() string { return getField(c.DefaultTLSSecret) } -func (c IngressConfig) GetEnableProxyProtocol() bool { return getField(c.EnableProxyProtocol) } - -type LoadBalancerConfig struct { - Enabled *bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` - CIDRs *[]string `json:"cidrs,omitempty" yaml:"cidrs,omitempty"` - L2Mode *bool `json:"l2-mode,omitempty" yaml:"l2-mode,omitempty"` - L2Interfaces *[]string `json:"l2-interfaces,omitempty" yaml:"l2-interfaces,omitempty"` - BGPMode *bool `json:"bgp-mode,omitempty" yaml:"bgp-mode,omitempty"` - BGPLocalASN *int `json:"bgp-local-asn,omitempty" yaml:"bgp-local-asn,omitempty"` - BGPPeerAddress *string `json:"bgp-peer-address,omitempty" yaml:"bgp-peer-address,omitempty"` - BGPPeerASN *int `json:"bgp-peer-asn,omitempty" yaml:"bgp-peer-asn,omitempty"` - BGPPeerPort *int `json:"bgp-peer-port,omitempty" yaml:"bgp-peer-port,omitempty"` -} - -func (c LoadBalancerConfig) GetEnabled() bool { return getField(c.Enabled) } -func (c LoadBalancerConfig) GetCIDRs() []string { return getField(c.CIDRs) } -func (c LoadBalancerConfig) GetL2Mode() bool { return getField(c.L2Mode) } -func (c LoadBalancerConfig) GetL2Interfaces() []string { return getField(c.L2Interfaces) } -func (c LoadBalancerConfig) GetBGPMode() bool { return getField(c.BGPMode) } -func (c LoadBalancerConfig) GetBGPLocalASN() int { return getField(c.BGPLocalASN) } -func (c LoadBalancerConfig) GetBGPPeerAddress() string { return getField(c.BGPPeerAddress) } -func (c LoadBalancerConfig) GetBGPPeerASN() int { return getField(c.BGPPeerASN) } -func (c LoadBalancerConfig) GetBGPPeerPort() int { return getField(c.BGPPeerPort) } - -type LocalStorageConfig struct { - Enabled *bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` - LocalPath *string `json:"local-path,omitempty" yaml:"local-path,omitempty"` - ReclaimPolicy *string `json:"reclaim-policy,omitempty" yaml:"reclaim-policy,omitempty"` - Default *bool `json:"default,omitempty" yaml:"default,omitempty"` -} - -func (c LocalStorageConfig) GetEnabled() bool { return getField(c.Enabled) } -func (c LocalStorageConfig) GetLocalPath() string { return getField(c.LocalPath) } -func (c LocalStorageConfig) GetReclaimPolicy() string { return getField(c.ReclaimPolicy) } -func (c LocalStorageConfig) GetDefault() bool { return getField(c.Default) } - -type NetworkConfig struct { - Enabled *bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` -} - -func (c NetworkConfig) GetEnabled() bool { return getField(c.Enabled) } - -type GatewayConfig struct { - Enabled *bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` -} - -func (c GatewayConfig) GetEnabled() bool { return getField(c.Enabled) } - -type MetricsServerConfig struct { - Enabled *bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` -} - -func (c MetricsServerConfig) GetEnabled() bool { return getField(c.Enabled) } - -type UserFacingDatastoreConfig struct { - // Type of the datastore. Needs to be "external". - Type *string `json:"type,omitempty" yaml:"type,omitempty"` - Servers *[]string `json:"servers,omitempty" yaml:"servers,omitempty"` - CACert *string `json:"ca-crt,omitempty" yaml:"ca-crt,omitempty"` - ClientCert *string `json:"client-crt,omitempty" yaml:"client-crt,omitempty"` - ClientKey *string `json:"client-key,omitempty" yaml:"client-key,omitempty"` -} - -func (c UserFacingDatastoreConfig) GetType() string { return getField(c.Type) } -func (c UserFacingDatastoreConfig) GetServers() []string { return getField(c.Servers) } -func (c UserFacingDatastoreConfig) GetCACert() string { return getField(c.CACert) } -func (c UserFacingDatastoreConfig) GetClientCert() string { return getField(c.ClientCert) } -func (c UserFacingDatastoreConfig) GetClientKey() string { return getField(c.ClientKey) } - -func (c UserFacingClusterConfig) String() string { - b, err := yaml.Marshal(c) - if err != nil { - return fmt.Sprintf("%#v\n", c) - } - return string(b) -} - -func (c NetworkConfig) String() string { - b, err := yaml.Marshal(c) - if err != nil { - return fmt.Sprintf("%#v\n", c) - } - return string(b) -} - -func (c DNSConfig) String() string { - b, err := yaml.Marshal(c) - if err != nil { - return fmt.Sprintf("%#v\n", c) - } - return string(b) -} - -func (c IngressConfig) String() string { - b, err := yaml.Marshal(c) - if err != nil { - return fmt.Sprintf("%#v\n", c) - } - return string(b) -} - -func (c LoadBalancerConfig) String() string { - b, err := yaml.Marshal(c) - if err != nil { - return fmt.Sprintf("%#v\n", c) - } - return string(b) -} - -func (c LocalStorageConfig) String() string { - b, err := yaml.Marshal(c) - if err != nil { - return fmt.Sprintf("%#v\n", c) - } - return string(b) -} - -func (c GatewayConfig) String() string { - b, err := yaml.Marshal(c) - if err != nil { - return fmt.Sprintf("%#v\n", c) - } - return string(b) -} - -func (c MetricsServerConfig) String() string { - b, err := yaml.Marshal(c) - if err != nil { - return fmt.Sprintf("%#v\n", c) - } - return string(b) -} diff --git a/src/k8s/api/v1/cluster_node.go b/src/k8s/api/v1/cluster_node.go deleted file mode 100644 index 724e5460f..000000000 --- a/src/k8s/api/v1/cluster_node.go +++ /dev/null @@ -1,19 +0,0 @@ -package apiv1 - -import "time" - -// JoinClusterRequest is used to request to add a node to the cluster. -type JoinClusterRequest struct { - Name string `json:"name"` - Address string `json:"address"` - Token string `json:"token"` - Config string `json:"config"` - Timeout time.Duration `json:"timeout"` -} - -// RemoveNodeRequest is used to request to remove a node from the cluster. -type RemoveNodeRequest struct { - Name string `json:"name"` - Force bool `json:"force"` - Timeout time.Duration `json:"timeout"` -} diff --git a/src/k8s/api/v1/join_config.go b/src/k8s/api/v1/join_config.go deleted file mode 100644 index f4b56bfba..000000000 --- a/src/k8s/api/v1/join_config.go +++ /dev/null @@ -1,126 +0,0 @@ -package apiv1 - -import ( - "fmt" - - "gopkg.in/yaml.v2" -) - -type ControlPlaneNodeJoinConfig struct { - ExtraSANS []string `json:"extra-sans,omitempty" yaml:"extra-sans,omitempty"` - - // Seed certificates for external CA - FrontProxyClientCert *string `json:"front-proxy-client-crt,omitempty" yaml:"front-proxy-client-crt,omitempty"` - FrontProxyClientKey *string `json:"front-proxy-client-key,omitempty" yaml:"front-proxy-client-key,omitempty"` - KubeProxyClientCert *string `json:"kube-proxy-client-crt,omitempty" yaml:"kube-proxy-client-crt,omitempty"` - KubeProxyClientKey *string `json:"kube-proxy-client-key,omitempty" yaml:"kube-proxy-client-key,omitempty"` - KubeSchedulerClientCert *string `json:"kube-scheduler-client-crt,omitempty" yaml:"kube-scheduler-client-crt,omitempty"` - KubeSchedulerClientKey *string `json:"kube-scheduler-client-key,omitempty" yaml:"kube-scheduler-client-key,omitempty"` - KubeControllerManagerClientCert *string `json:"kube-controller-manager-client-crt,omitempty" yaml:"kube-controller-manager-client-crt,omitempty"` - KubeControllerManagerClientKey *string `json:"kube-controller-manager-client-key,omitempty" yaml:"kube-ControllerManager-client-key,omitempty"` - - APIServerCert *string `json:"apiserver-crt,omitempty" yaml:"apiserver-crt,omitempty"` - APIServerKey *string `json:"apiserver-key,omitempty" yaml:"apiserver-key,omitempty"` - KubeletCert *string `json:"kubelet-crt,omitempty" yaml:"kubelet-crt,omitempty"` - KubeletKey *string `json:"kubelet-key,omitempty" yaml:"kubelet-key,omitempty"` - KubeletClientCert *string `json:"kubelet-client-crt,omitempty" yaml:"kubelet-client-crt,omitempty"` - KubeletClientKey *string `json:"kubelet-client-key,omitempty" yaml:"kubelet-client-key,omitempty"` - - // ExtraNodeConfigFiles will be written to /var/snap/k8s/common/args/conf.d - ExtraNodeConfigFiles map[string]string `json:"extra-node-config-files,omitempty" yaml:"extra-node-config-files,omitempty"` - - // Extra args to add to individual services (set any arg to null to delete) - ExtraNodeKubeAPIServerArgs map[string]*string `json:"extra-node-kube-apiserver-args,omitempty" yaml:"extra-node-kube-apiserver-args,omitempty"` - ExtraNodeKubeControllerManagerArgs map[string]*string `json:"extra-node-kube-controller-manager-args,omitempty" yaml:"extra-node-kube-controller-manager-args,omitempty"` - ExtraNodeKubeSchedulerArgs map[string]*string `json:"extra-node-kube-scheduler-args,omitempty" yaml:"extra-node-kube-scheduler-args,omitempty"` - ExtraNodeKubeProxyArgs map[string]*string `json:"extra-node-kube-proxy-args,omitempty" yaml:"extra-node-kube-proxy-args,omitempty"` - ExtraNodeKubeletArgs map[string]*string `json:"extra-node-kubelet-args,omitempty" yaml:"extra-node-kubelet-args,omitempty"` - ExtraNodeContainerdArgs map[string]*string `json:"extra-node-containerd-args,omitempty" yaml:"extra-node-containerd-args,omitempty"` - ExtraNodeK8sDqliteArgs map[string]*string `json:"extra-node-k8s-dqlite-args,omitempty" yaml:"extra-node-k8s-dqlite-args,omitempty"` - - // Extra configuration for the containerd config.toml - ExtraNodeContainerdConfig map[string]any `json:"extra-node-containerd-config,omitempty" yaml:"extra-node-containerd-config,omitempty"` -} - -type WorkerNodeJoinConfig struct { - KubeletCert *string `json:"kubelet-crt,omitempty" yaml:"kubelet-crt,omitempty"` - KubeletKey *string `json:"kubelet-key,omitempty" yaml:"kubelet-key,omitempty"` - KubeletClientCert *string `json:"kubelet-client-crt,omitempty" yaml:"kubelet-client-crt,omitempty"` - KubeletClientKey *string `json:"kubelet-client-key,omitempty" yaml:"kubelet-client-key,omitempty"` - KubeProxyClientCert *string `json:"kube-proxy-client-crt,omitempty" yaml:"kube-proxy-client-crt,omitempty"` - KubeProxyClientKey *string `json:"kube-proxy-client-key,omitempty" yaml:"kube-proxy-client-key,omitempty"` - - // ExtraNodeConfigFiles will be written to /var/snap/k8s/common/args/conf.d - ExtraNodeConfigFiles map[string]string `json:"extra-node-config-files,omitempty" yaml:"extra-node-config-files,omitempty"` - - // Extra args to add to individual services (set any arg to null to delete) - ExtraNodeKubeProxyArgs map[string]*string `json:"extra-node-kube-proxy-args,omitempty" yaml:"extra-node-kube-proxy-args,omitempty"` - ExtraNodeKubeletArgs map[string]*string `json:"extra-node-kubelet-args,omitempty" yaml:"extra-node-kubelet-args,omitempty"` - ExtraNodeContainerdArgs map[string]*string `json:"extra-node-containerd-args,omitempty" yaml:"extra-node-containerd-args,omitempty"` - ExtraNodeK8sAPIServerProxyArgs map[string]*string `json:"extra-node-k8s-apiserver-proxy-args,omitempty" yaml:"extra-node-k8s-apiserver-proxy-args,omitempty"` - - // Extra configuration for the containerd config.toml - ExtraNodeContainerdConfig map[string]any `json:"extra-node-containerd-config,omitempty" yaml:"extra-node-containerd-config,omitempty"` -} - -func (c *ControlPlaneNodeJoinConfig) GetFrontProxyClientCert() string { - return getField(c.FrontProxyClientCert) -} -func (c *ControlPlaneNodeJoinConfig) GetFrontProxyClientKey() string { - return getField(c.FrontProxyClientKey) -} -func (b *ControlPlaneNodeJoinConfig) GetKubeProxyClientCert() string { - return getField(b.KubeProxyClientCert) -} -func (b *ControlPlaneNodeJoinConfig) GetKubeProxyClientKey() string { - return getField(b.KubeProxyClientKey) -} -func (b *ControlPlaneNodeJoinConfig) GetKubeSchedulerClientCert() string { - return getField(b.KubeSchedulerClientCert) -} -func (b *ControlPlaneNodeJoinConfig) GetKubeSchedulerClientKey() string { - return getField(b.KubeSchedulerClientKey) -} -func (b *ControlPlaneNodeJoinConfig) GetKubeControllerManagerClientCert() string { - return getField(b.KubeControllerManagerClientCert) -} -func (b *ControlPlaneNodeJoinConfig) GetKubeControllerManagerClientKey() string { - return getField(b.KubeControllerManagerClientKey) -} -func (c *ControlPlaneNodeJoinConfig) GetAPIServerCert() string { return getField(c.APIServerCert) } -func (c *ControlPlaneNodeJoinConfig) GetAPIServerKey() string { return getField(c.APIServerKey) } -func (c *ControlPlaneNodeJoinConfig) GetKubeletCert() string { return getField(c.KubeletCert) } -func (c *ControlPlaneNodeJoinConfig) GetKubeletKey() string { return getField(c.KubeletKey) } -func (c *ControlPlaneNodeJoinConfig) GetKubeletClientCert() string { - return getField(c.KubeletClientCert) -} -func (c *ControlPlaneNodeJoinConfig) GetKubeletClientKey() string { - return getField(c.KubeletClientKey) -} - -func (w *WorkerNodeJoinConfig) GetKubeletCert() string { return getField(w.KubeletCert) } -func (w *WorkerNodeJoinConfig) GetKubeletKey() string { return getField(w.KubeletKey) } -func (w *WorkerNodeJoinConfig) GetKubeletClientCert() string { return getField(w.KubeletClientCert) } -func (w *WorkerNodeJoinConfig) GetKubeletClientKey() string { return getField(w.KubeletClientKey) } -func (w *WorkerNodeJoinConfig) GetKubeProxyClientCert() string { - return getField(w.KubeProxyClientCert) -} -func (w *WorkerNodeJoinConfig) GetKubeProxyClientKey() string { return getField(w.KubeProxyClientKey) } - -// WorkerJoinConfigFromMicrocluster parses a microcluster map[string]string and retrieves the WorkerNodeJoinConfig. -func ControlPlaneJoinConfigFromMicrocluster(m map[string]string) (ControlPlaneNodeJoinConfig, error) { - config := ControlPlaneNodeJoinConfig{} - if err := yaml.UnmarshalStrict([]byte(m["controlPlaneJoinConfig"]), &config); err != nil { - return ControlPlaneNodeJoinConfig{}, fmt.Errorf("failed to unmarshal control plane join config: %w", err) - } - return config, nil -} - -// WorkerJoinConfigFromMicrocluster parses a microcluster map[string]string and retrieves the WorkerNodeJoinConfig. -func WorkerJoinConfigFromMicrocluster(m map[string]string) (WorkerNodeJoinConfig, error) { - config := WorkerNodeJoinConfig{} - if err := yaml.UnmarshalStrict([]byte(m["workerJoinConfig"]), &config); err != nil { - return WorkerNodeJoinConfig{}, fmt.Errorf("failed to unmarshal worker join config: %w", err) - } - return config, nil -} diff --git a/src/k8s/api/v1/kubernetes_auth_tokens.go b/src/k8s/api/v1/kubernetes_auth_tokens.go deleted file mode 100644 index 1a2617ca3..000000000 --- a/src/k8s/api/v1/kubernetes_auth_tokens.go +++ /dev/null @@ -1,61 +0,0 @@ -package apiv1 - -// GenerateKubernetesAuthTokenRequest is used to request a new Kubernetes auth token. -type GenerateKubernetesAuthTokenRequest struct { - Username string `json:"username"` - Groups []string `json:"groups"` -} - -// CreateKubernetesAuthTokenResponse is used to return the Kubernetes auth token. -type CreateKubernetesAuthTokenResponse struct { - Token string `json:"token"` -} - -// CreateKubernetesAuthTokenRequest is the request for "DELETE 1.0/kubernetes/auth/tokens". -type RevokeKubernetesAuthTokenRequest struct { - Token string `json:"token"` -} - -// CheckKubernetesAuthTokenResponse is the response for "GET 1.0/kubernetes/auth/tokens". -type CheckKubernetesAuthTokenResponse struct { - Username string `json:"username"` - Groups []string `json:"groups"` -} - -// TokenReviewRequest is the request for "POST 1.0/kubernetes/auth/webhook". -// This mirrors the definition of the Kubernetes API group="authentication.k8s.io/v1" kind="TokenReview" -// https://kubernetes.io/docs/reference/kubernetes-api/authentication-resources/token-review-v1/ -type TokenReview struct { - APIVersion string `json:"apiVersion"` - Kind string `json:"kind"` - Spec TokenReviewSpec `json:"spec"` - Status TokenReviewStatus `json:"status"` -} - -// TokenReviewSpec is set by kube-apiserver in TokenReview. -// This mirrors the definition of the Kubernetes API group="authentication.k8s.io/v1" kind="TokenReview" -// https://kubernetes.io/docs/reference/kubernetes-api/authentication-resources/token-review-v1/#TokenReviewSpec -type TokenReviewSpec struct { - Audiences []string `json:"audiences,omitempty"` - Token string `json:"token"` -} - -// TokenReviewStatus is set by the webhook server in TokenReview. -// This mirrors the definition of the Kubernetes API group="authentication.k8s.io/v1" kind="TokenReview" -// https://kubernetes.io/docs/reference/kubernetes-api/authentication-resources/token-review-v1/#TokenReviewStatus -type TokenReviewStatus struct { - Audiences []string `json:"audiences,omitempty"` - Authenticated bool `json:"authenticated"` - Error string `json:"error,omitempty"` - User TokenReviewStatusUserInfo `json:"user,omitempty"` -} - -// TokenReviewStatusUserInfo is set by the webhook server in TokenReview. -// This mirrors the definition of the Kubernetes API group="authentication.k8s.io/v1" kind="TokenReview" -// https://kubernetes.io/docs/reference/kubernetes-api/authentication-resources/token-review-v1/#TokenReviewStatus -type TokenReviewStatusUserInfo struct { - Extra map[string][]string `json:"extra,omitempty"` - Groups []string `json:"groups,omitempty"` - Username string `json:"username,omitempty"` - UID string `json:"uid,omitempty"` -} diff --git a/src/k8s/api/v1/node.go b/src/k8s/api/v1/node.go deleted file mode 100644 index 4624c0c84..000000000 --- a/src/k8s/api/v1/node.go +++ /dev/null @@ -1,6 +0,0 @@ -package apiv1 - -// GetNodeStatusResponse is the response for "GET 1.0/k8sd/node". -type GetNodeStatusResponse struct { - NodeStatus NodeStatus `json:"status"` -} diff --git a/src/k8s/api/v1/tokens.go b/src/k8s/api/v1/tokens.go deleted file mode 100644 index cb897fde1..000000000 --- a/src/k8s/api/v1/tokens.go +++ /dev/null @@ -1,17 +0,0 @@ -package apiv1 - -// GetJoinTokenRequest is used to request a token for joining a node to the cluster. -type GetJoinTokenRequest struct { - // If true, a token for joining a worker node is created. - // If false, a token for joining a control plane node is created. - Worker bool `json:"worker"` - // Name of the node that should join. - Name string `json:"name"` -} - -// GetJoinTokenResponse is used to return a token for joining nodes in the cluster. -type GetJoinTokenResponse struct { - // We want to be able to quickly find the tokens in the code, but have the same - // JSON response for control-plane and worker nodes, thus the discrepancy in naming. - EncodedToken string `json:"token"` -} diff --git a/src/k8s/api/v1/types.go b/src/k8s/api/v1/types.go deleted file mode 100644 index 29f5b7896..000000000 --- a/src/k8s/api/v1/types.go +++ /dev/null @@ -1,154 +0,0 @@ -package apiv1 - -import ( - "fmt" - "strings" - "time" -) - -type ClusterRole string - -const ( - ClusterRoleControlPlane ClusterRole = "control-plane" - ClusterRoleWorker ClusterRole = "worker" - // The role of a node is unknown if it has not yet joined a cluster, - // currently joining or is about to leave. - ClusterRoleUnknown ClusterRole = "unknown" -) - -// DatastoreRole as provided by dqlite -type DatastoreRole string - -const ( - DatastoreRoleVoter DatastoreRole = "voter" - DatastoreRoleStandBy DatastoreRole = "stand-by" - DatastoreRoleSpare DatastoreRole = "spare" - DatastoreRolePending DatastoreRole = "PENDING" - DatastoreRoleUnknown DatastoreRole = "unknown" -) - -// NodeStatus holds information about a node in the k8s cluster. -type NodeStatus struct { - // Name is the name for this cluster member that was when joining the cluster. - // This is typically the hostname of the node. - Name string `json:"name,omitempty" yaml:"name,omitempty"` - // Address is the IP address of the node. - Address string `json:"address,omitempty" yaml:"address,omitempty"` - // ClusterRole is the role that the node has within the k8s cluster. - ClusterRole ClusterRole `json:"cluster-role,omitempty" yaml:"cluster-role,omitempty"` - // DatastoreRole is the role that the node has within the datastore cluster. - // Only applicable for control-plane nodes, empty for workers. - DatastoreRole DatastoreRole `json:"datastore-role,omitempty" yaml:"datastore-role,omitempty"` -} - -// FeatureStatus encapsulates the deployment status of a feature. -type FeatureStatus struct { - // Enabled shows whether or not the deployment of manifests for a status was successful. - Enabled bool `json:"enabled" yaml:"enabled"` - // Message contains information about the status of a feature. It is only supposed to be human readable and informative and should not be programmatically parsed. - Message string `json:"message" yaml:"message"` - // Version shows the version of the deployed feature. - Version string `json:"version" yaml:"version"` - // UpdatedAt shows when the last update was done. - UpdatedAt time.Time `json:"updated-at" yaml:"updated-at"` -} - -func (f FeatureStatus) String() string { - if f.Message != "" { - return f.Message - } - if f.Enabled { - return "enabled" - } - return "disabled" -} - -type Datastore struct { - Type string `json:"type,omitempty"` - Servers []string `json:"servers,omitempty" yaml:"servers,omitempty"` -} - -// ClusterStatus holds information about the cluster, e.g. its current members -type ClusterStatus struct { - // Ready is true if at least one node in the cluster is in READY state. - Ready bool `json:"ready,omitempty"` - Members []NodeStatus `json:"members,omitempty"` - Config UserFacingClusterConfig `json:"config,omitempty"` - Datastore Datastore `json:"datastore,omitempty"` - - DNS FeatureStatus `json:"dns,omitempty" yaml:"dns,omitempty"` - Network FeatureStatus `json:"network,omitempty" yaml:"network,omitempty"` - LoadBalancer FeatureStatus `json:"load-balancer,omitempty" yaml:"load-balancer,omitempty"` - Ingress FeatureStatus `json:"ingress,omitempty" yaml:"ingress,omitempty"` - Gateway FeatureStatus `json:"gateway,omitempty" yaml:"gateway,omitempty"` - MetricsServer FeatureStatus `json:"metrics-server,omitempty" yaml:"metrics-server,omitempty"` - LocalStorage FeatureStatus `json:"local-storage,omitempty" yaml:"local-storage,omitempty"` -} - -// HaClusterFormed returns true if the cluster is in high-availability mode (more than two voter nodes). -func (c ClusterStatus) HaClusterFormed() bool { - voters := 0 - for _, member := range c.Members { - if member.DatastoreRole == DatastoreRoleVoter { - voters++ - } - } - return voters > 2 -} - -// TICS -COV_GO_SUPPRESSED_ERROR -// we are just formatting the output for the k8s status command, it is ok to ignore failures from result.WriteString() - -// TODO: Print k8s version. However, multiple nodes can run different version, so we would need to query all nodes. -func (c ClusterStatus) String() string { - result := strings.Builder{} - - // Status - if c.Ready { - result.WriteString(fmt.Sprintf("%-25s %s", "cluster status:", "ready")) - } else { - result.WriteString(fmt.Sprintf("%-25s %s", "cluster status:", "not ready")) - } - result.WriteString("\n") - - // Control Plane Nodes - result.WriteString(fmt.Sprintf("%-25s ", "control plane nodes:")) - if len(c.Members) > 0 { - members := make([]string, 0, len(c.Members)) - for _, m := range c.Members { - members = append(members, fmt.Sprintf("%s (%s)", m.Address, m.DatastoreRole)) - } - result.WriteString(strings.Join(members, ", ")) - } else { - result.WriteString("none") - } - result.WriteString("\n") - - // High availability - result.WriteString(fmt.Sprintf("%-25s ", "high availability:")) - if c.HaClusterFormed() { - result.WriteString("yes") - } else { - result.WriteString("no") - } - result.WriteString("\n") - - // Datastore - // TODO: how to understand if the ds is running or not? - if c.Datastore.Type != "" { - result.WriteString(fmt.Sprintf("%-25s %s\n", "datastore:", c.Datastore.Type)) - } else { - result.WriteString(fmt.Sprintf("%-25s %s\n", "datastore:", "disabled")) - } - - result.WriteString(fmt.Sprintf("%-25s %s\n", "network:", c.Network)) - result.WriteString(fmt.Sprintf("%-25s %s\n", "dns:", c.DNS)) - result.WriteString(fmt.Sprintf("%-25s %s\n", "ingress:", c.Ingress)) - result.WriteString(fmt.Sprintf("%-25s %s\n", "load-balancer:", c.LoadBalancer)) - result.WriteString(fmt.Sprintf("%-25s %s\n", "local-storage:", c.LocalStorage)) - result.WriteString(fmt.Sprintf("%-25s %s", "gateway", c.Gateway)) - - return result.String() -} - -// TICS +COV_GO_SUPPRESSED_ERROR diff --git a/src/k8s/api/v1/util.go b/src/k8s/api/v1/util.go deleted file mode 100644 index f4453fdc5..000000000 --- a/src/k8s/api/v1/util.go +++ /dev/null @@ -1,9 +0,0 @@ -package apiv1 - -func getField[T any](val *T) T { - if val != nil { - return *val - } - var zero T - return zero -} diff --git a/src/k8s/api/v1/version.go b/src/k8s/api/v1/version.go deleted file mode 100644 index 3640152b3..000000000 --- a/src/k8s/api/v1/version.go +++ /dev/null @@ -1,8 +0,0 @@ -package apiv1 - -import "github.com/canonical/microcluster/v2/rest/types" - -const ( - // K8sdAPIVersion is the path prefix that will be used for the k8sd endpoints for this api version. - K8sdAPIVersion types.EndpointPrefix = "1.0" -) diff --git a/src/k8s/api/v1/worker.go b/src/k8s/api/v1/worker.go deleted file mode 100644 index daff634d1..000000000 --- a/src/k8s/api/v1/worker.go +++ /dev/null @@ -1,42 +0,0 @@ -package apiv1 - -// WorkerNodeInfoRequest is used by a worker node to retrieve the required credentials -// to join a cluster. -type WorkerNodeInfoRequest struct { - // Address is the address of the worker node. - Address string `json:"address"` -} - -// WorkerNodeInfoResponse is used to return a worker node token. -type WorkerNodeInfoResponse struct { - // CACert is the PEM encoded certificate authority of the cluster. - CACert string `json:"ca,omitempty"` - // ClientCACert is the PEM encoded certificate authority of the cluster clients. - ClientCACert string `json:"client-ca,omitempty"` - // APIServers is a list of kube-apiserver endpoints of the cluster. - APIServers []string `json:"apiServers"` - // KubeletClientCert is the certificate to use in kubelet to authenticate with kube-apiserver. - KubeletClientCert string `json:"kubeletClientCert"` - // KubeletClientKey is the private key to use in kubelet to authenticate with kube-apiserver. - KubeletClientKey string `json:"kubeletClientKey"` - // KubeProxyClientCert is the certificate to use in kube-proxy to authenticate with kube-apiserver. - KubeProxyClientCert string `json:"kubeProxyClientCert"` - // KubeProxyClientKey is the private key to use in kube-proxy to authenticate with kube-apiserver. - KubeProxyClientKey string `json:"kubeProxyClientKey"` - // PodCIDR is the configured CIDR for pods in the cluster. - PodCIDR string `json:"podCIDR"` - // ServiceCIDR is the configured CIDR for services in the cluster. - ServiceCIDR string `json:"serviceCIDR"` - // ClusterDNS is the DNS server address of the cluster. - ClusterDNS string `json:"clusterDNS,omitempty"` - // ClusterDomain is the DNS domain of the cluster. - ClusterDomain string `json:"clusterDomain,omitempty"` - // CloudProvider is the cloud provider used in the cluster. - CloudProvider string `json:"cloudProvider,omitempty"` - // KubeletCert is the certificate to use for kubelet TLS. It will be empty if the cluster is not using self-signed certificates. - KubeletCert string `json:"kubeletCrt,omitempty"` - // KubeletKey is the private key to use for kubelet TLS. It will be empty if the cluster is not using self-signed certificates. - KubeletKey string `json:"kubeletKey,omitempty"` - // K8sdPublicKey is the public key that can be used to validate authenticity of cluster messages. - K8sdPublicKey string `json:"k8sdPublicKey,omitempty"` -} diff --git a/src/k8s/cmd/k8s/k8s_bootstrap.go b/src/k8s/cmd/k8s/k8s_bootstrap.go index 968ca82f2..b4243d824 100644 --- a/src/k8s/cmd/k8s/k8s_bootstrap.go +++ b/src/k8s/cmd/k8s/k8s_bootstrap.go @@ -11,7 +11,7 @@ import ( "time" "unicode" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/canonical/k8s/pkg/config" "github.com/canonical/k8s/pkg/k8sd/features" @@ -129,7 +129,7 @@ func newBootstrapCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { cmd.PrintErrln("Bootstrapping the cluster. This may take a few seconds, please wait.") - node, err := client.BootstrapCluster(cmd.Context(), apiv1.PostClusterBootstrapRequest{ + response, err := client.BootstrapCluster(cmd.Context(), apiv1.BootstrapClusterRequest{ Name: opts.name, Address: address, Config: bootstrapConfig, @@ -141,7 +141,7 @@ func newBootstrapCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { return } - outputFormatter.Print(BootstrapResult{Node: node}) + outputFormatter.Print(BootstrapResult{Node: apiv1.NodeStatus(response)}) }, } diff --git a/src/k8s/cmd/k8s/k8s_bootstrap_test.go b/src/k8s/cmd/k8s/k8s_bootstrap_test.go index e8fdba3d4..39fa074d8 100644 --- a/src/k8s/cmd/k8s/k8s_bootstrap_test.go +++ b/src/k8s/cmd/k8s/k8s_bootstrap_test.go @@ -7,10 +7,9 @@ import ( "path/filepath" "testing" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/canonical/k8s/pkg/utils" - - apiv1 "github.com/canonical/k8s/api/v1" . "github.com/onsi/gomega" ) diff --git a/src/k8s/cmd/k8s/k8s_config.go b/src/k8s/cmd/k8s/k8s_config.go index 93ccf2499..5b20bf8e6 100644 --- a/src/k8s/cmd/k8s/k8s_config.go +++ b/src/k8s/cmd/k8s/k8s_config.go @@ -4,7 +4,7 @@ import ( "context" "time" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/spf13/cobra" ) @@ -45,14 +45,14 @@ func newKubeConfigCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout) cobra.OnFinalize(cancel) - config, err := client.KubeConfig(ctx, apiv1.GetKubeConfigRequest{Server: opts.server}) + response, err := client.KubeConfig(ctx, apiv1.KubeConfigRequest{Server: opts.server}) if err != nil { cmd.PrintErrf("Error: Failed to generate an admin kubeconfig for %q.\n\nThe error was: %v\n", opts.server, err) env.Exit(1) return } - cmd.Println(config) + cmd.Println(response.KubeConfig) }, } cmd.Flags().StringVar(&opts.server, "server", "", "custom cluster server address") diff --git a/src/k8s/cmd/k8s/k8s_disable.go b/src/k8s/cmd/k8s/k8s_disable.go index 8f8f227fd..57eb5904f 100644 --- a/src/k8s/cmd/k8s/k8s_disable.go +++ b/src/k8s/cmd/k8s/k8s_disable.go @@ -6,11 +6,10 @@ import ( "strings" "time" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" + cmdutil "github.com/canonical/k8s/cmd/util" "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/utils" - - api "github.com/canonical/k8s/api/v1" - cmdutil "github.com/canonical/k8s/cmd/util" "github.com/spf13/cobra" ) @@ -34,7 +33,7 @@ func newDisableCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { Args: cmdutil.MinimumNArgs(env, 1), PreRun: chainPreRunHooks(hookRequireRoot(env), hookInitializeFormatter(env, &opts.outputFormat)), Run: func(cmd *cobra.Command, args []string) { - config := api.UserFacingClusterConfig{} + config := apiv1.UserFacingClusterConfig{} if opts.timeout < minTimeout { cmd.PrintErrf("Timeout %v is less than minimum of %v. Using the minimum %v instead.\n", opts.timeout, minTimeout, minTimeout) @@ -44,31 +43,31 @@ func newDisableCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { for _, feature := range args { switch feature { case string(features.Network): - config.Network = api.NetworkConfig{ + config.Network = apiv1.NetworkConfig{ Enabled: utils.Pointer(false), } case string(features.DNS): - config.DNS = api.DNSConfig{ + config.DNS = apiv1.DNSConfig{ Enabled: utils.Pointer(false), } case string(features.Gateway): - config.Gateway = api.GatewayConfig{ + config.Gateway = apiv1.GatewayConfig{ Enabled: utils.Pointer(false), } case string(features.Ingress): - config.Ingress = api.IngressConfig{ + config.Ingress = apiv1.IngressConfig{ Enabled: utils.Pointer(false), } case string(features.LocalStorage): - config.LocalStorage = api.LocalStorageConfig{ + config.LocalStorage = apiv1.LocalStorageConfig{ Enabled: utils.Pointer(false), } case string(features.LoadBalancer): - config.LoadBalancer = api.LoadBalancerConfig{ + config.LoadBalancer = apiv1.LoadBalancerConfig{ Enabled: utils.Pointer(false), } case string(features.MetricsServer): - config.MetricsServer = api.MetricsServerConfig{ + config.MetricsServer = apiv1.MetricsServerConfig{ Enabled: utils.Pointer(false), } default: @@ -77,9 +76,6 @@ func newDisableCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { return } } - request := api.UpdateClusterConfigRequest{ - Config: config, - } client, err := env.Snap.K8sdClient("") if err != nil { @@ -91,7 +87,7 @@ func newDisableCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { cmd.PrintErrf("Disabling %s from the cluster. This may take a few seconds, please wait.\n", strings.Join(args, ", ")) ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout) cobra.OnFinalize(cancel) - if err := client.SetClusterConfig(ctx, request); err != nil { + if err := client.SetClusterConfig(ctx, apiv1.SetClusterConfigRequest{Config: config}); err != nil { cmd.PrintErrf("Error: Failed to disable %s from the cluster.\n\nThe error was: %v\n", strings.Join(args, ", "), err) env.Exit(1) return diff --git a/src/k8s/cmd/k8s/k8s_disable_test.go b/src/k8s/cmd/k8s/k8s_disable_test.go index e03903469..ba442776b 100644 --- a/src/k8s/cmd/k8s/k8s_disable_test.go +++ b/src/k8s/cmd/k8s/k8s_disable_test.go @@ -4,7 +4,7 @@ import ( "bytes" "testing" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/cmd/k8s" cmdutil "github.com/canonical/k8s/cmd/util" k8sdmock "github.com/canonical/k8s/pkg/client/k8sd/mock" @@ -18,7 +18,7 @@ func TestDisableCmd(t *testing.T) { tests := []struct { name string funcs []string - expectedCall apiv1.UpdateClusterConfigRequest + expectedCall apiv1.SetClusterConfigRequest expectedCode int expectedStdout string expectedStderr string @@ -32,7 +32,7 @@ func TestDisableCmd(t *testing.T) { { name: "one", funcs: []string{string(features.Gateway)}, - expectedCall: apiv1.UpdateClusterConfigRequest{ + expectedCall: apiv1.SetClusterConfigRequest{ Config: apiv1.UserFacingClusterConfig{ Gateway: apiv1.GatewayConfig{Enabled: utils.Pointer(false)}, }, @@ -42,7 +42,7 @@ func TestDisableCmd(t *testing.T) { { name: "multiple", funcs: []string{string(features.LoadBalancer), string(features.Gateway)}, - expectedCall: apiv1.UpdateClusterConfigRequest{ + expectedCall: apiv1.SetClusterConfigRequest{ Config: apiv1.UserFacingClusterConfig{ Gateway: apiv1.GatewayConfig{Enabled: utils.Pointer(false)}, LoadBalancer: apiv1.LoadBalancerConfig{Enabled: utils.Pointer(false)}, diff --git a/src/k8s/cmd/k8s/k8s_enable.go b/src/k8s/cmd/k8s/k8s_enable.go index fbe5cbbb4..e49ceb836 100644 --- a/src/k8s/cmd/k8s/k8s_enable.go +++ b/src/k8s/cmd/k8s/k8s_enable.go @@ -6,11 +6,10 @@ import ( "strings" "time" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" + cmdutil "github.com/canonical/k8s/cmd/util" "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/utils" - - api "github.com/canonical/k8s/api/v1" - cmdutil "github.com/canonical/k8s/cmd/util" "github.com/spf13/cobra" ) @@ -34,7 +33,7 @@ func newEnableCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { Args: cmdutil.MinimumNArgs(env, 1), PreRun: chainPreRunHooks(hookRequireRoot(env), hookInitializeFormatter(env, &opts.outputFormat)), Run: func(cmd *cobra.Command, args []string) { - config := api.UserFacingClusterConfig{} + config := apiv1.UserFacingClusterConfig{} if opts.timeout < minTimeout { cmd.PrintErrf("Timeout %v is less than minimum of %v. Using the minimum %v instead.\n", opts.timeout, minTimeout, minTimeout) @@ -44,31 +43,31 @@ func newEnableCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { for _, feature := range args { switch feature { case string(features.Network): - config.Network = api.NetworkConfig{ + config.Network = apiv1.NetworkConfig{ Enabled: utils.Pointer(true), } case string(features.DNS): - config.DNS = api.DNSConfig{ + config.DNS = apiv1.DNSConfig{ Enabled: utils.Pointer(true), } case string(features.Gateway): - config.Gateway = api.GatewayConfig{ + config.Gateway = apiv1.GatewayConfig{ Enabled: utils.Pointer(true), } case string(features.Ingress): - config.Ingress = api.IngressConfig{ + config.Ingress = apiv1.IngressConfig{ Enabled: utils.Pointer(true), } case string(features.LocalStorage): - config.LocalStorage = api.LocalStorageConfig{ + config.LocalStorage = apiv1.LocalStorageConfig{ Enabled: utils.Pointer(true), } case string(features.LoadBalancer): - config.LoadBalancer = api.LoadBalancerConfig{ + config.LoadBalancer = apiv1.LoadBalancerConfig{ Enabled: utils.Pointer(true), } case string(features.MetricsServer): - config.MetricsServer = api.MetricsServerConfig{ + config.MetricsServer = apiv1.MetricsServerConfig{ Enabled: utils.Pointer(true), } default: @@ -77,10 +76,6 @@ func newEnableCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { return } } - request := api.UpdateClusterConfigRequest{ - Config: config, - } - client, err := env.Snap.K8sdClient("") if err != nil { cmd.PrintErrf("Error: Failed to create a k8sd client. Make sure that the k8sd service is running.\n\nThe error was: %v\n", err) @@ -91,7 +86,7 @@ func newEnableCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { cmd.PrintErrf("Enabling %s on the cluster. This may take a few seconds, please wait.\n", strings.Join(args, ", ")) ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout) cobra.OnFinalize(cancel) - if err := client.SetClusterConfig(ctx, request); err != nil { + if err := client.SetClusterConfig(ctx, apiv1.SetClusterConfigRequest{Config: config}); err != nil { cmd.PrintErrf("Error: Failed to enable %s on the cluster.\n\nThe error was: %v\n", strings.Join(args, ", "), err) env.Exit(1) return diff --git a/src/k8s/cmd/k8s/k8s_enable_test.go b/src/k8s/cmd/k8s/k8s_enable_test.go index efba1b262..25194aa0f 100644 --- a/src/k8s/cmd/k8s/k8s_enable_test.go +++ b/src/k8s/cmd/k8s/k8s_enable_test.go @@ -4,7 +4,7 @@ import ( "bytes" "testing" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/cmd/k8s" cmdutil "github.com/canonical/k8s/cmd/util" k8sdmock "github.com/canonical/k8s/pkg/client/k8sd/mock" @@ -18,7 +18,7 @@ func TestK8sEnableCmd(t *testing.T) { tests := []struct { name string funcs []string - expectedCall apiv1.UpdateClusterConfigRequest + expectedCall apiv1.SetClusterConfigRequest expectedCode int expectedStdout string expectedStderr string @@ -32,7 +32,7 @@ func TestK8sEnableCmd(t *testing.T) { { name: "one", funcs: []string{string(features.Gateway)}, - expectedCall: apiv1.UpdateClusterConfigRequest{ + expectedCall: apiv1.SetClusterConfigRequest{ Config: apiv1.UserFacingClusterConfig{ Gateway: apiv1.GatewayConfig{Enabled: utils.Pointer(true)}, }, @@ -42,7 +42,7 @@ func TestK8sEnableCmd(t *testing.T) { { name: "multiple", funcs: []string{string(features.LoadBalancer), string(features.Gateway)}, - expectedCall: apiv1.UpdateClusterConfigRequest{ + expectedCall: apiv1.SetClusterConfigRequest{ Config: apiv1.UserFacingClusterConfig{ Gateway: apiv1.GatewayConfig{Enabled: utils.Pointer(true)}, LoadBalancer: apiv1.LoadBalancerConfig{Enabled: utils.Pointer(true)}, diff --git a/src/k8s/cmd/k8s/k8s_get.go b/src/k8s/cmd/k8s/k8s_get.go index 9e20aea7f..8fb85dfeb 100644 --- a/src/k8s/cmd/k8s/k8s_get.go +++ b/src/k8s/cmd/k8s/k8s_get.go @@ -6,7 +6,7 @@ import ( "strings" "time" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/canonical/k8s/pkg/k8sd/features" "github.com/spf13/cobra" @@ -39,12 +39,13 @@ func newGetCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout) cobra.OnFinalize(cancel) - config, err := client.GetClusterConfig(ctx) + response, err := client.GetClusterConfig(ctx) if err != nil { cmd.PrintErrf("Error: Failed to get the current cluster configuration.\n\nThe error was: %v\n", err) env.Exit(1) return } + config := response.Config config.MetricsServer = apiv1.MetricsServerConfig{} config.CloudProvider = nil diff --git a/src/k8s/cmd/k8s/k8s_get_join_token.go b/src/k8s/cmd/k8s/k8s_get_join_token.go index 2b346e571..3d8e51084 100644 --- a/src/k8s/cmd/k8s/k8s_get_join_token.go +++ b/src/k8s/cmd/k8s/k8s_get_join_token.go @@ -4,7 +4,7 @@ import ( "context" "time" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/spf13/cobra" ) diff --git a/src/k8s/cmd/k8s/k8s_helm.go b/src/k8s/cmd/k8s/k8s_helm.go index e2b7749ef..052876a16 100644 --- a/src/k8s/cmd/k8s/k8s_helm.go +++ b/src/k8s/cmd/k8s/k8s_helm.go @@ -5,7 +5,7 @@ import ( "path/filepath" "syscall" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/spf13/cobra" ) @@ -24,7 +24,7 @@ func newHelmCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { return } - if status, initialized, err := client.NodeStatus(cmd.Context()); err != nil { + if response, initialized, err := client.NodeStatus(cmd.Context()); err != nil { cmd.PrintErrf("Error: Failed to check the current node status.\n\nThe error was: %v\n", err) env.Exit(1) return @@ -32,7 +32,7 @@ func newHelmCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { cmd.PrintErrln("Error: The node is not part of a Kubernetes cluster. You can bootstrap a new cluster with:\n\n sudo k8s bootstrap") env.Exit(1) return - } else if status.ClusterRole == apiv1.ClusterRoleWorker { + } else if response.NodeStatus.ClusterRole == apiv1.ClusterRoleWorker { cmd.PrintErrln("Error: k8s helm commands are not allowed on worker nodes.") env.Exit(1) return diff --git a/src/k8s/cmd/k8s/k8s_join_cluster.go b/src/k8s/cmd/k8s/k8s_join_cluster.go index de7115a5a..7507fedcb 100644 --- a/src/k8s/cmd/k8s/k8s_join_cluster.go +++ b/src/k8s/cmd/k8s/k8s_join_cluster.go @@ -6,7 +6,7 @@ import ( "os" "time" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/canonical/k8s/pkg/config" "github.com/canonical/k8s/pkg/utils" diff --git a/src/k8s/cmd/k8s/k8s_kubectl.go b/src/k8s/cmd/k8s/k8s_kubectl.go index 754d91007..4a9f49f99 100644 --- a/src/k8s/cmd/k8s/k8s_kubectl.go +++ b/src/k8s/cmd/k8s/k8s_kubectl.go @@ -5,7 +5,7 @@ import ( "path/filepath" "syscall" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/spf13/cobra" ) @@ -24,7 +24,7 @@ func newKubectlCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { return } - if status, initialized, err := client.NodeStatus(cmd.Context()); err != nil { + if response, initialized, err := client.NodeStatus(cmd.Context()); err != nil { cmd.PrintErrf("Error: Failed to retrieve the node status.\n\nThe error was: %v\n", err) env.Exit(1) return @@ -32,7 +32,7 @@ func newKubectlCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { cmd.PrintErrln("Error: The node is not part of a Kubernetes cluster. You can bootstrap a new cluster with:\n\n sudo k8s bootstrap") env.Exit(1) return - } else if status.ClusterRole == apiv1.ClusterRoleWorker { + } else if response.NodeStatus.ClusterRole == apiv1.ClusterRoleWorker { cmd.PrintErrln("Error: k8s kubectl commands are not allowed on worker nodes.") env.Exit(1) return diff --git a/src/k8s/cmd/k8s/k8s_local_node_status.go b/src/k8s/cmd/k8s/k8s_local_node_status.go index 71cf71268..82b103ef0 100644 --- a/src/k8s/cmd/k8s/k8s_local_node_status.go +++ b/src/k8s/cmd/k8s/k8s_local_node_status.go @@ -22,7 +22,7 @@ func newLocalNodeStatusCommand(env cmdutil.ExecutionEnvironment) *cobra.Command return } - status, initialized, err := client.NodeStatus(cmd.Context()) + response, initialized, err := client.NodeStatus(cmd.Context()) if err != nil { cmd.PrintErrf("Error: Failed to check the current node status.\n\nThe error was: %v\n", err) env.Exit(1) @@ -33,7 +33,7 @@ func newLocalNodeStatusCommand(env cmdutil.ExecutionEnvironment) *cobra.Command return } - outputFormatter.Print(status) + outputFormatter.Print(response.NodeStatus) }, } cmd.Flags().StringVar(&opts.outputFormat, "output-format", "plain", "set the output format to one of plain, json or yaml") diff --git a/src/k8s/cmd/k8s/k8s_remove_node.go b/src/k8s/cmd/k8s/k8s_remove_node.go index b5939be01..d88b0c011 100644 --- a/src/k8s/cmd/k8s/k8s_remove_node.go +++ b/src/k8s/cmd/k8s/k8s_remove_node.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/spf13/cobra" ) diff --git a/src/k8s/cmd/k8s/k8s_set.go b/src/k8s/cmd/k8s/k8s_set.go index 659444a21..946153916 100644 --- a/src/k8s/cmd/k8s/k8s_set.go +++ b/src/k8s/cmd/k8s/k8s_set.go @@ -6,7 +6,7 @@ import ( "strings" "time" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/utils" @@ -55,14 +55,10 @@ func newSetCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { return } - request := apiv1.UpdateClusterConfigRequest{ - Config: config, - } - ctx, cancel := context.WithTimeout(cmd.Context(), opts.timeout) cobra.OnFinalize(cancel) - if err := client.SetClusterConfig(ctx, request); err != nil { + if err := client.SetClusterConfig(ctx, apiv1.SetClusterConfigRequest{Config: config}); err != nil { cmd.PrintErrf("Error: Failed to apply requested cluster configuration changes.\n\nThe error was: %v\n", err) env.Exit(1) return diff --git a/src/k8s/cmd/k8s/k8s_set_test.go b/src/k8s/cmd/k8s/k8s_set_test.go index 361fd8fe8..8c6bc23d1 100644 --- a/src/k8s/cmd/k8s/k8s_set_test.go +++ b/src/k8s/cmd/k8s/k8s_set_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/utils" . "github.com/onsi/gomega" "github.com/onsi/gomega/types" diff --git a/src/k8s/cmd/k8s/k8s_status.go b/src/k8s/cmd/k8s/k8s_status.go index 839bc4151..15f235e88 100644 --- a/src/k8s/cmd/k8s/k8s_status.go +++ b/src/k8s/cmd/k8s/k8s_status.go @@ -4,7 +4,7 @@ import ( "context" "time" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/spf13/cobra" ) @@ -46,17 +46,18 @@ func newStatusCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { return } - status, err := client.ClusterStatus(ctx, opts.waitReady) + response, err := client.ClusterStatus(ctx, opts.waitReady) if err != nil { cmd.PrintErrf("Error: Failed to retrieve the cluster status.\n\nThe error was: %v\n", err) env.Exit(1) return } + status := response.ClusterStatus // silence the config, this should be retrieved with "k8s get". status.Config = apiv1.UserFacingClusterConfig{} - outputFormatter.Print(status) + outputFormatter.Print(ClusterStatus(status)) }, } diff --git a/src/k8s/cmd/k8s/k8s_status_format.go b/src/k8s/cmd/k8s/k8s_status_format.go new file mode 100644 index 000000000..892bc6e13 --- /dev/null +++ b/src/k8s/cmd/k8s/k8s_status_format.go @@ -0,0 +1,77 @@ +package k8s + +import ( + "fmt" + "strings" + + apiv1 "github.com/canonical/k8s-snap-api/api/v1" +) + +type ClusterStatus apiv1.ClusterStatus + +// TICS -COV_GO_SUPPRESSED_ERROR +// we are just formatting the output for the k8s status command, it is ok to ignore failures from result.WriteString() + +func (c ClusterStatus) isHA() bool { + voters := 0 + for _, member := range c.Members { + if member.DatastoreRole == apiv1.DatastoreRoleVoter { + voters++ + } + } + return voters > 2 +} + +// TODO: Print k8s version. However, multiple nodes can run different version, so we would need to query all nodes. +func (c ClusterStatus) String() string { + result := strings.Builder{} + + // Status + if c.Ready { + result.WriteString(fmt.Sprintf("%-25s %s", "cluster status:", "ready")) + } else { + result.WriteString(fmt.Sprintf("%-25s %s", "cluster status:", "not ready")) + } + result.WriteString("\n") + + // Control Plane Nodes + result.WriteString(fmt.Sprintf("%-25s ", "control plane nodes:")) + if len(c.Members) > 0 { + members := make([]string, 0, len(c.Members)) + for _, m := range c.Members { + members = append(members, fmt.Sprintf("%s (%s)", m.Address, m.DatastoreRole)) + } + result.WriteString(strings.Join(members, ", ")) + } else { + result.WriteString("none") + } + result.WriteString("\n") + + // High availability + result.WriteString(fmt.Sprintf("%-25s ", "high availability:")) + if c.isHA() { + result.WriteString("yes") + } else { + result.WriteString("no") + } + result.WriteString("\n") + + // Datastore + // TODO: how to understand if the ds is running or not? + if c.Datastore.Type != "" { + result.WriteString(fmt.Sprintf("%-25s %s\n", "datastore:", c.Datastore.Type)) + } else { + result.WriteString(fmt.Sprintf("%-25s %s\n", "datastore:", "disabled")) + } + + result.WriteString(fmt.Sprintf("%-25s %s\n", "network:", c.Network)) + result.WriteString(fmt.Sprintf("%-25s %s\n", "dns:", c.DNS)) + result.WriteString(fmt.Sprintf("%-25s %s\n", "ingress:", c.Ingress)) + result.WriteString(fmt.Sprintf("%-25s %s\n", "load-balancer:", c.LoadBalancer)) + result.WriteString(fmt.Sprintf("%-25s %s\n", "local-storage:", c.LocalStorage)) + result.WriteString(fmt.Sprintf("%-25s %s", "gateway", c.Gateway)) + + return result.String() +} + +// TICS +COV_GO_SUPPRESSED_ERROR diff --git a/src/k8s/api/v1/types_test.go b/src/k8s/cmd/k8s/k8s_status_format_test.go similarity index 70% rename from src/k8s/api/v1/types_test.go rename to src/k8s/cmd/k8s/k8s_status_format_test.go index 8a5c02cfb..ea09c2ee0 100644 --- a/src/k8s/api/v1/types_test.go +++ b/src/k8s/cmd/k8s/k8s_status_format_test.go @@ -1,59 +1,14 @@ -package apiv1_test +package k8s_test import ( "testing" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" + "github.com/canonical/k8s/cmd/k8s" . "github.com/onsi/gomega" ) -func TestHaClusterFormed(t *testing.T) { - g := NewWithT(t) - - testCases := []struct { - name string - members []apiv1.NodeStatus - expectedResult bool - }{ - { - name: "Less than 3 voters", - members: []apiv1.NodeStatus{ - {DatastoreRole: apiv1.DatastoreRoleVoter}, - {DatastoreRole: apiv1.DatastoreRoleVoter}, - {DatastoreRole: apiv1.DatastoreRoleStandBy}, - }, - expectedResult: false, - }, - { - name: "Exactly 3 voters", - members: []apiv1.NodeStatus{ - {DatastoreRole: apiv1.DatastoreRoleVoter}, - {DatastoreRole: apiv1.DatastoreRoleVoter}, - {DatastoreRole: apiv1.DatastoreRoleVoter}, - }, - expectedResult: true, - }, - { - name: "More than 3 voters", - members: []apiv1.NodeStatus{ - {DatastoreRole: apiv1.DatastoreRoleVoter}, - {DatastoreRole: apiv1.DatastoreRoleVoter}, - {DatastoreRole: apiv1.DatastoreRoleVoter}, - {DatastoreRole: apiv1.DatastoreRoleVoter}, - {DatastoreRole: apiv1.DatastoreRoleStandBy}, - }, - expectedResult: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - g.Expect(apiv1.ClusterStatus{Members: tc.members}.HaClusterFormed()).To(Equal(tc.expectedResult)) - }) - } -} - -func TestString(t *testing.T) { +func TestClusterStatusFormat(t *testing.T) { testCases := []struct { name string clusterStatus apiv1.ClusterStatus @@ -133,7 +88,7 @@ gateway disabled`, for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { g := NewWithT(t) - g.Expect(tc.clusterStatus.String()).To(Equal(tc.expectedOutput)) + g.Expect(k8s.ClusterStatus(tc.clusterStatus).String()).To(Equal(tc.expectedOutput)) }) } } diff --git a/src/k8s/cmd/k8s/k8s_x_capi.go b/src/k8s/cmd/k8s/k8s_x_capi.go index 193df8e95..bdeb88377 100644 --- a/src/k8s/cmd/k8s/k8s_x_capi.go +++ b/src/k8s/cmd/k8s/k8s_x_capi.go @@ -1,7 +1,7 @@ package k8s import ( - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" cmdutil "github.com/canonical/k8s/cmd/util" "github.com/spf13/cobra" ) @@ -26,7 +26,7 @@ func newXCAPICmd(env cmdutil.ExecutionEnvironment) *cobra.Command { return } - err = client.SetClusterAPIAuthToken(cmd.Context(), apiv1.SetClusterAPIAuthTokenRequest{Token: token}) + err = client.SetClusterAPIAuthToken(cmd.Context(), apiv1.ClusterAPISetAuthTokenRequest{Token: token}) if err != nil { cmd.PrintErrf("Error: Failed to set the CAPI auth token.\n\nThe error was: %v\n", err) env.Exit(1) diff --git a/src/k8s/cmd/k8s/k8s_x_snapd_config.go b/src/k8s/cmd/k8s/k8s_x_snapd_config.go index 2064d5e5b..82bb8aaf5 100644 --- a/src/k8s/cmd/k8s/k8s_x_snapd_config.go +++ b/src/k8s/cmd/k8s/k8s_x_snapd_config.go @@ -52,13 +52,13 @@ func newXSnapdConfigCmd(env cmdutil.ExecutionEnvironment) *cobra.Command { env.Exit(1) return } - config, err := client.GetClusterConfig(cmd.Context()) + response, err := client.GetClusterConfig(cmd.Context()) if err != nil { cmd.PrintErrf("Error: failed to retrieve cluster configuration: %v\n", err) env.Exit(1) return } - if err := snapdconfig.SetSnapdFromK8sd(cmd.Context(), config, env.Snap); err != nil { + if err := snapdconfig.SetSnapdFromK8sd(cmd.Context(), response.Config, env.Snap); err != nil { cmd.PrintErrf("Error: failed to update snapd state: %v\n", err) env.Exit(1) return diff --git a/src/k8s/go.mod b/src/k8s/go.mod index 28856b25e..4d6a3fc97 100644 --- a/src/k8s/go.mod +++ b/src/k8s/go.mod @@ -1,10 +1,11 @@ module github.com/canonical/k8s -go 1.22.5 +go 1.22.6 require ( dario.cat/mergo v1.0.0 github.com/canonical/go-dqlite v1.22.0 + github.com/canonical/k8s-snap-api v1.0.2 github.com/canonical/lxd v0.0.0-20240730172021-8e39e5d4f55f github.com/canonical/microcluster/v2 v2.0.2 github.com/go-logr/logr v1.4.2 diff --git a/src/k8s/go.sum b/src/k8s/go.sum index 05de77541..de638356d 100644 --- a/src/k8s/go.sum +++ b/src/k8s/go.sum @@ -99,6 +99,8 @@ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXe github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/canonical/go-dqlite v1.22.0 h1:DuJmfcREl4gkQJyvZzjl2GHFZROhbPyfdjDRQXpkOyw= github.com/canonical/go-dqlite v1.22.0/go.mod h1:Uvy943N8R4CFUAs59A1NVaziWY9nJ686lScY7ywurfg= +github.com/canonical/k8s-snap-api v1.0.2 h1:9tyIneGQ6dPouX/8DH/HBqQIk+PF+MtQB3Qwt43Cuu4= +github.com/canonical/k8s-snap-api v1.0.2/go.mod h1:LDPoIYCeYnfgOFrwVPJ/4edGU264w7BB7g0GsVi36AY= github.com/canonical/lxd v0.0.0-20240730172021-8e39e5d4f55f h1:bTaF5FmQk66wI8ILr+pzelTY6iNLXE9c2Ks2HG4Sp5U= github.com/canonical/lxd v0.0.0-20240730172021-8e39e5d4f55f/go.mod h1:BVyKLSsJLTLX3o6WW0f5YDOO+J5HE3Np2WwYVrug0sY= github.com/canonical/microcluster/v2 v2.0.2 h1:T0Bc3EQTdR17nAhKlAmGORL4y7FPcgAR09fWj/WlQck= diff --git a/src/k8s/pkg/client/k8sd/cluster.go b/src/k8s/pkg/client/k8sd/cluster.go index 47c1aaaa9..0daebcc57 100644 --- a/src/k8s/pkg/client/k8sd/cluster.go +++ b/src/k8s/pkg/client/k8sd/cluster.go @@ -5,13 +5,12 @@ import ( "fmt" "time" - apiv1 "github.com/canonical/k8s/api/v1" - "github.com/canonical/lxd/shared/api" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" ) -func (c *k8sd) BootstrapCluster(ctx context.Context, request apiv1.PostClusterBootstrapRequest) (apiv1.NodeStatus, error) { +func (c *k8sd) BootstrapCluster(ctx context.Context, request apiv1.BootstrapClusterRequest) (apiv1.BootstrapClusterResponse, error) { if err := c.app.Ready(ctx); err != nil { - return apiv1.NodeStatus{}, fmt.Errorf("k8sd is not ready: %w", err) + return apiv1.BootstrapClusterResponse{}, fmt.Errorf("k8sd is not ready: %w", err) } // NOTE(neoaggelos): microcluster adds an arbitrary 30 second timeout in case no context deadline is set. @@ -19,12 +18,7 @@ func (c *k8sd) BootstrapCluster(ctx context.Context, request apiv1.PostClusterBo ctx, cancel := context.WithTimeout(ctx, request.Timeout+30*time.Second) defer cancel() - var response apiv1.NodeStatus - if err := c.client.Query(ctx, "POST", apiv1.K8sdAPIVersion, api.NewURL().Path("k8sd", "cluster"), request, &response); err != nil { - return apiv1.NodeStatus{}, fmt.Errorf("failed to POST /k8sd/cluster: %w", err) - } - - return response, nil + return query(ctx, c, "POST", apiv1.BootstrapClusterRPC, request, &apiv1.BootstrapClusterResponse{}) } func (c *k8sd) JoinCluster(ctx context.Context, request apiv1.JoinClusterRequest) error { @@ -37,11 +31,8 @@ func (c *k8sd) JoinCluster(ctx context.Context, request apiv1.JoinClusterRequest ctx, cancel := context.WithTimeout(ctx, request.Timeout+30*time.Second) defer cancel() - if err := c.client.Query(ctx, "POST", apiv1.K8sdAPIVersion, api.NewURL().Path("k8sd", "cluster", "join"), request, nil); err != nil { - return fmt.Errorf("failed to POST /k8sd/cluster/join: %w", err) - } - - return nil + _, err := query(ctx, c, "POST", apiv1.JoinClusterRPC, request, &apiv1.JoinClusterResponse{}) + return err } func (c *k8sd) RemoveNode(ctx context.Context, request apiv1.RemoveNodeRequest) error { @@ -50,16 +41,10 @@ func (c *k8sd) RemoveNode(ctx context.Context, request apiv1.RemoveNodeRequest) ctx, cancel := context.WithTimeout(ctx, request.Timeout+30*time.Second) defer cancel() - if err := c.client.Query(ctx, "POST", apiv1.K8sdAPIVersion, api.NewURL().Path("k8sd", "cluster", "remove"), request, nil); err != nil { - return fmt.Errorf("failed to POST /k8sd/cluster/remove: %w", err) - } - return nil + _, err := query(ctx, c, "POST", apiv1.RemoveNodeRPC, request, &apiv1.RemoveNodeResponse{}) + return err } func (c *k8sd) GetJoinToken(ctx context.Context, request apiv1.GetJoinTokenRequest) (apiv1.GetJoinTokenResponse, error) { - var response apiv1.GetJoinTokenResponse - if err := c.client.Query(ctx, "POST", apiv1.K8sdAPIVersion, api.NewURL().Path("k8sd", "cluster", "tokens"), request, &response); err != nil { - return apiv1.GetJoinTokenResponse{}, fmt.Errorf("failed to POST /k8sd/cluster/tokens: %w", err) - } - return response, nil + return query(ctx, c, "POST", apiv1.GetJoinTokenRPC, request, &apiv1.GetJoinTokenResponse{}) } diff --git a/src/k8s/pkg/client/k8sd/clusterapi.go b/src/k8s/pkg/client/k8sd/clusterapi.go index 99fee22ab..27e3105aa 100644 --- a/src/k8s/pkg/client/k8sd/clusterapi.go +++ b/src/k8s/pkg/client/k8sd/clusterapi.go @@ -2,15 +2,11 @@ package k8sd import ( "context" - "fmt" - apiv1 "github.com/canonical/k8s/api/v1" - "github.com/canonical/lxd/shared/api" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" ) -func (c *k8sd) SetClusterAPIAuthToken(ctx context.Context, request apiv1.SetClusterAPIAuthTokenRequest) error { - if err := c.client.Query(ctx, "POST", apiv1.K8sdAPIVersion, api.NewURL().Path("x", "capi", "set-auth-token"), request, nil); err != nil { - return fmt.Errorf("failed to POST /x/capi/set-auth-token: %w", err) - } - return nil +func (c *k8sd) SetClusterAPIAuthToken(ctx context.Context, request apiv1.ClusterAPISetAuthTokenRequest) error { + _, err := query(ctx, c, "POST", apiv1.ClusterAPISetAuthTokenRPC, request, &apiv1.ClusterAPIGetJoinTokenResponse{}) + return err } diff --git a/src/k8s/pkg/client/k8sd/config.go b/src/k8s/pkg/client/k8sd/config.go index b31254530..1d31b63d3 100644 --- a/src/k8s/pkg/client/k8sd/config.go +++ b/src/k8s/pkg/client/k8sd/config.go @@ -2,24 +2,15 @@ package k8sd import ( "context" - "fmt" - apiv1 "github.com/canonical/k8s/api/v1" - "github.com/canonical/lxd/shared/api" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" ) -func (c *k8sd) SetClusterConfig(ctx context.Context, request apiv1.UpdateClusterConfigRequest) error { - if err := c.client.Query(ctx, "PUT", apiv1.K8sdAPIVersion, api.NewURL().Path("k8sd", "cluster", "config"), request, nil); err != nil { - return fmt.Errorf("failed to PUT /k8sd/cluster/config: %w", err) - } - return nil +func (c *k8sd) SetClusterConfig(ctx context.Context, request apiv1.SetClusterConfigRequest) error { + _, err := query(ctx, c, "PUT", apiv1.SetClusterConfigRPC, request, &apiv1.SetClusterConfigResponse{}) + return err } -func (c *k8sd) GetClusterConfig(ctx context.Context) (apiv1.UserFacingClusterConfig, error) { - var response apiv1.GetClusterConfigResponse - if err := c.client.Query(ctx, "GET", apiv1.K8sdAPIVersion, api.NewURL().Path("k8sd", "cluster", "config"), nil, &response); err != nil { - return apiv1.UserFacingClusterConfig{}, fmt.Errorf("failed to GET /k8sd/cluster/config: %w", err) - } - - return response.Config, nil +func (c *k8sd) GetClusterConfig(ctx context.Context) (apiv1.GetClusterConfigResponse, error) { + return query(ctx, c, "GET", apiv1.GetClusterConfigRPC, nil, &apiv1.GetClusterConfigResponse{}) } diff --git a/src/k8s/pkg/client/k8sd/interface.go b/src/k8s/pkg/client/k8sd/interface.go index 47205a4a5..523e0b726 100644 --- a/src/k8s/pkg/client/k8sd/interface.go +++ b/src/k8s/pkg/client/k8sd/interface.go @@ -3,13 +3,13 @@ package k8sd import ( "context" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" ) // ClusterClient implements methods for managing the cluster members. type ClusterClient interface { // BootstrapCluster initializes a new cluster using the provided configuration. - BootstrapCluster(context.Context, apiv1.PostClusterBootstrapRequest) (apiv1.NodeStatus, error) + BootstrapCluster(context.Context, apiv1.BootstrapClusterRequest) (apiv1.BootstrapClusterResponse, error) // GetJoinToken generates a token for nodes to join the cluster. GetJoinToken(context.Context, apiv1.GetJoinTokenRequest) (apiv1.GetJoinTokenResponse, error) // JoinCluster joins an existing cluster. @@ -22,29 +22,29 @@ type ClusterClient interface { type StatusClient interface { // NodeStatus retrieves the current status of the local node. // The second return value is false if the node is not part of a cluster. - NodeStatus(ctx context.Context) (apiv1.NodeStatus, bool, error) + NodeStatus(ctx context.Context) (apiv1.NodeStatusResponse, bool, error) // ClusterStatus retrieves the current status of the Kubernetes cluster. - ClusterStatus(ctx context.Context, waitReady bool) (apiv1.ClusterStatus, error) + ClusterStatus(ctx context.Context, waitReady bool) (apiv1.ClusterStatusResponse, error) } // ConfigClient implements methods to retrieve and manage the cluster configuration. type ConfigClient interface { // GetClusterConfig retrieves the k8sd cluster configuration. - GetClusterConfig(context.Context) (apiv1.UserFacingClusterConfig, error) + GetClusterConfig(context.Context) (apiv1.GetClusterConfigResponse, error) // SetClusterConfig updates the k8sd cluster configuration. - SetClusterConfig(context.Context, apiv1.UpdateClusterConfigRequest) error + SetClusterConfig(context.Context, apiv1.SetClusterConfigRequest) error } // UserClient implements methods to enable accessing the cluster. type UserClient interface { // KubeConfig retrieves a kubeconfig file that can be used to access the cluster. - KubeConfig(context.Context, apiv1.GetKubeConfigRequest) (string, error) + KubeConfig(context.Context, apiv1.KubeConfigRequest) (apiv1.KubeConfigResponse, error) } // ClusterAPIClient implements methods related to ClusterAPI endpoints. type ClusterAPIClient interface { // SetClusterAPIAuthToken sets the well-known token that can be used authenticating requests to the ClusterAPI related endpoints. - SetClusterAPIAuthToken(context.Context, apiv1.SetClusterAPIAuthTokenRequest) error + SetClusterAPIAuthToken(context.Context, apiv1.ClusterAPISetAuthTokenRequest) error } type Client interface { diff --git a/src/k8s/pkg/client/k8sd/mock/mock.go b/src/k8s/pkg/client/k8sd/mock/mock.go index 44d125d3f..d4d21123d 100644 --- a/src/k8s/pkg/client/k8sd/mock/mock.go +++ b/src/k8s/pkg/client/k8sd/mock/mock.go @@ -3,18 +3,18 @@ package mock import ( "context" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/client/k8sd" ) // Mock is a mock implementation of k8sd.Client. type Mock struct { // k8sd.ClusterClient - BootstrapClusterCalledWith apiv1.PostClusterBootstrapRequest - BootstrapClusterResult apiv1.NodeStatus + BootstrapClusterCalledWith apiv1.BootstrapClusterRequest + BootstrapClusterResponse apiv1.BootstrapClusterResponse BootstrapClusterErr error GetJoinTokenCalledWith apiv1.GetJoinTokenRequest - GetJoinTokenResult apiv1.GetJoinTokenResponse + GetJoinTokenResponse apiv1.GetJoinTokenResponse GetJoinTokenErr error JoinClusterCalledWith apiv1.JoinClusterRequest JoinClusterErr error @@ -22,35 +22,35 @@ type Mock struct { RemoveNodeErr error // k8sd.StatusClient - NodeStatusResult apiv1.NodeStatus + NodeStatusResponse apiv1.NodeStatusResponse NodeStatusInitialized bool NodeStatusErr error - ClusterStatusResult apiv1.ClusterStatus + ClusterStatusResponse apiv1.ClusterStatusResponse ClusterStatusErr error // k8sd.ConfigClient - GetClusterConfigResult apiv1.UserFacingClusterConfig + GetClusterConfigResponse apiv1.GetClusterConfigResponse GetClusterConfigErr error - SetClusterConfigCalledWith apiv1.UpdateClusterConfigRequest + SetClusterConfigCalledWith apiv1.SetClusterConfigRequest SetClusterConfigErr error // k8sd.UserClient - KubeConfigCalledWith apiv1.GetKubeConfigRequest - KubeConfigResult string + KubeConfigCalledWith apiv1.KubeConfigRequest + KubeConfigResponse apiv1.KubeConfigResponse KubeConfigErr error // k8sd.ClusterAPIClient - SetClusterAPIAuthTokenCalledWith apiv1.SetClusterAPIAuthTokenRequest + SetClusterAPIAuthTokenCalledWith apiv1.ClusterAPISetAuthTokenRequest SetClusterAPIAuthTokenErr error } -func (m *Mock) BootstrapCluster(_ context.Context, request apiv1.PostClusterBootstrapRequest) (apiv1.NodeStatus, error) { +func (m *Mock) BootstrapCluster(_ context.Context, request apiv1.BootstrapClusterRequest) (apiv1.BootstrapClusterResponse, error) { m.BootstrapClusterCalledWith = request - return m.BootstrapClusterResult, m.BootstrapClusterErr + return m.BootstrapClusterResponse, m.BootstrapClusterErr } func (m *Mock) GetJoinToken(_ context.Context, request apiv1.GetJoinTokenRequest) (apiv1.GetJoinTokenResponse, error) { m.GetJoinTokenCalledWith = request - return m.GetJoinTokenResult, m.GetJoinTokenErr + return m.GetJoinTokenResponse, m.GetJoinTokenErr } func (m *Mock) JoinCluster(_ context.Context, request apiv1.JoinClusterRequest) error { m.JoinClusterCalledWith = request @@ -61,27 +61,27 @@ func (m *Mock) RemoveNode(_ context.Context, request apiv1.RemoveNodeRequest) er return m.RemoveNodeErr } -func (m *Mock) NodeStatus(_ context.Context) (apiv1.NodeStatus, bool, error) { - return m.NodeStatusResult, m.NodeStatusInitialized, m.NodeStatusErr +func (m *Mock) NodeStatus(_ context.Context) (apiv1.NodeStatusResponse, bool, error) { + return m.NodeStatusResponse, m.NodeStatusInitialized, m.NodeStatusErr } -func (m *Mock) ClusterStatus(_ context.Context, waitReady bool) (apiv1.ClusterStatus, error) { - return m.ClusterStatusResult, m.ClusterStatusErr +func (m *Mock) ClusterStatus(_ context.Context, waitReady bool) (apiv1.ClusterStatusResponse, error) { + return m.ClusterStatusResponse, m.ClusterStatusErr } -func (m *Mock) GetClusterConfig(_ context.Context) (apiv1.UserFacingClusterConfig, error) { - return m.GetClusterConfigResult, m.GetClusterConfigErr +func (m *Mock) GetClusterConfig(_ context.Context) (apiv1.GetClusterConfigResponse, error) { + return m.GetClusterConfigResponse, m.GetClusterConfigErr } -func (m *Mock) SetClusterConfig(_ context.Context, request apiv1.UpdateClusterConfigRequest) error { +func (m *Mock) SetClusterConfig(_ context.Context, request apiv1.SetClusterConfigRequest) error { m.SetClusterConfigCalledWith = request return m.SetClusterConfigErr } -func (m *Mock) KubeConfig(_ context.Context, request apiv1.GetKubeConfigRequest) (string, error) { +func (m *Mock) KubeConfig(_ context.Context, request apiv1.KubeConfigRequest) (apiv1.KubeConfigResponse, error) { m.KubeConfigCalledWith = request - return m.KubeConfigResult, m.KubeConfigErr + return m.KubeConfigResponse, m.KubeConfigErr } -func (m *Mock) SetClusterAPIAuthToken(_ context.Context, request apiv1.SetClusterAPIAuthTokenRequest) error { +func (m *Mock) SetClusterAPIAuthToken(_ context.Context, request apiv1.ClusterAPISetAuthTokenRequest) error { m.SetClusterAPIAuthTokenCalledWith = request return m.SetClusterAPIAuthTokenErr } diff --git a/src/k8s/pkg/client/k8sd/query.go b/src/k8s/pkg/client/k8sd/query.go new file mode 100644 index 000000000..ade4f5877 --- /dev/null +++ b/src/k8s/pkg/client/k8sd/query.go @@ -0,0 +1,19 @@ +package k8sd + +import ( + "context" + "fmt" + "strings" + + apiv1 "github.com/canonical/k8s-snap-api/api/v1" + "github.com/canonical/lxd/shared/api" +) + +// query is a helper method to wrap common error checking and response handling. +func query[T any](ctx context.Context, c *k8sd, method string, path string, in any, out *T) (T, error) { + if err := c.client.Query(ctx, method, apiv1.K8sdAPIVersion, api.NewURL().Path(strings.Split(path, "/")...), in, out); err != nil { + var zero T + return zero, fmt.Errorf("failed to %s /%s: %w", method, path, err) + } + return *out, nil +} diff --git a/src/k8s/pkg/client/k8sd/status.go b/src/k8s/pkg/client/k8sd/status.go index 0e9e85179..05c60a023 100644 --- a/src/k8s/pkg/client/k8sd/status.go +++ b/src/k8s/pkg/client/k8sd/status.go @@ -3,40 +3,41 @@ package k8sd import ( "context" "errors" - "fmt" "net/http" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/utils/control" "github.com/canonical/lxd/shared/api" ) -func (c *k8sd) NodeStatus(ctx context.Context) (apiv1.NodeStatus, bool, error) { - var response apiv1.GetNodeStatusResponse - if err := c.client.Query(ctx, "GET", apiv1.K8sdAPIVersion, api.NewURL().Path("k8sd", "node"), nil, &response); err != nil { - +func (c *k8sd) NodeStatus(ctx context.Context) (apiv1.NodeStatusResponse, bool, error) { + response, err := query(ctx, c, "GET", apiv1.NodeStatusRPC, nil, &apiv1.NodeStatusResponse{}) + if err != nil { // Error 503 means the node is not initialized yet var statusErr api.StatusError if errors.As(err, &statusErr) { if statusErr.Status() == http.StatusServiceUnavailable { - return apiv1.NodeStatus{}, false, nil + return apiv1.NodeStatusResponse{}, false, nil } } - return apiv1.NodeStatus{}, false, fmt.Errorf("failed to GET /k8sd/node: %w", err) + return apiv1.NodeStatusResponse{}, false, err } - return response.NodeStatus, true, nil + + return response, true, nil } -func (c *k8sd) ClusterStatus(ctx context.Context, waitReady bool) (apiv1.ClusterStatus, error) { - var response apiv1.GetClusterStatusResponse +func (c *k8sd) ClusterStatus(ctx context.Context, waitReady bool) (apiv1.ClusterStatusResponse, error) { + var response apiv1.ClusterStatusResponse if err := control.WaitUntilReady(ctx, func() (bool, error) { - if err := c.client.Query(ctx, "GET", apiv1.K8sdAPIVersion, api.NewURL().Path("k8sd", "cluster"), nil, &response); err != nil { - return false, fmt.Errorf("failed to GET /k8sd/cluster: %w", err) + var err error + response, err = query(ctx, c, "GET", apiv1.ClusterStatusRPC, nil, &apiv1.ClusterStatusResponse{}) + if err != nil { + return false, err } return !waitReady || response.ClusterStatus.Ready, nil }); err != nil { - return apiv1.ClusterStatus{}, err + return apiv1.ClusterStatusResponse{}, err } - return response.ClusterStatus, nil + return response, nil } diff --git a/src/k8s/pkg/client/k8sd/user.go b/src/k8s/pkg/client/k8sd/user.go index 6f8e71f71..e3a373b93 100644 --- a/src/k8s/pkg/client/k8sd/user.go +++ b/src/k8s/pkg/client/k8sd/user.go @@ -2,16 +2,10 @@ package k8sd import ( "context" - "fmt" - apiv1 "github.com/canonical/k8s/api/v1" - "github.com/canonical/lxd/shared/api" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" ) -func (c *k8sd) KubeConfig(ctx context.Context, request apiv1.GetKubeConfigRequest) (string, error) { - var response apiv1.GetKubeConfigResponse - if err := c.client.Query(ctx, "GET", apiv1.K8sdAPIVersion, api.NewURL().Path("k8sd", "kubeconfig"), request, &response); err != nil { - return "", fmt.Errorf("failed to GET /k8sd/kubeconfig: %w", err) - } - return response.KubeConfig, nil +func (c *k8sd) KubeConfig(ctx context.Context, request apiv1.KubeConfigRequest) (apiv1.KubeConfigResponse, error) { + return query(ctx, c, "GET", apiv1.KubeConfigRPC, request, &apiv1.KubeConfigResponse{}) } diff --git a/src/k8s/pkg/k8sd/api/capi_auth.go b/src/k8s/pkg/k8sd/api/capi_auth.go index 4f8550ae6..7f45b5643 100644 --- a/src/k8s/pkg/k8sd/api/capi_auth.go +++ b/src/k8s/pkg/k8sd/api/capi_auth.go @@ -7,14 +7,14 @@ import ( "fmt" "net/http" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/database" "github.com/canonical/lxd/lxd/response" "github.com/canonical/microcluster/v2/state" ) func (e *Endpoints) postSetClusterAPIAuthToken(s state.State, r *http.Request) response.Response { - request := apiv1.SetClusterAPIAuthTokenRequest{} + request := apiv1.ClusterAPISetAuthTokenRequest{} if err := json.NewDecoder(r.Body).Decode(&request); err != nil { return response.BadRequest(fmt.Errorf("failed to parse request: %w", err)) } @@ -25,5 +25,5 @@ func (e *Endpoints) postSetClusterAPIAuthToken(s state.State, r *http.Request) r return response.InternalError(err) } - return response.SyncResponse(true, nil) + return response.SyncResponse(true, &apiv1.SetClusterConfigResponse{}) } diff --git a/src/k8s/pkg/k8sd/api/certificates_refresh.go b/src/k8s/pkg/k8sd/api/certificates_refresh.go index 3313cac46..f555171e5 100644 --- a/src/k8s/pkg/k8sd/api/certificates_refresh.go +++ b/src/k8s/pkg/k8sd/api/certificates_refresh.go @@ -9,7 +9,7 @@ import ( "net/http" "path/filepath" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" databaseutil "github.com/canonical/k8s/pkg/k8sd/database/util" "github.com/canonical/k8s/pkg/k8sd/pki" "github.com/canonical/k8s/pkg/k8sd/setup" @@ -37,7 +37,7 @@ func (e *Endpoints) postRefreshCertsPlan(s state.State, r *http.Request) respons if isWorker { return response.SyncResponse(true, apiv1.RefreshCertificatesPlanResponse{ Seed: seed, - CertificatesSigningRequests: []string{ + CertificateSigningRequests: []string{ fmt.Sprintf("k8sd-%d-worker-kubelet-serving", seed), fmt.Sprintf("k8sd-%d-worker-kubelet-client", seed), fmt.Sprintf("k8sd-%d-worker-kube-proxy-client", seed), diff --git a/src/k8s/pkg/k8sd/api/cluster.go b/src/k8s/pkg/k8sd/api/cluster.go index 67b4749d1..2ed64af84 100644 --- a/src/k8s/pkg/k8sd/api/cluster.go +++ b/src/k8s/pkg/k8sd/api/cluster.go @@ -6,7 +6,7 @@ import ( "fmt" "net/http" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/api/impl" "github.com/canonical/k8s/pkg/k8sd/database" databaseutil "github.com/canonical/k8s/pkg/k8sd/database/util" @@ -53,7 +53,7 @@ func (e *Endpoints) getClusterStatus(s state.State, r *http.Request) response.Re return response.InternalError(fmt.Errorf("database transaction failed: %w", err)) } - result := apiv1.GetClusterStatusResponse{ + return response.SyncResponse(true, &apiv1.ClusterStatusResponse{ ClusterStatus: apiv1.ClusterStatus{ Ready: ready, Members: members, @@ -70,7 +70,5 @@ func (e *Endpoints) getClusterStatus(s state.State, r *http.Request) response.Re MetricsServer: statuses[features.MetricsServer].ToAPI(), LocalStorage: statuses[features.LocalStorage].ToAPI(), }, - } - - return response.SyncResponse(true, &result) + }) } diff --git a/src/k8s/pkg/k8sd/api/cluster_bootstrap.go b/src/k8s/pkg/k8sd/api/cluster_bootstrap.go index 8ea12f59e..4a9c3304a 100644 --- a/src/k8s/pkg/k8sd/api/cluster_bootstrap.go +++ b/src/k8s/pkg/k8sd/api/cluster_bootstrap.go @@ -6,20 +6,20 @@ import ( "net/http" "time" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/utils" "github.com/canonical/lxd/lxd/response" "github.com/canonical/microcluster/v2/state" ) func (e *Endpoints) postClusterBootstrap(_ state.State, r *http.Request) response.Response { - req := apiv1.PostClusterBootstrapRequest{} + req := apiv1.BootstrapClusterRequest{} if err := utils.NewStrictJSONDecoder(r.Body).Decode(&req); err != nil { return response.BadRequest(fmt.Errorf("failed to parse request: %w", err)) } - //Convert Bootstrap config to map - config, err := req.Config.ToMicrocluster() + // Convert Bootstrap config to map + config, err := utils.MicroclusterMapWithBootstrapConfig(nil, req.Config) if err != nil { return response.BadRequest(fmt.Errorf("failed to prepare bootstrap config: %w", err)) } @@ -41,17 +41,15 @@ func (e *Endpoints) postClusterBootstrap(_ state.State, r *http.Request) respons defer cancel() // NOTE(neoaggelos): pass the timeout as a config option, so that the context cancel will propagate errors. - config = utils.MicroclusterConfigWithTimeout(config, req.Timeout) + config = utils.MicroclusterMapWithTimeout(config, req.Timeout) // Bootstrap the cluster if err := e.provider.MicroCluster().NewCluster(ctx, hostname, req.Address, config); err != nil { return response.BadRequest(fmt.Errorf("failed to bootstrap new cluster: %w", err)) } - result := apiv1.NodeStatus{ + return response.SyncResponse(true, &apiv1.BootstrapClusterResponse{ Name: hostname, Address: req.Address, - } - - return response.SyncResponse(true, &result) + }) } diff --git a/src/k8s/pkg/k8sd/api/cluster_config.go b/src/k8s/pkg/k8sd/api/cluster_config.go index f61f4a7c1..7bc01fcae 100644 --- a/src/k8s/pkg/k8sd/api/cluster_config.go +++ b/src/k8s/pkg/k8sd/api/cluster_config.go @@ -6,7 +6,7 @@ import ( "fmt" "net/http" - api "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/database" databaseutil "github.com/canonical/k8s/pkg/k8sd/database/util" "github.com/canonical/k8s/pkg/k8sd/types" @@ -16,7 +16,7 @@ import ( ) func (e *Endpoints) putClusterConfig(s state.State, r *http.Request) response.Response { - var req api.UpdateClusterConfigRequest + var req apiv1.SetClusterConfigRequest if err := utils.NewStrictJSONDecoder(r.Body).Decode(&req); err != nil { return response.BadRequest(fmt.Errorf("failed to decode request: %w", err)) @@ -50,7 +50,7 @@ func (e *Endpoints) putClusterConfig(s state.State, r *http.Request) response.Re !requestedConfig.DNS.Empty() || !requestedConfig.Kubelet.Empty(), ) - return response.SyncResponse(true, &api.UpdateClusterConfigResponse{}) + return response.SyncResponse(true, &apiv1.SetClusterConfigResponse{}) } func (e *Endpoints) getClusterConfig(s state.State, r *http.Request) response.Response { @@ -59,8 +59,7 @@ func (e *Endpoints) getClusterConfig(s state.State, r *http.Request) response.Re return response.InternalError(fmt.Errorf("failed to retrieve cluster configuration: %w", err)) } - result := api.GetClusterConfigResponse{ + return response.SyncResponse(true, &apiv1.GetClusterConfigResponse{ Config: config.ToUserFacing(), - } - return response.SyncResponse(true, &result) + }) } diff --git a/src/k8s/pkg/k8sd/api/cluster_join.go b/src/k8s/pkg/k8sd/api/cluster_join.go index 5abc2552b..03116dec4 100644 --- a/src/k8s/pkg/k8sd/api/cluster_join.go +++ b/src/k8s/pkg/k8sd/api/cluster_join.go @@ -6,7 +6,7 @@ import ( "net/http" "time" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/types" "github.com/canonical/k8s/pkg/utils" "github.com/canonical/lxd/lxd/response" @@ -35,25 +35,24 @@ func (e *Endpoints) postClusterJoin(s state.State, r *http.Request) response.Res defer cancel() // NOTE(neoaggelos): pass the timeout as a config option, so that the context cancel will propagate errors. - config = utils.MicroclusterConfigWithTimeout(config, req.Timeout) + config = utils.MicroclusterMapWithTimeout(config, req.Timeout) internalToken := types.InternalWorkerNodeToken{} // Check if token is worker token if internalToken.Decode(req.Token) == nil { // valid worker node token - let's join the cluster // The validation of the token is done when fetching the cluster information. - config["workerToken"] = req.Token - config["workerJoinConfig"] = req.Config + config = utils.MicroclusterMapWithWorkerJoinConfig(config, req.Token, req.Config) if err := e.provider.MicroCluster().NewCluster(ctx, hostname, req.Address, config); err != nil { return response.InternalError(fmt.Errorf("failed to join k8sd cluster as worker: %w", err)) } } else { // Is not a worker token. let microcluster check if it is a valid control-plane token. - config["controlPlaneJoinConfig"] = req.Config + config = utils.MicroclusterMapWithControlPlaneJoinConfig(config, req.Config) if err := e.provider.MicroCluster().JoinCluster(ctx, hostname, req.Address, req.Token, config); err != nil { return response.InternalError(fmt.Errorf("failed to join k8sd cluster as control plane: %w", err)) } } - return response.SyncResponse(true, nil) + return response.SyncResponse(true, &apiv1.JoinClusterResponse{}) } diff --git a/src/k8s/pkg/k8sd/api/cluster_remove.go b/src/k8s/pkg/k8sd/api/cluster_remove.go index 1979ea92f..19d6df39c 100644 --- a/src/k8s/pkg/k8sd/api/cluster_remove.go +++ b/src/k8s/pkg/k8sd/api/cluster_remove.go @@ -6,7 +6,7 @@ import ( "fmt" "net/http" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" databaseutil "github.com/canonical/k8s/pkg/k8sd/database/util" "github.com/canonical/k8s/pkg/log" "github.com/canonical/k8s/pkg/utils" @@ -70,7 +70,7 @@ func (e *Endpoints) postClusterRemove(s state.State, r *http.Request) response.R return response.InternalError(fmt.Errorf("failed to delete cluster member %s: %w", req.Name, err)) } - return response.SyncResponse(true, nil) + return response.SyncResponse(true, &apiv1.RemoveNodeResponse{}) } cfg, err := databaseutil.GetClusterConfig(ctx, s) @@ -98,5 +98,5 @@ func (e *Endpoints) postClusterRemove(s state.State, r *http.Request) response.R return response.InternalError(fmt.Errorf("failed to remove k8s node %q: %w", req.Name, err)) } - return response.SyncResponse(true, nil) + return response.SyncResponse(true, &apiv1.RemoveNodeResponse{}) } diff --git a/src/k8s/pkg/k8sd/api/cluster_tokens.go b/src/k8s/pkg/k8sd/api/cluster_tokens.go index 4d39863ad..a386c0315 100644 --- a/src/k8s/pkg/k8sd/api/cluster_tokens.go +++ b/src/k8s/pkg/k8sd/api/cluster_tokens.go @@ -7,7 +7,7 @@ import ( "net/http" "time" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/database" "github.com/canonical/k8s/pkg/k8sd/types" "github.com/canonical/k8s/pkg/utils" diff --git a/src/k8s/pkg/k8sd/api/endpoints.go b/src/k8s/pkg/k8sd/api/endpoints.go index 64cc69e6e..139ab164f 100644 --- a/src/k8s/pkg/k8sd/api/endpoints.go +++ b/src/k8s/pkg/k8sd/api/endpoints.go @@ -4,7 +4,7 @@ package api import ( "context" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/microcluster/v2/rest" ) @@ -45,7 +45,7 @@ func (e *Endpoints) Endpoints() []rest.Endpoint { // Cluster status and bootstrap { Name: "Cluster", - Path: "k8sd/cluster", + Path: apiv1.BootstrapClusterRPC, // == apiv1.ClusterStatusRPC Get: rest.EndpointAction{Handler: e.getClusterStatus, AccessHandler: e.restrictWorkers}, Post: rest.EndpointAction{Handler: e.postClusterBootstrap}, AllowedBeforeInit: true, @@ -54,33 +54,33 @@ func (e *Endpoints) Endpoints() []rest.Endpoint { // Returns the status (e.g. current role) of the local node (control-plane, worker or unknown). { Name: "NodeStatus", - Path: "k8sd/node", + Path: apiv1.NodeStatusRPC, Get: rest.EndpointAction{Handler: e.getNodeStatus}, }, // Clustering // Unified token endpoint for both, control-plane and worker-node. { - Name: "ClusterJoinTokens", - Path: "k8sd/cluster/tokens", + Name: "GetJoinToken", + Path: apiv1.GetJoinTokenRPC, Post: rest.EndpointAction{Handler: e.postClusterJoinTokens, AccessHandler: e.restrictWorkers}, }, { - Name: "ClusterJoin", - Path: "k8sd/cluster/join", + Name: "JoinCluster", + Path: apiv1.JoinClusterRPC, Post: rest.EndpointAction{Handler: e.postClusterJoin}, // Joining a node is a bootstrapping action which needs to be available before k8sd is initialized. AllowedBeforeInit: true, }, // Cluster removal (control-plane and worker nodes) { - Name: "ClusterRemove", - Path: "k8sd/cluster/remove", + Name: "RemoveNode", + Path: apiv1.RemoveNodeRPC, Post: rest.EndpointAction{Handler: e.postClusterRemove, AccessHandler: e.restrictWorkers}, }, // Worker nodes { - Name: "WorkerInfo", - Path: "k8sd/worker/info", + Name: "GetWorkerJoinInfo", + Path: apiv1.GetWorkerJoinInfoRPC, // AllowUntrusted disabled the microcluster authorization check. Authorization is done via custom token. Post: rest.EndpointAction{ Handler: e.postWorkerInfo, @@ -102,21 +102,20 @@ func (e *Endpoints) Endpoints() []rest.Endpoint { // Kubeconfig { Name: "Kubeconfig", - Path: "k8sd/kubeconfig", + Path: apiv1.KubeConfigRPC, Get: rest.EndpointAction{Handler: e.getKubeconfig, AccessHandler: e.restrictWorkers}, }, // Get and modify the cluster configuration (e.g. to enable/disable features) { Name: "ClusterConfig", - Path: "k8sd/cluster/config", + Path: apiv1.GetClusterConfigRPC, // == apiv1.SetClusterConfigRPC Put: rest.EndpointAction{Handler: e.putClusterConfig, AccessHandler: e.restrictWorkers}, Get: rest.EndpointAction{Handler: e.getClusterConfig, AccessHandler: e.restrictWorkers}, }, // Kubernetes auth tokens and token review webhook for kube-apiserver { Name: "KubernetesAuthTokens", - Path: "kubernetes/auth/tokens", - Get: rest.EndpointAction{Handler: e.getKubernetesAuthTokens, AllowUntrusted: true}, + Path: apiv1.GenerateKubernetesAuthTokenRPC, // == apiv1.RevokeKubernetesAuthTokenRPC Post: rest.EndpointAction{Handler: e.postKubernetesAuthTokens}, Delete: rest.EndpointAction{Handler: e.deleteKubernetesAuthTokens}, }, @@ -127,18 +126,18 @@ func (e *Endpoints) Endpoints() []rest.Endpoint { }, // ClusterAPI management endpoints. { - Name: "ClusterAPI/GenerateJoinToken", - Path: "x/capi/generate-join-token", + Name: "ClusterAPI/GetJoinToken", + Path: apiv1.ClusterAPIGetJoinTokenRPC, Post: rest.EndpointAction{Handler: e.postClusterJoinTokens, AccessHandler: ValidateCAPIAuthTokenAccessHandler("capi-auth-token"), AllowUntrusted: true}, }, { Name: "ClusterAPI/SetAuthToken", - Path: "x/capi/set-auth-token", + Path: apiv1.ClusterAPISetAuthTokenRPC, Post: rest.EndpointAction{Handler: e.postSetClusterAPIAuthToken}, }, { Name: "ClusterAPI/RemoveNode", - Path: "x/capi/remove-node", + Path: apiv1.ClusterAPIRemoveNodeRPC, Post: rest.EndpointAction{Handler: e.postClusterRemove, AccessHandler: ValidateCAPIAuthTokenAccessHandler("capi-auth-token"), AllowUntrusted: true}, }, } diff --git a/src/k8s/pkg/k8sd/api/impl/k8sd.go b/src/k8s/pkg/k8sd/api/impl/k8sd.go index 23d8c0914..a027f59d9 100644 --- a/src/k8s/pkg/k8sd/api/impl/k8sd.go +++ b/src/k8s/pkg/k8sd/api/impl/k8sd.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/snap" snaputil "github.com/canonical/k8s/pkg/snap/util" nodeutil "github.com/canonical/k8s/pkg/utils/node" diff --git a/src/k8s/pkg/k8sd/api/kubeconfig.go b/src/k8s/pkg/k8sd/api/kubeconfig.go index 607ac234a..bde13c37d 100644 --- a/src/k8s/pkg/k8sd/api/kubeconfig.go +++ b/src/k8s/pkg/k8sd/api/kubeconfig.go @@ -4,7 +4,7 @@ import ( "fmt" "net/http" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" databaseutil "github.com/canonical/k8s/pkg/k8sd/database/util" "github.com/canonical/k8s/pkg/k8sd/setup" "github.com/canonical/k8s/pkg/utils" @@ -13,7 +13,7 @@ import ( ) func (e *Endpoints) getKubeconfig(s state.State, r *http.Request) response.Response { - req := apiv1.GetKubeConfigRequest{} + req := apiv1.KubeConfigRequest{} if err := utils.NewStrictJSONDecoder(r.Body).Decode(&req); err != nil { return response.BadRequest(fmt.Errorf("failed to parse request: %w", err)) } @@ -32,8 +32,8 @@ func (e *Endpoints) getKubeconfig(s state.State, r *http.Request) response.Respo if err != nil { return response.InternalError(fmt.Errorf("failed to get kubeconfig: %w", err)) } - result := apiv1.GetKubeConfigResponse{ + + return response.SyncResponse(true, &apiv1.KubeConfigResponse{ KubeConfig: kubeconfig, - } - return response.SyncResponse(true, &result) + }) } diff --git a/src/k8s/pkg/k8sd/api/kubernetes_auth_tokens.go b/src/k8s/pkg/k8sd/api/kubernetes_auth_tokens.go index cbedb9966..16112207b 100644 --- a/src/k8s/pkg/k8sd/api/kubernetes_auth_tokens.go +++ b/src/k8s/pkg/k8sd/api/kubernetes_auth_tokens.go @@ -8,7 +8,7 @@ import ( "fmt" "net/http" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/database" databaseutil "github.com/canonical/k8s/pkg/k8sd/database/util" "github.com/canonical/k8s/pkg/utils" @@ -16,22 +16,6 @@ import ( "github.com/canonical/microcluster/v2/state" ) -func (e *Endpoints) getKubernetesAuthTokens(s state.State, r *http.Request) response.Response { - token := r.Header.Get("token") - - var username string - var groups []string - if err := s.Database().Transaction(r.Context(), func(ctx context.Context, tx *sql.Tx) error { - var err error - username, groups, err = database.CheckToken(ctx, tx, token) - return err - }); err != nil { - return response.NotFound(err) - } - - return response.SyncResponse(true, apiv1.CheckKubernetesAuthTokenResponse{Username: username, Groups: groups}) -} - func (e *Endpoints) postKubernetesAuthTokens(s state.State, r *http.Request) response.Response { request := apiv1.GenerateKubernetesAuthTokenRequest{} if err := json.NewDecoder(r.Body).Decode(&request); err != nil { @@ -43,7 +27,7 @@ func (e *Endpoints) postKubernetesAuthTokens(s state.State, r *http.Request) res return response.InternalError(err) } - return response.SyncResponse(true, apiv1.CreateKubernetesAuthTokenResponse{Token: token}) + return response.SyncResponse(true, apiv1.GenerateKubernetesAuthTokenResponse{Token: token}) } func (e *Endpoints) deleteKubernetesAuthTokens(s state.State, r *http.Request) response.Response { diff --git a/src/k8s/pkg/k8sd/api/node.go b/src/k8s/pkg/k8sd/api/node.go index 9f6d23004..27370723a 100644 --- a/src/k8s/pkg/k8sd/api/node.go +++ b/src/k8s/pkg/k8sd/api/node.go @@ -3,7 +3,7 @@ package api import ( "net/http" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/api/impl" "github.com/canonical/lxd/lxd/response" "github.com/canonical/microcluster/v2/state" @@ -17,9 +17,7 @@ func (e *Endpoints) getNodeStatus(s state.State, r *http.Request) response.Respo return response.InternalError(err) } - result := apiv1.GetNodeStatusResponse{ + return response.SyncResponse(true, &apiv1.NodeStatusResponse{ NodeStatus: status, - } - - return response.SyncResponse(true, &result) + }) } diff --git a/src/k8s/pkg/k8sd/api/worker.go b/src/k8s/pkg/k8sd/api/worker.go index d902e3ea3..0edf28221 100644 --- a/src/k8s/pkg/k8sd/api/worker.go +++ b/src/k8s/pkg/k8sd/api/worker.go @@ -7,7 +7,7 @@ import ( "net" "net/http" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/database" databaseutil "github.com/canonical/k8s/pkg/k8sd/database/util" "github.com/canonical/k8s/pkg/k8sd/pki" @@ -19,7 +19,7 @@ import ( func (e *Endpoints) postWorkerInfo(s state.State, r *http.Request) response.Response { snap := e.provider.Snap() - req := apiv1.WorkerNodeInfoRequest{} + req := apiv1.GetWorkerJoinInfoRequest{} if err := utils.NewStrictJSONDecoder(r.Body).Decode(&req); err != nil { return response.BadRequest(fmt.Errorf("failed to parse request: %w", err)) } @@ -65,7 +65,7 @@ func (e *Endpoints) postWorkerInfo(s state.State, r *http.Request) response.Resp return response.InternalError(fmt.Errorf("delete worker node token transaction failed: %w", err)) } - return response.SyncResponse(true, &apiv1.WorkerNodeInfoResponse{ + return response.SyncResponse(true, &apiv1.GetWorkerJoinInfoResponse{ CACert: cfg.Certificates.GetCACert(), ClientCACert: cfg.Certificates.GetClientCACert(), APIServers: servers, diff --git a/src/k8s/pkg/k8sd/app/app.go b/src/k8s/pkg/k8sd/app/app.go index 62a286027..944cda197 100644 --- a/src/k8s/pkg/k8sd/app/app.go +++ b/src/k8s/pkg/k8sd/app/app.go @@ -8,7 +8,7 @@ import ( "sync" "time" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/api" "github.com/canonical/k8s/pkg/k8sd/controllers" "github.com/canonical/k8s/pkg/k8sd/controllers/csrsigning" diff --git a/src/k8s/pkg/k8sd/app/hooks_bootstrap.go b/src/k8s/pkg/k8sd/app/hooks_bootstrap.go index 680df0d5b..026f60485 100644 --- a/src/k8s/pkg/k8sd/app/hooks_bootstrap.go +++ b/src/k8s/pkg/k8sd/app/hooks_bootstrap.go @@ -11,7 +11,7 @@ import ( "net/http" "path/filepath" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/database" "github.com/canonical/k8s/pkg/k8sd/pki" "github.com/canonical/k8s/pkg/k8sd/setup" @@ -30,20 +30,20 @@ func (a *App) onBootstrap(ctx context.Context, s state.State, initConfig map[str // NOTE(neoaggelos): context timeout is passed over configuration, so that hook failures are propagated to the client ctx, cancel := context.WithCancel(ctx) defer cancel() - if t := utils.MicroclusterTimeoutFromConfig(initConfig); t != 0 { + if t := utils.MicroclusterTimeoutFromMap(initConfig); t != 0 { ctx, cancel = context.WithTimeout(ctx, t) defer cancel() } if workerToken, ok := initConfig["workerToken"]; ok { - workerConfig, err := apiv1.WorkerJoinConfigFromMicrocluster(initConfig) + workerConfig, err := utils.MicroclusterWorkerJoinConfigFromMap(initConfig) if err != nil { return fmt.Errorf("failed to unmarshal worker join config: %w", err) } return a.onBootstrapWorkerNode(ctx, s, workerToken, workerConfig) } - bootstrapConfig, err := apiv1.BootstrapConfigFromMicrocluster(initConfig) + bootstrapConfig, err := utils.MicroclusterBootstrapConfigFromMap(initConfig) if err != nil { return fmt.Errorf("failed to unmarshal bootstrap config: %w", err) } @@ -51,7 +51,7 @@ func (a *App) onBootstrap(ctx context.Context, s state.State, initConfig map[str return a.onBootstrapControlPlane(ctx, s, bootstrapConfig) } -func (a *App) onBootstrapWorkerNode(ctx context.Context, s state.State, encodedToken string, joinConfig apiv1.WorkerNodeJoinConfig) (rerr error) { +func (a *App) onBootstrapWorkerNode(ctx context.Context, s state.State, encodedToken string, joinConfig apiv1.WorkerJoinConfig) (rerr error) { snap := a.Snap() log := log.FromContext(ctx).WithValues("hook", "join") @@ -104,11 +104,11 @@ func (a *App) onBootstrapWorkerNode(ctx context.Context, s state.State, encodedT } type wrappedResponse struct { - Error string `json:"error"` - Metadata apiv1.WorkerNodeInfoResponse `json:"metadata"` + Error string `json:"error"` + Metadata apiv1.GetWorkerJoinInfoResponse `json:"metadata"` } - requestBody, err := json.Marshal(apiv1.WorkerNodeInfoRequest{Address: nodeIP.String()}) + requestBody, err := json.Marshal(apiv1.GetWorkerJoinInfoRequest{Address: nodeIP.String()}) if err != nil { return fmt.Errorf("failed to prepare worker info request: %w", err) } diff --git a/src/k8s/pkg/k8sd/app/hooks_join.go b/src/k8s/pkg/k8sd/app/hooks_join.go index 0a9e0af01..795f72103 100644 --- a/src/k8s/pkg/k8sd/app/hooks_join.go +++ b/src/k8s/pkg/k8sd/app/hooks_join.go @@ -5,7 +5,6 @@ import ( "fmt" "net" - apiv1 "github.com/canonical/k8s/api/v1" databaseutil "github.com/canonical/k8s/pkg/k8sd/database/util" "github.com/canonical/k8s/pkg/k8sd/pki" "github.com/canonical/k8s/pkg/k8sd/setup" @@ -22,12 +21,12 @@ func (a *App) onPostJoin(ctx context.Context, s state.State, initConfig map[stri // NOTE(neoaggelos): context timeout is passed over configuration, so that hook failures are propagated to the client ctx, cancel := context.WithCancel(ctx) defer cancel() - if t := utils.MicroclusterTimeoutFromConfig(initConfig); t != 0 { + if t := utils.MicroclusterTimeoutFromMap(initConfig); t != 0 { ctx, cancel = context.WithTimeout(ctx, t) defer cancel() } - joinConfig, err := apiv1.ControlPlaneJoinConfigFromMicrocluster(initConfig) + joinConfig, err := utils.MicroclusterControlPlaneJoinConfigFromMap(initConfig) if err != nil { return fmt.Errorf("failed to unmarshal control plane join config: %w", err) } diff --git a/src/k8s/pkg/k8sd/app/hooks_remove.go b/src/k8s/pkg/k8sd/app/hooks_remove.go index 7b28bdcb8..49cc836db 100644 --- a/src/k8s/pkg/k8sd/app/hooks_remove.go +++ b/src/k8s/pkg/k8sd/app/hooks_remove.go @@ -7,7 +7,7 @@ import ( "net" "os" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" databaseutil "github.com/canonical/k8s/pkg/k8sd/database/util" "github.com/canonical/k8s/pkg/k8sd/pki" "github.com/canonical/k8s/pkg/k8sd/setup" diff --git a/src/k8s/pkg/k8sd/types/cluster_config_convert.go b/src/k8s/pkg/k8sd/types/cluster_config_convert.go index 050b15481..f254d3c5d 100644 --- a/src/k8s/pkg/k8sd/types/cluster_config_convert.go +++ b/src/k8s/pkg/k8sd/types/cluster_config_convert.go @@ -3,7 +3,7 @@ package types import ( "fmt" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/utils" ) diff --git a/src/k8s/pkg/k8sd/types/cluster_config_convert_loadbalancer_internal_test.go b/src/k8s/pkg/k8sd/types/cluster_config_convert_loadbalancer_internal_test.go index f2ccda154..debaa6d20 100644 --- a/src/k8s/pkg/k8sd/types/cluster_config_convert_loadbalancer_internal_test.go +++ b/src/k8s/pkg/k8sd/types/cluster_config_convert_loadbalancer_internal_test.go @@ -1,9 +1,9 @@ package types import ( - "github.com/canonical/k8s/pkg/utils" "testing" + "github.com/canonical/k8s/pkg/utils" . "github.com/onsi/gomega" ) diff --git a/src/k8s/pkg/k8sd/types/cluster_config_convert_test.go b/src/k8s/pkg/k8sd/types/cluster_config_convert_test.go index 26bb7c2ae..194eedc80 100644 --- a/src/k8s/pkg/k8sd/types/cluster_config_convert_test.go +++ b/src/k8s/pkg/k8sd/types/cluster_config_convert_test.go @@ -3,7 +3,7 @@ package types_test import ( "testing" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/types" "github.com/canonical/k8s/pkg/utils" . "github.com/onsi/gomega" diff --git a/src/k8s/pkg/k8sd/types/cluster_config_datastore_convert.go b/src/k8s/pkg/k8sd/types/cluster_config_datastore_convert.go index f425e9a5c..d0a99bab0 100644 --- a/src/k8s/pkg/k8sd/types/cluster_config_datastore_convert.go +++ b/src/k8s/pkg/k8sd/types/cluster_config_datastore_convert.go @@ -3,7 +3,7 @@ package types import ( "fmt" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" ) // DatastoreConfigFromUserFacing converts UserFacingDatastoreConfig from public API into a Datastore config. diff --git a/src/k8s/pkg/k8sd/types/cluster_config_datastore_convert_test.go b/src/k8s/pkg/k8sd/types/cluster_config_datastore_convert_test.go index 7268befaa..d2f5dbc0b 100644 --- a/src/k8s/pkg/k8sd/types/cluster_config_datastore_convert_test.go +++ b/src/k8s/pkg/k8sd/types/cluster_config_datastore_convert_test.go @@ -1,11 +1,11 @@ package types_test import ( - "github.com/canonical/k8s/pkg/utils" "testing" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/types" + "github.com/canonical/k8s/pkg/utils" . "github.com/onsi/gomega" ) diff --git a/src/k8s/pkg/k8sd/types/cluster_config_datastore_test.go b/src/k8s/pkg/k8sd/types/cluster_config_datastore_test.go index 42d8f257b..33c3e2949 100644 --- a/src/k8s/pkg/k8sd/types/cluster_config_datastore_test.go +++ b/src/k8s/pkg/k8sd/types/cluster_config_datastore_test.go @@ -1,11 +1,11 @@ package types_test import ( - "github.com/canonical/k8s/pkg/utils" "testing" "github.com/canonical/k8s/pkg/k8sd/types" "github.com/canonical/k8s/pkg/snap/mock" + "github.com/canonical/k8s/pkg/utils" . "github.com/onsi/gomega" ) diff --git a/src/k8s/pkg/k8sd/types/cluster_config_merge_test.go b/src/k8s/pkg/k8sd/types/cluster_config_merge_test.go index 4cdfe77e7..e77f44fcb 100644 --- a/src/k8s/pkg/k8sd/types/cluster_config_merge_test.go +++ b/src/k8s/pkg/k8sd/types/cluster_config_merge_test.go @@ -4,9 +4,8 @@ import ( "fmt" "testing" - "github.com/canonical/k8s/pkg/utils" - "github.com/canonical/k8s/pkg/k8sd/types" + "github.com/canonical/k8s/pkg/utils" . "github.com/onsi/gomega" ) diff --git a/src/k8s/pkg/k8sd/types/cluster_config_validate_test.go b/src/k8s/pkg/k8sd/types/cluster_config_validate_test.go index 681f79b1b..db58934ed 100644 --- a/src/k8s/pkg/k8sd/types/cluster_config_validate_test.go +++ b/src/k8s/pkg/k8sd/types/cluster_config_validate_test.go @@ -1,10 +1,10 @@ package types_test import ( - "github.com/canonical/k8s/pkg/utils" "testing" "github.com/canonical/k8s/pkg/k8sd/types" + "github.com/canonical/k8s/pkg/utils" . "github.com/onsi/gomega" ) diff --git a/src/k8s/pkg/k8sd/types/feature_status.go b/src/k8s/pkg/k8sd/types/feature_status.go index 162184527..86f5aa32d 100644 --- a/src/k8s/pkg/k8sd/types/feature_status.go +++ b/src/k8s/pkg/k8sd/types/feature_status.go @@ -3,7 +3,7 @@ package types import ( "time" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" ) // FeatureStatus encapsulates the deployment status of a feature. diff --git a/src/k8s/pkg/k8sd/types/feature_status_test.go b/src/k8s/pkg/k8sd/types/feature_status_test.go index ab6d2bc4c..ef124eec4 100644 --- a/src/k8s/pkg/k8sd/types/feature_status_test.go +++ b/src/k8s/pkg/k8sd/types/feature_status_test.go @@ -4,10 +4,9 @@ import ( "testing" "time" - . "github.com/onsi/gomega" - - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/types" + . "github.com/onsi/gomega" ) func TestK8sdFeatureStatusToAPI(t *testing.T) { 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 ce22c71ff..51584957e 100644 --- a/src/k8s/pkg/utils/experimental/snapdconfig/k8s_to_snapd.go +++ b/src/k8s/pkg/utils/experimental/snapdconfig/k8s_to_snapd.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/k8sd/types" "github.com/canonical/k8s/pkg/snap" 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 2cec720f4..00ca2aee1 100644 --- a/src/k8s/pkg/utils/experimental/snapdconfig/snapd_to_k8s.go +++ b/src/k8s/pkg/utils/experimental/snapdconfig/snapd_to_k8s.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/k8s/pkg/client/k8sd" "github.com/canonical/k8s/pkg/k8sd/features" "github.com/canonical/k8s/pkg/snap" @@ -31,7 +31,7 @@ func SetK8sdFromSnapd(ctx context.Context, client k8sd.Client, snap snap.Snap) e return fmt.Errorf("failed to parse snapd configuration: %w", err) } - if err := client.SetClusterConfig(ctx, apiv1.UpdateClusterConfigRequest{Config: config}); err != nil { + if err := client.SetClusterConfig(ctx, apiv1.SetClusterConfigRequest{Config: config}); err != nil { return fmt.Errorf("failed to update k8s configuration: %w", err) } diff --git a/src/k8s/pkg/utils/microcluster.go b/src/k8s/pkg/utils/microcluster.go index 7eadcfb70..00d18ff31 100644 --- a/src/k8s/pkg/utils/microcluster.go +++ b/src/k8s/pkg/utils/microcluster.go @@ -1,22 +1,31 @@ package utils -import "time" +import ( + "encoding/json" + "fmt" + "time" -// MicroclusterConfigWithTimeout adds a "timeout" configuration value to the config struct. + apiv1 "github.com/canonical/k8s-snap-api/api/v1" + "gopkg.in/yaml.v2" +) + +// MicroclusterMapWithTimeout adds a "timeout" configuration value to the config struct. // If timeout is zero, the configuration is not affected. -func MicroclusterConfigWithTimeout(config map[string]string, timeout time.Duration) map[string]string { +func MicroclusterMapWithTimeout(m map[string]string, timeout time.Duration) map[string]string { if timeout == 0 { - return config + return m } - - config["_timeout"] = timeout.String() - return config + if m == nil { + m = make(map[string]string) + } + m["_timeout"] = timeout.String() + return m } -// MicroclusterTimeoutFromConfig returns the configured timeout option from the config struct. -// If case of an invalid or empty value, 0 is returned. -func MicroclusterTimeoutFromConfig(config map[string]string) time.Duration { - if v, ok := config["_timeout"]; !ok { +// MicroclusterTimeoutFromMap returns the configured timeout option from the config struct. +// In case of an invalid or empty value, 0 is returned. +func MicroclusterTimeoutFromMap(m map[string]string) time.Duration { + if v, ok := m["_timeout"]; !ok { return 0 } else if d, err := time.ParseDuration(v); err != nil { return 0 @@ -24,3 +33,62 @@ func MicroclusterTimeoutFromConfig(config map[string]string) time.Duration { return d } } + +// MicroclusterConfigWithBootstrap adds apiv1.BootstrapConfig to the config struct. +func MicroclusterMapWithBootstrapConfig(m map[string]string, bootstrap apiv1.BootstrapConfig) (map[string]string, error) { + b, err := json.Marshal(bootstrap) + if err != nil { + return m, fmt.Errorf("failed to marshal bootstrap config: %w", err) + } + if m == nil { + m = make(map[string]string) + } + m["bootstrapConfig"] = string(b) + return m, nil +} + +// MicroclusterBootstrapConfigFromMap returns an apiv1.BootstrapConfig from the config struct. +func MicroclusterBootstrapConfigFromMap(m map[string]string) (apiv1.BootstrapConfig, error) { + var config apiv1.BootstrapConfig + if err := json.Unmarshal([]byte(m["bootstrapConfig"]), &config); err != nil { + return apiv1.BootstrapConfig{}, fmt.Errorf("failed to unmarshal bootstrap config: %w", err) + } + return config, nil +} + +// MicroclusterMapWithControlPlaneJoinConfig adds (a JSON formatted) apiv1.ControlPlaneJoinConfig to the config struct. +func MicroclusterMapWithControlPlaneJoinConfig(m map[string]string, controlPlaneJoinConfigJSON string) map[string]string { + if m == nil { + m = make(map[string]string) + } + m["controlPlaneJoinConfig"] = controlPlaneJoinConfigJSON + return m +} + +// MicroclusterControlPlaneJoinConfigFromMap returns an apiv1.ControlPlaneJoinConfig from the config struct. +func MicroclusterControlPlaneJoinConfigFromMap(m map[string]string) (apiv1.ControlPlaneJoinConfig, error) { + var config apiv1.ControlPlaneJoinConfig + if err := yaml.UnmarshalStrict([]byte(m["controlPlaneJoinConfig"]), &config); err != nil { + return apiv1.ControlPlaneJoinConfig{}, fmt.Errorf("failed to unmarshal control plane join config: %w", err) + } + return config, nil +} + +// MicroclusterMapWithWorkerJoinConfig adds (a JSON formatted) apiv1.WorkerJoinConfig to the config struct. +func MicroclusterMapWithWorkerJoinConfig(m map[string]string, token string, workerJoinConfigJSON string) map[string]string { + if m == nil { + m = make(map[string]string) + } + m["workerToken"] = token + m["workerJoinConfig"] = workerJoinConfigJSON + return m +} + +// MicroclusterWorkerJoinConfigFromMap returns an apiv1.WorkerJoinConfig from the config struct. +func MicroclusterWorkerJoinConfigFromMap(m map[string]string) (apiv1.WorkerJoinConfig, error) { + var config apiv1.WorkerJoinConfig + if err := yaml.UnmarshalStrict([]byte(m["workerJoinConfig"]), &config); err != nil { + return apiv1.WorkerJoinConfig{}, fmt.Errorf("failed to unmarshal worker join config: %w", err) + } + return config, nil +} diff --git a/src/k8s/pkg/utils/microcluster_test.go b/src/k8s/pkg/utils/microcluster_test.go index 5d819d709..f3a587c7d 100644 --- a/src/k8s/pkg/utils/microcluster_test.go +++ b/src/k8s/pkg/utils/microcluster_test.go @@ -13,7 +13,7 @@ func TestMicroclusterTimeout(t *testing.T) { g := NewWithT(t) m := map[string]string{} - g.Expect(utils.MicroclusterTimeoutFromConfig(m)).To(BeZero()) + g.Expect(utils.MicroclusterTimeoutFromMap(m)).To(BeZero()) }) t.Run("Normal", func(t *testing.T) { @@ -22,7 +22,7 @@ func TestMicroclusterTimeout(t *testing.T) { timeout := 5 * time.Second m := map[string]string{} - mWithTimeout := utils.MicroclusterConfigWithTimeout(m, timeout) - g.Expect(utils.MicroclusterTimeoutFromConfig(mWithTimeout)).To(Equal(timeout)) + mWithTimeout := utils.MicroclusterMapWithTimeout(m, timeout) + g.Expect(utils.MicroclusterTimeoutFromMap(mWithTimeout)).To(Equal(timeout)) }) } diff --git a/src/k8s/pkg/utils/node/node.go b/src/k8s/pkg/utils/node/node.go index ff26b3749..de0da373e 100644 --- a/src/k8s/pkg/utils/node/node.go +++ b/src/k8s/pkg/utils/node/node.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - apiv1 "github.com/canonical/k8s/api/v1" + apiv1 "github.com/canonical/k8s-snap-api/api/v1" "github.com/canonical/microcluster/v2/state" )