Skip to content

Commit

Permalink
Address code review
Browse files Browse the repository at this point in the history
  • Loading branch information
mateoflorido committed Apr 8, 2024
1 parent 971c46c commit b7ae955
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 99 deletions.
38 changes: 27 additions & 11 deletions src/k8s/api/v1/bootstrap_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,41 @@ type BootstrapConfig struct {
// Seed configuration for certificates
ExtraSANs []string `json:"extra-sans,omitempty" yaml:"extra-sans,omitempty"`

CACert string `yaml:"ca-crt,omitempty"`
CAKey string `yaml:"ca-key,omitempty"`
FrontProxyCACert string `yaml:"front-proxy-ca-crt"`
FrontProxyCAKey string `yaml:"front-proxy-ca-key"`
APIServerKubeletClientCert string `yaml:"apiserver-kubelet-client-crt"`
APIServerKubeletClientKey string `yaml:"apiserver-kubelet-client-key"`
ServiceAccountKey string `yaml:"service-account-key"`
// Seed configuration for external certificates
CACert *string `json:"ca-crt,omitempty" yaml:"ca-crt,omitempty"`
CAKey *string `json:"ca-key,omitempty" yaml:"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"`
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"`
ServiceAccountKey *string `json:"service-account-key,omitempty" yaml:"service-account-key,omitempty"`

APIServerCert string `yaml:"apiserver-crt,omitempty"`
APIServerKey string `yaml:"apiserver-key,omitempty"`
KubeletCert string `yaml:"kubelet-crt,omitempty"`
KubeletKey string `yaml:"kubelet-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"`
}

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) GetFrontProxyCACert() string { return getField(b.FrontProxyCACert) }
func (b *BootstrapConfig) GetFrontProxyCAKey() string { return getField(b.FrontProxyCAKey) }
func (b *BootstrapConfig) GetAPIServerKubeletClientCert() string {
return getField(b.APIServerKubeletClientCert)
}
func (b *BootstrapConfig) GetAPIServerKubeletClientKey() string {
return getField(b.APIServerKubeletClientKey)
}
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) }

// ToMicrocluster converts a BootstrapConfig to a map[string]string for use in microcluster.
func (b *BootstrapConfig) ToMicrocluster() (map[string]string, error) {
Expand Down
8 changes: 4 additions & 4 deletions src/k8s/api/v1/cluster_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package v1

// 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 JoinClusterConfig `json:"config"`
Name string `json:"name"`
Address string `json:"address"`
Token string `json:"token"`
Config ControlPlaneNodeJoinConfig `json:"config"`
}

// RemoveNodeRequest is used to request to remove a node from the cluster.
Expand Down
27 changes: 27 additions & 0 deletions src/k8s/api/v1/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package v1

import (
"encoding/json"
"fmt"
)

type MicroclusterConfig interface {
ToMicrocluster() (map[string]string, error)
}

// ToMicrocluster implements the conversion to Microcluster for any MicroclusterConfig.
func ToMicrocluster(m MicroclusterConfig, key string) (map[string]string, error) {
config, err := json.Marshal(m)
if err != nil {
return nil, fmt.Errorf("Failed to marshal config %s: %w", key, err)
}
return map[string]string{key: string(config)}, nil
}

// ConfigFromMicrocluster parses a Microcluster map[string]string and retrieves the Config structure based on the provided MicroclusterConfig type.
func ConfigFromMicrocluster(m map[string]string, key string, target MicroclusterConfig) error {
if err := json.Unmarshal([]byte(m[key]), target); err != nil {
return fmt.Errorf("failed to unmarshal %s: %w", key, err)
}
return nil
}
31 changes: 31 additions & 0 deletions src/k8s/api/v1/join_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package v1

type ControlPlaneNodeJoinConfig struct {
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"`
}

type WorkerNodeJoinConfig struct {
KubeletCert *string `json:"kubelet-crt,omitempty" yaml:"kubelet-crt,omitempty"`
KubeletKey *string `json:"kubelet-key,omitempty" yaml:"kubelet-key,omitempty"`
}

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 (w *WorkerNodeJoinConfig) GetKubeletCert() string { return getField(w.KubeletCert) }
func (w *WorkerNodeJoinConfig) GetKubeletKey() string { return getField(w.KubeletKey) }

