Skip to content

Commit

Permalink
Merge pull request 'Increasing worker node count to 2 in the generate…
Browse files Browse the repository at this point in the history
…d AWS sample config file | Added support for MachinePool labels and taints' (#12) from increase-root-ebs-volume-size into main

Reviewed-on: https://gitea.obmondo.com/EnableIT/kubeaid-bootstrap-script/pulls/12
  • Loading branch information
Archisman Mridha committed Nov 1, 2024
2 parents 68330f8 + 3d51b47 commit 820ae9f
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 109 deletions.
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[Makefile]
indent_style = tab
tab_width = 2

[*.yaml, *.yaml.tmpl]
indent_style = space
indent_size = 2
19 changes: 14 additions & 5 deletions cmd/bootstrap_cluster/bootstrap_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import (
var currentTime = time.Now().Unix()

func BootstrapCluster(ctx *cli.Context) error {
// Initialize temp dir where repositories will be cloned.
utils.InitTempDir()

// Parse the config file.
configFilePath := ctx.Path(constants.FlagNameConfigFile)
constants.ParsedConfig = config.ParseConfigFile(configFilePath)
Expand Down Expand Up @@ -85,7 +88,12 @@ func BootstrapCluster(ctx *cli.Context) error {
// The clusterawsadm utility takes the credentials that you set as environment variables and
// uses them to create a CloudFormation stack in your AWS account with the correct IAM resources.
// NOTE : This requires admin privileges.
utils.ExecuteCommandOrDie("clusterawsadm bootstrap iam create-cloudformation-stack")
output, err := utils.ExecuteCommand("clusterawsadm bootstrap iam create-cloudformation-stack")
//
// If an error occurs and it's not about AWS Cloudformation stack already existing, then panic.
if err != nil && !strings.Contains(output, "already exists, updating") {
log.Fatalf("Command execution failed : %v", output)
}

default:
utils.Unreachable()
Expand Down Expand Up @@ -135,7 +143,7 @@ func BootstrapCluster(ctx *cli.Context) error {
In that case, we'll retry. Otherwise, we'll fail if the 'error' word exists in the output.
If not that means the command execution is successfull.
*/
output := utils.ExecuteCommand("argocd app sync argo-cd/capi-cluster", false)
output, _ := utils.ExecuteCommand("argocd app sync argo-cd/capi-cluster")
if strings.Contains(output, "failed to call webhook") {
slog.Info("Waiting for kubeadm-control-plane-system and kubeadm-bootstrap-system webhooks to be available....")
time.Sleep(5 * time.Second)
Expand Down Expand Up @@ -182,11 +190,11 @@ func BootstrapCluster(ctx *cli.Context) error {
// Wait for the Kubernetes API server to be reachable and atleast 1 worker node to be
// initialized.
for {
initializedNodeCountAsString := utils.ExecuteCommand(`
initializedNodeCountAsString, _ := utils.ExecuteCommand(`
kubectl get nodes --no-headers -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints \
| grep -v node.cluster.x-k8s.io/uninitialized \
| wc -l
`, false)
`)
initializedNodeCountAsString = strings.TrimSpace(initializedNodeCountAsString)

initializedNodeCount, _ := strconv.Atoi(initializedNodeCountAsString)
Expand Down Expand Up @@ -246,7 +254,8 @@ func BootstrapCluster(ctx *cli.Context) error {
capiClusterNamespace := utils.GetCapiClusterNamespace()
utils.ExecuteCommandOrDie(fmt.Sprintf("kubectl create namespace %s", capiClusterNamespace))

// Sync the root, cert-manager, sealed-secrets, secrets and cluster-api ArgoCD Apps.
// Sync the root, cert-manager, sealed-secrets, secrets, kube-prometheus and cluster-api ArgoCD
// Apps.
argocdAppsToBeSynced := []string{
"root",
"cert-manager",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,4 @@ aws:
ami:
id: {{ .ControlPlaneAMI }}
machinePools:
{{- range .MachinePools }}
- name: {{ .Name }}
replicas: {{ .Replicas }}
instanceType: {{ .InstanceType }}
sshKeyName: {{ .SSHKeyName }}
ami:
id: {{ .AMI }}
rootVolumeSize: {{ .RootVolumeSize }}
{{- end }}
{{ .MachinePools | toYaml | indent 2 }}
29 changes: 19 additions & 10 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (
"log/slog"
"os"

"github.com/go-playground/validator/v10"
"github.com/creasty/defaults"
"gopkg.in/yaml.v3"
v1 "k8s.io/api/core/v1"
)

type (
Expand Down Expand Up @@ -62,12 +63,18 @@ type (
}

AWSMachinePool struct {
Name string `yaml:"name" validate:"required"`
Replicas int `yaml:"replicas" validate:"required"`
InstanceType string `yaml:"instanceType" validate:"required"`
SSHKeyName string `yaml:"sshKeyName" validate:"required"`
AMI string `yaml:"ami" validate:"required"`
RootVolumeSize int `yaml:"rootVolumeSize" validate:"required"`
Name string `yaml:"name" validate:"required"`
Replicas int `yaml:"replicas" validate:"required"`
InstanceType string `yaml:"instanceType" validate:"required"`
SSHKeyName string `yaml:"sshKeyName" validate:"required"`
AMI AMIConfig `yaml:"ami" validate:"required"`
RootVolumeSize int `yaml:"rootVolumeSize" validate:"required"`
Labels map[string]string `yaml:"labels" default:"[]"`
Taints []v1.Taint `yaml:"taints" default:"[]"`
}

AMIConfig struct {
ID string `yaml:"id" validate:"required"`
}

AzureConfig struct{}
Expand Down Expand Up @@ -103,10 +110,12 @@ func ParseConfig(configAsString string) (*Config, error) {
}
slog.Info("Parsed config")

validate := validator.New(validator.WithRequiredStructEnabled())
if err := validate.Struct(parsedConfig); err != nil {
return nil, fmt.Errorf("config validation failed : %v", err)
// Set defaults.
if err := defaults.Set(parsedConfig); err != nil {
log.Fatalf("Failed setting defaults for parsed config : %v", err)
}

validateConfig(parsedConfig)

return parsedConfig, nil
}
16 changes: 13 additions & 3 deletions config/templates/aws.sample.config.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ cloud:
aws:
accessKey: "...."
secretKey: "...."
region: us-east-2
region: us-east-1

sshKeyName: kubeaid-demo

Expand All @@ -24,14 +24,24 @@ cloud:

machinePools:
- name: primary
ami: {{ .AMI }}
ami:
id: {{ .AMI }}
instanceType: t4g.medium

replicas: 1
replicas: 2
rootVolumeSize: 35

sshKeyName: kubeaid-demo

# Label should meet one of the following criterias to propagate to Node :
#
# (1) Has node-role.kubernetes.io as prefix.
# (2) Belongs to node-restriction.kubernetes.io domain.
# (3) Belongs to node.cluster.x-k8s.io domain.
#
# REFER : https://cluster-api.sigs.k8s.io/developer/architecture/controllers/metadata-propagation#machine
# labels: []

monitoring:
kubePrometheusVersion: v0.14.0
grafanaURL: ""
Expand Down
77 changes: 77 additions & 0 deletions config/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package config

import (
"fmt"
"log"
"log/slog"
"os"
"strings"

"github.com/go-playground/validator/v10"
"github.com/siderolabs/talos/pkg/machinery/labels"
)

var (
// A user defined MachinePool label key should belong to one of these domains.
// REFER : https://cluster-api.sigs.k8s.io/developer/architecture/controllers/metadata-propagation#machine.
ValidMachinePoolLabelDomains = []string{
"node.cluster.x-k8s.io/",
"node-role.kubernetes.io/",
"node-restriction.kubernetes.io/",
}
)

// Validates the parsed config.
// Panics on failure.
// TODO : Extract the MachinePool labels and taints validation task from 'cloud specifics' section.
func validateConfig(config *Config) {
// Validate based on struct tags.
validate := validator.New(validator.WithRequiredStructEnabled())
if err := validate.Struct(config); err != nil {
log.Fatalf("config validation failed : %v", err)
}

// Cloud provider specific validations.
switch {
case config.Cloud.AWS != nil:

for _, machinePool := range config.Cloud.AWS.MachinePools {
// Validate MachinePools labels.
//
// (1) according to Kubernetes specifications.
if err := labels.Validate(machinePool.Labels); err != nil {
log.Fatalf("MachinePool labels validation failed : %v", err)
}
//
// (2) according to ClusterAPI specifications.
for key := range machinePool.Labels {
// Check if the label belongs to a domain considered valid by ClusterAPI.
isValidMachinePoolLabelDomain := false
for _, machinePoolLabelDomains := range ValidMachinePoolLabelDomains {
if strings.HasPrefix(key, machinePoolLabelDomains) {
isValidMachinePoolLabelDomain = true
break
}
}
if !isValidMachinePoolLabelDomain {
slog.Error("MachinePool label key should belong to one of these domains", slog.Any("domains", ValidMachinePoolLabelDomains))
os.Exit(1)
}
}

taintsAsKVPairs := map[string]string{}
for _, taint := range machinePool.Taints {
taintsAsKVPairs[taint.Key] = fmt.Sprintf("%s:%s", taint.Value, taint.Effect)
}
//
// Validate MachinePool taints.
if err := labels.ValidateTaints(taintsAsKVPairs); err != nil {
log.Fatalf("MachinePool taint validation failed : %v", err)
}
}

case config.Cloud.Azure != nil:
case config.Cloud.Hetzner != nil:
log.Fatal("Support for Azure and Hetzner are coming soon")
}
}
24 changes: 3 additions & 21 deletions constants/constants.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
package constants

import (
"fmt"
"log"
"log/slog"
"os"
"time"

"github.com/Obmondo/kubeaid-bootstrap-script/config"
)

Expand Down Expand Up @@ -40,11 +34,11 @@ var (
// Custom ARM and Ubuntu based AMI for each supported Kubernetes version, built by and published
// from Obmondo's AWS account.
ObmondoPublishedAMIs = map[string]string{
K8s_v1_31_0: "ami-05ec083b8943c8487",
K8s_v1_30_0: "ami-09d7052173f1960be",
K8s_v1_31_0: "ami-0fc044fa0d061d18d",
K8s_v1_30_0: "ami-0c4219b11327ef260",
}

TempDir = getTempDir()
TempDir string

ParsedConfig *config.Config

Expand Down Expand Up @@ -89,15 +83,3 @@ var (
"argocd-apps/ccm-aws.values.yaml.tmpl",
}
)

// NOTE : This should be in the utils package. But I've placed this here to avoid import cycle
// error.
func getTempDir() string {
name := fmt.Sprintf("kubeaid-bootstrap-script-%d", time.Now().Unix())
path, err := os.MkdirTemp("/tmp", name)
if err != nil {
log.Fatalf("Failed creating temp dir : %v", err)
}
slog.Info("Created temp dir", slog.String("path", path))
return path
}
45 changes: 36 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,46 +1,73 @@
module github.com/Obmondo/kubeaid-bootstrap-script

go 1.22.1
go 1.22.7

require (
github.com/creasty/defaults v1.8.0
github.com/go-git/go-git/v5 v5.12.0
github.com/go-playground/validator/v10 v10.22.1
github.com/go-sprout/sprout v0.6.0
github.com/siderolabs/talos/pkg/machinery v1.8.2
github.com/urfave/cli/v2 v2.27.4
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.31.2
)

require (
dario.cat/mergo v1.0.0 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/ProtonMail/go-crypto v1.1.0-alpha.5.0.20240827111422-b5837fa4476e // indirect
github.com/cloudflare/circl v1.3.9 // indirect
github.com/containerd/go-cni v1.1.10 // indirect
github.com/containernetworking/cni v1.2.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/siderolabs/crypto v0.5.0 // indirect
github.com/siderolabs/gen v0.5.0 // indirect
github.com/skeema/knownhosts v1.2.2 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/apimachinery v0.31.2 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
)
Loading

0 comments on commit 820ae9f

Please sign in to comment.