// ToMicrocluster converts a BootstrapConfig to a map[string]string for use in microcluster.
func (j *ControlPlaneNodeJoinConfig) ToMicrocluster() (map[string]string, error) {
return ToMicrocluster(j, "joinClusterConfig")
}

// ToMicrocluster converts a BootstrapConfig to a map[string]string for use in microcluster.
func (w *WorkerNodeJoinConfig) ToMicrocluster() (map[string]string, error) {
return ToMicrocluster(w, "joinClusterConfig")
}
26 changes: 0 additions & 26 deletions src/k8s/api/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,6 @@ import (
"gopkg.in/yaml.v2"
)

type JoinClusterConfig struct {
APIServerCert string `yaml:"apiserver-crt,omitempty"`
APIServerKey string `yaml:"apiserver-key,omitempty"`
KubeletCert string `yaml:"kubelet-crt,omitempty"`
KubeletKey string `yaml:"kubelet-key,omitempty"`
}

func (j *JoinClusterConfig) ToMap() (map[string]string, error) {
config, err := yaml.Marshal(j)
if err != nil {
return nil, fmt.Errorf("failed to marshal config map: %w", err)
}
return map[string]string{
"joinClusterConfig": string(config),
}, nil
}

func JoinClusterConfigFromMap(m map[string]string) (*JoinClusterConfig, error) {
config := &JoinClusterConfig{}
err := yaml.Unmarshal([]byte(m["joinClusterConfig"]), config)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal join config: %w", err)
}
return config, nil
}

type ClusterRole string

const (
Expand Down
20 changes: 9 additions & 11 deletions src/k8s/cmd/k8s/k8s_join_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ func newJoinClusterCmd(env cmdutil.ExecutionEnvironment) *cobra.Command {
return
}

joinClusterConfig := apiv1.JoinClusterConfig{}
joinClusterConfig := apiv1.ControlPlaneNodeJoinConfig{}
if opts.configFile != "" {
joinClusterConfig, err = getJoinClusterConfigFromYaml(opts.configFile)
joinClusterConfig, err = getJoinConfigFromYaml(opts.configFile)
if err != nil {
cmd.PrintErrf("Error: Failed to read join configuration from %q.\n\nThe error was: %v\n", opts.configFile, err)
env.Exit(1)
Expand All @@ -88,21 +88,19 @@ func newJoinClusterCmd(env cmdutil.ExecutionEnvironment) *cobra.Command {
}
cmd.Flags().StringVar(&opts.name, "name", "", "node name, defaults to hostname")
cmd.Flags().StringVar(&opts.address, "address", "", "microcluster address, defaults to the node IP address")
cmd.PersistentFlags().StringVar(&opts.configFile, "config", "", "path to the YAML file containing your custom cluster join configuration")
cmd.PersistentFlags().StringVar(&opts.configFile, "file", "", "path to the YAML file containing your custom cluster join configuration")
return cmd
}

func getJoinClusterConfigFromYaml(filePath string) (apiv1.JoinClusterConfig, error) {
config := apiv1.JoinClusterConfig{}

yamlContent, err := os.ReadFile(filePath)
func getJoinConfigFromYaml(filePath string) (apiv1.ControlPlaneNodeJoinConfig, error) {
b, err := os.ReadFile(filePath)
if err != nil {
return config, fmt.Errorf("failed to read YAML config file: %w", err)
return apiv1.ControlPlaneNodeJoinConfig{}, fmt.Errorf("failed to read file: %w", err)
}

err = yaml.Unmarshal(yamlContent, &config)
if err != nil {
return config, fmt.Errorf("failed to parse YAML config file: %w", err)
var config apiv1.ControlPlaneNodeJoinConfig
if err := yaml.UnmarshalStrict(b, &config); err != nil {
return apiv1.ControlPlaneNodeJoinConfig{}, fmt.Errorf("failed to parse YAML config file: %w", err)
}

return config, nil
Expand Down
2 changes: 1 addition & 1 deletion src/k8s/pkg/k8sd/api/cluster_join.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (e *Endpoints) postClusterJoin(s *state.State, r *http.Request) response.Re
return response.BadRequest(fmt.Errorf("failed to parse request: %w", err))
}

config, err := req.Config.ToMap()
config, err := req.Config.ToMicrocluster()
if err != nil {
return response.BadRequest(fmt.Errorf("failed to convert join config to map: %w", err))
}
Expand Down
58 changes: 30 additions & 28 deletions src/k8s/pkg/k8sd/app/hooks_bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,30 @@ import (
// onBootstrap configures local services then writes the cluster config on the database.
func (a *App) onBootstrap(s *state.State, initConfig map[string]string) error {
if workerToken, ok := initConfig["workerToken"]; ok {
return a.onBootstrapWorkerNode(s, workerToken, initConfig)
var workerConfig apiv1.WorkerNodeJoinConfig
err := apiv1.ConfigFromMicrocluster(initConfig, "joinClusterConfig", &workerConfig)
if err != nil {
return fmt.Errorf("failed to unmarshal worker join config: %w", err)
}
return a.onBootstrapWorkerNode(s, workerToken, workerConfig)
}

bootstrapConfig, err := apiv1.BootstrapConfigFromMicrocluster(initConfig)
if err != nil {
return fmt.Errorf("failed to unmarshal bootstrap config: %w", err)
}

return a.onBootstrapControlPlane(s, initConfig)
return a.onBootstrapControlPlane(s, bootstrapConfig)
}

func (a *App) onBootstrapWorkerNode(s *state.State, encodedToken string, initConfig map[string]string) error {
func (a *App) onBootstrapWorkerNode(s *state.State, encodedToken string, joinConfig apiv1.WorkerNodeJoinConfig) error {
snap := a.Snap()

token := &types.InternalWorkerNodeToken{}
if err := token.Decode(encodedToken); err != nil {
return fmt.Errorf("failed to parse worker token: %w", err)
}

joinClusterConfig, err := apiv1.JoinClusterConfigFromMap(initConfig)
if err != nil {
return fmt.Errorf("failed to unmarshal join config: %w", err)
}

if len(token.JoinAddresses) == 0 {
return fmt.Errorf("empty list of control plane addresses")
}
Expand Down Expand Up @@ -108,9 +113,11 @@ func (a *App) onBootstrapWorkerNode(s *state.State, encodedToken string, initCon
KubeletKey: response.KubeletKey,
}

if !certificates.AreKubeletCertsSet() {
certificates.KubeletCert = joinClusterConfig.KubeletCert
certificates.KubeletKey = joinClusterConfig.KubeletKey
if v := joinConfig.GetKubeletCert(); v != "" {
certificates.KubeletCert = v
}
if v := joinConfig.GetKubeletKey(); v != "" {
certificates.KubeletKey = v
}

if err := certificates.CompleteCertificates(); err != nil {
Expand Down Expand Up @@ -155,14 +162,9 @@ func (a *App) onBootstrapWorkerNode(s *state.State, encodedToken string, initCon
return nil
}

func (a *App) onBootstrapControlPlane(s *state.State, initConfig map[string]string) error {
func (a *App) onBootstrapControlPlane(s *state.State, bootstrapConfig apiv1.BootstrapConfig) error {
snap := a.Snap()

bootstrapConfig, err := apiv1.BootstrapConfigFromMicrocluster(initConfig)
if err != nil {
return fmt.Errorf("failed to unmarshal bootstrap config: %w", err)
}

cfg, err := types.ClusterConfigFromBootstrapConfig(bootstrapConfig)
if err != nil {
return fmt.Errorf("invalid bootstrap config: %w", err)
Expand Down Expand Up @@ -235,21 +237,21 @@ func (a *App) onBootstrapControlPlane(s *state.State, initConfig map[string]stri
IncludeMachineAddressSANs: true,
})

certificates.CACert = bootstrapConfig.CACert
certificates.CAKey = bootstrapConfig.CAKey
certificates.CACert = bootstrapConfig.GetCACert()
certificates.CAKey = bootstrapConfig.GetCAKey()

certificates.FrontProxyCACert = bootstrapConfig.FrontProxyCACert
certificates.FrontProxyCAKey = bootstrapConfig.FrontProxyCAKey
certificates.FrontProxyCACert = bootstrapConfig.GetFrontProxyCACert()
certificates.FrontProxyCAKey = bootstrapConfig.GetFrontProxyCAKey()

certificates.ServiceAccountKey = bootstrapConfig.ServiceAccountKey
certificates.ServiceAccountKey = bootstrapConfig.GetServiceAccountKey()

certificates.APIServerKubeletClientCert = bootstrapConfig.APIServerKubeletClientCert
certificates.APIServerKubeletClientKey = bootstrapConfig.APIServerKubeletClientKey
certificates.APIServerKubeletClientCert = bootstrapConfig.GetAPIServerKubeletClientCert()
certificates.APIServerKubeletClientKey = bootstrapConfig.GetAPIServerKubeletClientKey()

certificates.APIServerCert = bootstrapConfig.APIServerCert
certificates.APIServerKey = bootstrapConfig.APIServerKey
certificates.KubeletCert = bootstrapConfig.KubeletCert
certificates.KubeletKey = bootstrapConfig.KubeletKey
certificates.APIServerCert = bootstrapConfig.GetAPIServerCert()
certificates.APIServerKey = bootstrapConfig.GetAPIServerKey()
certificates.KubeletCert = bootstrapConfig.GetKubeletCert()
certificates.KubeletKey = bootstrapConfig.GetKubeletKey()

if err := certificates.CompleteCertificates(); err != nil {
return fmt.Errorf("failed to initialize control plane certificates: %w", err)
Expand Down
11 changes: 6 additions & 5 deletions src/k8s/pkg/k8sd/app/hooks_join.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import (
func (a *App) onPostJoin(s *state.State, initConfig map[string]string) error {
snap := a.Snap()

joinClusterConfig, err := apiv1.JoinClusterConfigFromMap(initConfig)
var joinClusterConfig apiv1.ControlPlaneNodeJoinConfig
err := apiv1.ConfigFromMicrocluster(initConfig, "joinClusterConfig", &joinClusterConfig)
if err != nil {
return fmt.Errorf("failed to unmarshal join config: %w", err)
}
Expand Down Expand Up @@ -92,10 +93,10 @@ func (a *App) onPostJoin(s *state.State, initConfig map[string]string) error {
certificates.ServiceAccountKey = cfg.Certificates.GetServiceAccountKey()

// load certificates from joinClusterConfig
certificates.APIServerCert = joinClusterConfig.APIServerCert
certificates.APIServerKey = joinClusterConfig.APIServerKey
certificates.KubeletCert = joinClusterConfig.KubeletCert
certificates.KubeletKey = joinClusterConfig.KubeletKey
certificates.APIServerCert = joinClusterConfig.GetAPIServerCert()
certificates.APIServerKey = joinClusterConfig.GetAPIServerKey()
certificates.KubeletCert = joinClusterConfig.GetKubeletCert()
certificates.KubeletKey = joinClusterConfig.GetKubeletKey()

if err := certificates.CompleteCertificates(); err != nil {
return fmt.Errorf("failed to initialize control plane certificates: %w", err)
Expand Down
4 changes: 0 additions & 4 deletions src/k8s/pkg/k8sd/pki/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,3 @@ func (c *WorkerNodePKI) CompleteCertificates() error {
}
return nil
}

func (c *WorkerNodePKI) AreKubeletCertsSet() bool {
return c.KubeletCert != "" && c.KubeletKey != ""
}
10 changes: 1 addition & 9 deletions src/k8s/pkg/k8sd/types/cluster_config_certificates.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package types

import "reflect"

type Certificates struct {
CACert *string `json:"ca-crt,omitempty"`
CAKey *string `json:"ca-key,omitempty"`
Expand All @@ -26,11 +24,5 @@ func (c Certificates) GetAPIServerKubeletClientKey() string {

// Check if every field of the Certificate struct is Nil, returns false otherwise.
func (c Certificates) Empty() bool {
val := reflect.ValueOf(c)
for i := 0; i < val.NumField(); i++ {
if val.Field(i).Kind() == reflect.Pointer && !val.Field(i).IsNil() {
return false
}
}
return true
return c.CACert == nil && c.CAKey == nil && c.FrontProxyCACert == nil && c.FrontProxyCAKey == nil && c.ServiceAccountKey == nil && c.APIServerKubeletClientCert == nil && c.APIServerKubeletClientKey == nil
}

0 comments on commit b7ae955

Please sign in to comment.