From 884c512e643052174ecf4827aabe0084bd568065 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Wed, 31 May 2023 13:13:52 +0530 Subject: [PATCH 1/3] :construction: wip --- apps/nodectrl/internal/app/main.go | 18 +- apps/nodectrl/internal/domain/aws.go | 215 ++++++++---- apps/nodectrl/internal/domain/azure.go | 5 + apps/nodectrl/internal/domain/common.go | 101 ++++++ apps/nodectrl/internal/domain/do.go | 105 +----- apps/nodectrl/internal/domain/gcp.go | 5 + apps/nodectrl/internal/domain/interface.go | 9 + apps/nodectrl/internal/domain/main.go | 99 ++---- .../domain/terraform-utils/.dockerignore | 3 + .../domain/terraform-utils/aws-test.go | 53 +++ .../internal/domain/terraform-utils/aws.go | 305 ++++++++++++++++++ .../internal/domain/terraform-utils/azure.go | 11 + .../internal/domain/terraform-utils/common.go | 125 +++++++ .../domain/terraform-utils/do-test.go | 82 +++++ .../internal/domain/terraform-utils/do.go | 300 +++++++++++++++++ .../domain/terraform-utils/interface.go | 9 + .../internal/domain/terraform-utils/main.go | 3 + .../terraform-utils/terraform/aws/init.sh | 7 + .../terraform-utils/terraform/aws/resource.tf | 161 +++++++++ .../terraform/aws/variables.tf | 31 ++ .../terraform-utils/terraform/do/init.sh | 7 + .../terraform-utils/terraform/do/resource.tf | 36 +++ .../terraform-utils/terraform/do/variables.tf | 40 +++ .../domain/terraform-utils/terraform/e | 1 + .../terraform-utils/terraform/secrets.yml | 2 + .../internal/domain/terraform-utils/types.go | 9 + .../internal/domain/terraform/aws/init.sh | 7 + .../internal/domain/terraform/aws/resource.tf | 161 +++++++++ .../domain/terraform/aws/variables.tf | 31 ++ .../internal/domain/terraform/do/init.sh | 7 + .../internal/domain/terraform/do/resource.tf | 36 +++ .../internal/domain/terraform/do/variables.tf | 40 +++ apps/nodectrl/internal/domain/terraform/e | 1 + .../internal/domain/terraform/secrets.yml | 2 + apps/nodectrl/internal/domain/utils/common.go | 125 +++++++ apps/nodectrl/internal/domain/utils/dump.go | 127 ++++++++ apps/nodectrl/internal/domain/utils/zipper.go | 190 +++++++++++ apps/nodectrl/internal/env/env.go | 27 ++ apps/nodectrl/internal/framework/main.go | 16 +- apps/nodectrl/main.go | 9 +- apps/{nodectrl => nodectrl2}/.dockerignore | 0 apps/nodectrl2/Dockerfile | 29 ++ apps/nodectrl2/Taskfile.yml | 27 ++ apps/nodectrl2/internal/app/main.go | 11 + apps/nodectrl2/internal/domain/aws.go | 103 ++++++ apps/nodectrl2/internal/domain/do.go | 106 ++++++ apps/nodectrl2/internal/domain/main.go | 91 ++++++ apps/nodectrl2/internal/domain/port.go | 5 + apps/nodectrl2/internal/framework/main.go | 24 ++ apps/nodectrl2/main.go | 25 ++ apps/{nodectrl => nodectrl2}/task.md | 0 go.mod | 2 +- go.sum | 5 +- 53 files changed, 2682 insertions(+), 267 deletions(-) create mode 100644 apps/nodectrl/internal/domain/azure.go create mode 100644 apps/nodectrl/internal/domain/common.go create mode 100644 apps/nodectrl/internal/domain/gcp.go create mode 100644 apps/nodectrl/internal/domain/interface.go create mode 100644 apps/nodectrl/internal/domain/terraform-utils/.dockerignore create mode 100644 apps/nodectrl/internal/domain/terraform-utils/aws-test.go create mode 100644 apps/nodectrl/internal/domain/terraform-utils/aws.go create mode 100644 apps/nodectrl/internal/domain/terraform-utils/azure.go create mode 100644 apps/nodectrl/internal/domain/terraform-utils/common.go create mode 100644 apps/nodectrl/internal/domain/terraform-utils/do-test.go create mode 100644 apps/nodectrl/internal/domain/terraform-utils/do.go create mode 100644 apps/nodectrl/internal/domain/terraform-utils/interface.go create mode 100644 apps/nodectrl/internal/domain/terraform-utils/main.go create mode 100644 apps/nodectrl/internal/domain/terraform-utils/terraform/aws/init.sh create mode 100644 apps/nodectrl/internal/domain/terraform-utils/terraform/aws/resource.tf create mode 100644 apps/nodectrl/internal/domain/terraform-utils/terraform/aws/variables.tf create mode 100644 apps/nodectrl/internal/domain/terraform-utils/terraform/do/init.sh create mode 100644 apps/nodectrl/internal/domain/terraform-utils/terraform/do/resource.tf create mode 100644 apps/nodectrl/internal/domain/terraform-utils/terraform/do/variables.tf create mode 100644 apps/nodectrl/internal/domain/terraform-utils/terraform/e create mode 100644 apps/nodectrl/internal/domain/terraform-utils/terraform/secrets.yml create mode 100644 apps/nodectrl/internal/domain/terraform-utils/types.go create mode 100644 apps/nodectrl/internal/domain/terraform/aws/init.sh create mode 100644 apps/nodectrl/internal/domain/terraform/aws/resource.tf create mode 100644 apps/nodectrl/internal/domain/terraform/aws/variables.tf create mode 100644 apps/nodectrl/internal/domain/terraform/do/init.sh create mode 100644 apps/nodectrl/internal/domain/terraform/do/resource.tf create mode 100644 apps/nodectrl/internal/domain/terraform/do/variables.tf create mode 100644 apps/nodectrl/internal/domain/terraform/e create mode 100644 apps/nodectrl/internal/domain/terraform/secrets.yml create mode 100644 apps/nodectrl/internal/domain/utils/common.go create mode 100644 apps/nodectrl/internal/domain/utils/dump.go create mode 100644 apps/nodectrl/internal/domain/utils/zipper.go create mode 100644 apps/nodectrl/internal/env/env.go rename apps/{nodectrl => nodectrl2}/.dockerignore (100%) create mode 100644 apps/nodectrl2/Dockerfile create mode 100644 apps/nodectrl2/Taskfile.yml create mode 100644 apps/nodectrl2/internal/app/main.go create mode 100644 apps/nodectrl2/internal/domain/aws.go create mode 100644 apps/nodectrl2/internal/domain/do.go create mode 100644 apps/nodectrl2/internal/domain/main.go create mode 100644 apps/nodectrl2/internal/domain/port.go create mode 100644 apps/nodectrl2/internal/framework/main.go create mode 100644 apps/nodectrl2/main.go rename apps/{nodectrl => nodectrl2}/task.md (100%) diff --git a/apps/nodectrl/internal/app/main.go b/apps/nodectrl/internal/app/main.go index dec13f138..925673c10 100644 --- a/apps/nodectrl/internal/app/main.go +++ b/apps/nodectrl/internal/app/main.go @@ -1,11 +1,23 @@ package app import ( + "fmt" + "os" + "go.uber.org/fx" - "kloudlite.io/apps/nodecontroller/internal/domain" + "kloudlite.io/apps/nodectrl/internal/domain" ) -var Module = fx.Module( - "app", +var Module = fx.Module("app", domain.Module, + fx.Invoke( + func(d domain.Domain, shutdowner fx.Shutdowner) { + if err := d.StartJob(); err != nil { + fmt.Println(err) + os.Exit(1) + } else { + shutdowner.Shutdown() + } + }, + ), ) diff --git a/apps/nodectrl/internal/domain/aws.go b/apps/nodectrl/internal/domain/aws.go index 4b324380c..6532355ae 100644 --- a/apps/nodectrl/internal/domain/aws.go +++ b/apps/nodectrl/internal/domain/aws.go @@ -1,103 +1,180 @@ package domain import ( - "encoding/base64" - "encoding/json" - "errors" "fmt" + "os" + "os/exec" + "path" "gopkg.in/yaml.v3" - infraclient "kloudlite.io/pkg/infraClient" + "kloudlite.io/apps/nodectrl/internal/domain/utils" ) -type awsConfig struct { - Version string `yaml:"version"` - Action string `yaml:"action"` - Provider string `yaml:"provider"` - Spec struct { - Provider struct { - AccessKey string `yaml:"accessKey"` - AccessSecret string `yaml:"accessSecret"` - AccountId string `yaml:"accountId"` - } `yaml:"provider"` - Node struct { - Region string `yaml:"region"` - InstanceType string `yaml:"instanceType"` - NodeId string `yaml:"nodeId"` - VPC string `yaml:"vpc"` - } `yaml:"node"` - } `yaml:"spec"` +type CommonProviderData struct { + StorePath string `yaml:"storePath"` + TfTemplates string `yaml:"tfTemplates"` + Labels map[string]string `yaml:"labels"` + Taints []string `yaml:"taints"` + Secrets string `yaml:"secrets"` + SSHPath string `yaml:"sshPath"` } -func (d *domainI) doWithAWS() error { +type awsProviderConfig struct { + accessKey string `yaml:"accessKey"` + accessSecret string `yaml:"accessSecret"` + accountId string `yaml:"accountId"` +} + +type AWSNode struct { + NodeId string `yaml:"nodeId"` + Region string `yaml:"region"` + InstanceType string `yaml:"instanceType"` + VPC string `yaml:"vpc"` + ImageId string `yaml:"imageId"` +} + +type awsClient struct { + node AWSNode + + accessKey string + accessSecret string + + SSHPath string + accountId string + secrets string + providerDir string + storePath string + tfTemplates string + labels map[string]string + taints []string +} - out, err := base64.StdEncoding.DecodeString(d.env.Config) - if err != nil { +// getFolder implements doProviderClient +func (a awsClient) getFolder() string { + // eg -> /path/acc_id/do/blr1/node_id/do + + return path.Join(a.storePath, a.accountId, a.providerDir, a.node.Region, a.node.NodeId) +} + +// initTFdir implements doProviderClient +func (d awsClient) initTFdir() error { + + folder := d.getFolder() + + if err := utils.ExecCmd(fmt.Sprintf("cp -r %s %s", fmt.Sprintf("%s/%s", d.tfTemplates, d.providerDir), folder), "initialize terraform"); err != nil { return err } - var awsConf awsConfig - e := yaml.Unmarshal(out, &awsConf) - if e != nil { - return e + + cmd := exec.Command("terraform", "init") + cmd.Dir = path.Join(folder, d.providerDir) + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd.Run() +} + +// NewNode implements ProviderClient +func (a awsClient) NewNode() error { + + values := map[string]string{} + + values["access_key"] = a.accessKey + values["secret_key"] = a.accessSecret + + values["region"] = a.node.Region + values["node_id"] = a.node.NodeId + values["instance_type"] = a.node.InstanceType + values["keys-path"] = a.SSHPath + values["ami"] = a.node.ImageId + + return nil + + // making dir + if err := utils.Mkdir(a.getFolder()); err != nil { + return err } - klConf, err := d.getKlConf() - if err != nil { + + // initialize directory + if err := a.initTFdir(); err != nil { return err } - labels := map[string]string{} - if e := json.Unmarshal([]byte(d.env.Labels), &labels); e != nil { - fmt.Println(e) + // apply terraform + return utils.ApplyTF(path.Join(a.getFolder(), a.providerDir), values) + +} + +// AttachNode implements ProviderClientkkk +func (awsClient) AttachNode() error { + panic("unimplemented") +} + +// DeleteNode implements ProviderClient +func (awsClient) DeleteNode() error { + panic("unimplemented") +} + +// UnattachNode implements ProviderClient +func (awsClient) UnattachNode() error { + panic("unimplemented") +} + +func NewAwsProviderClient(node AWSNode, cpd CommonProviderData, apc awsProviderConfig) ProviderClient { + return awsClient{ + node: node, + accessKey: apc.accessKey, + accessSecret: apc.accessSecret, + accountId: apc.accountId, + + providerDir: "aws", + secrets: cpd.Secrets, + storePath: cpd.StorePath, + tfTemplates: cpd.TfTemplates, + labels: cpd.Labels, + taints: cpd.Taints, + SSHPath: cpd.SSHPath, } +} - taints := []string{} - if e := json.Unmarshal([]byte(d.env.Taints), &taints); e != nil { - fmt.Println(e) +func (d domain) StartAwsJob() error { + + node := AWSNode{} + + if err := yaml.Unmarshal([]byte(d.env.NodeConfig), &node); err != nil { + return err } - awsProvider := infraclient.NewAWSProvider(infraclient.AWSProvider{ - AccessKey: awsConf.Spec.Provider.AccessKey, - AccessSecret: awsConf.Spec.Provider.AccessSecret, - AccountId: awsConf.Spec.Provider.AccountId, - }, infraclient.AWSProviderEnv{ - StorePath: klConf.Values.StorePath, - TfTemplates: klConf.Values.TfTemplates, - Labels: labels, - Taints: taints, - Secrets: klConf.Values.Secrets, - SSHPath: klConf.Values.SSHPath, - }) - - awsNode := infraclient.AWSNode{ - NodeId: awsConf.Spec.Node.NodeId, - Region: awsConf.Spec.Node.Region, - InstanceType: awsConf.Spec.Node.InstanceType, - VPC: awsConf.Spec.Node.VPC, + cpd := CommonProviderData{} + + if err := yaml.Unmarshal([]byte(d.env.ProviderConfig), &cpd); err != nil { + return err + } + + apc := awsProviderConfig{} + + if err := yaml.Unmarshal([]byte(d.env.AWSProviderConfig), &apc); err != nil { + return err } - // return nil + pc := NewAwsProviderClient(node, cpd, apc) - switch awsConf.Action { + switch d.env.Action { case "create": - err = awsProvider.NewNode(awsNode) - if err != nil { + fmt.Println("needs to create node") + if err := pc.NewNode(); err != nil { return err } - err = awsProvider.AttachNode(awsNode) - if err != nil { - return err - } - case "delete": - err = awsProvider.DeleteNode(awsNode) - - if err != nil { + fmt.Println("needs to delete node") + if err := pc.DeleteNode(); err != nil { return err } - + case "": + return fmt.Errorf("ACTION not provided, supported actions {create, delete} ") default: - return errors.New("wrong action") + return fmt.Errorf("not supported actions '%s' please provide one of supported action like { create, delete }", d.env.Action) } - + fmt.Println("aws job started") return nil } diff --git a/apps/nodectrl/internal/domain/azure.go b/apps/nodectrl/internal/domain/azure.go new file mode 100644 index 000000000..c5930c983 --- /dev/null +++ b/apps/nodectrl/internal/domain/azure.go @@ -0,0 +1,5 @@ +package domain + +func (d domain) StartAzureJob() error { + panic("not implemented yet") +} diff --git a/apps/nodectrl/internal/domain/common.go b/apps/nodectrl/internal/domain/common.go new file mode 100644 index 000000000..1b7f3f926 --- /dev/null +++ b/apps/nodectrl/internal/domain/common.go @@ -0,0 +1,101 @@ +package domain + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "strings" + + "kloudlite.io/apps/nodectrl/internal/domain/utils" +) + +const ( + CLUSTER_ID = "kl" +) + +// rmTFdir implements doProviderClient +// func rmdir(folder string) error { +// return execCmd(fmt.Sprintf("rm -rf %q", folder), "") +// } + +// makeTFdir implements doProviderClient +func mkdir(folder string) error { + return utils.ExecCmd(fmt.Sprintf("mkdir -p %q", folder), "mkdir ") +} + +// destroyNode implements doProviderClient +func destroyNode(folder string, values map[string]string) error { + vars := []string{"destroy", "-auto-approve"} + + for k, v := range values { + vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) + } + + cmd := exec.Command("terraform", vars...) + cmd.Dir = folder + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err := cmd.Run() + if err != nil { + fmt.Println(err) + return err + } + + return os.RemoveAll(folder) + + // return err +} + +func getOutput(folder, key string) ([]byte, error) { + vars := []string{"output", "-json"} + fmt.Printf("[#] terraform %s\n", strings.Join(vars, " ")) + cmd := exec.Command("terraform", vars...) + cmd.Dir = folder + + // cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + out, err := cmd.Output() + if err != nil { + return nil, err + + } + + // fmt.Println(string(out)) + + var resp map[string]struct { + Value string `json:"value"` + } + + err = json.Unmarshal(out, &resp) + if err != nil { + return nil, err + } + + return []byte(resp[key].Value), nil +} + +// applyTF implements doProviderClient +func applyTF(folder string, values map[string]string) error { + + vars := []string{"apply", "-auto-approve"} + + fmt.Printf("[#] terraform %s", strings.Join(vars, " ")) + + for k, v := range values { + vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) + } + + cmd := exec.Command("terraform", vars...) + cmd.Dir = folder + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + cmd.Dir = folder + + return cmd.Run() +} diff --git a/apps/nodectrl/internal/domain/do.go b/apps/nodectrl/internal/domain/do.go index d0bea72b4..929ec17c1 100644 --- a/apps/nodectrl/internal/domain/do.go +++ b/apps/nodectrl/internal/domain/do.go @@ -1,106 +1,5 @@ package domain -import ( - "encoding/base64" - "encoding/json" - "errors" - "fmt" - - "gopkg.in/yaml.v3" - infraclient "kloudlite.io/pkg/infraClient" -) - -type doConfig struct { - Version string `yaml:"version"` - Action string `yaml:"action"` - Provider string `yaml:"provider"` - Spec struct { - Provider struct { - ApiToken string `yaml:"apiToken"` - AccountId string `yaml:"accountId"` - } `yaml:"provider"` - Node struct { - Region string `yaml:"region"` - Size string `yaml:"size"` - NodeId string `yaml:"nodeId"` - ImageId string `yaml:"imageId"` - } `yaml:"node"` - } `yaml:"spec"` -} - -func (d *domainI) doWithDO() error { - - out, err := base64.StdEncoding.DecodeString(d.env.Config) - if err != nil { - fmt.Println("here") - return err - } - - var doConf doConfig - e := yaml.Unmarshal(out, &doConf) - if e != nil { - return e - } - klConf, err := d.getKlConf() - if err != nil { - fmt.Println("here") - return err - } - - labels := map[string]string{} - if e := json.Unmarshal([]byte(d.env.Labels), &labels); e != nil { - fmt.Println(e) - } - - taints := []string{} - if e := json.Unmarshal([]byte(d.env.Taints), &taints); e != nil { - fmt.Println(e) - } - - doProvider := infraclient.NewDOProvider(infraclient.DoProvider{ - ApiToken: doConf.Spec.Provider.ApiToken, - AccountId: doConf.Spec.Provider.AccountId, - }, infraclient.DoProviderEnv{ - StorePath: klConf.Values.StorePath, - TfTemplates: klConf.Values.TfTemplates, - Secrets: klConf.Values.Secrets, - Labels: labels, - Taints: taints, - SSHPath: klConf.Values.SSHPath, - }) - - doNode := infraclient.DoNode{ - Region: doConf.Spec.Node.Region, - Size: doConf.Spec.Node.Size, - NodeId: doConf.Spec.Node.NodeId, - ImageId: doConf.Spec.Node.ImageId, - } - - // return nil - - switch doConf.Action { - case "create": - if err = doProvider.NewNode(doNode); err != nil { - return err - } - - if err = doProvider.AttachNode(doNode); err != nil { - return err - } - - case "delete": - - if err = doProvider.UnattachNode(doNode); err != nil { - return err - } - - if err = doProvider.DeleteNode(doNode); err != nil { - return err - } - - default: - return errors.New("wrong action") - } - - return nil +func (d domain) StartDoJob() error { + panic("not implemented yet") } diff --git a/apps/nodectrl/internal/domain/gcp.go b/apps/nodectrl/internal/domain/gcp.go new file mode 100644 index 000000000..ca2d40eeb --- /dev/null +++ b/apps/nodectrl/internal/domain/gcp.go @@ -0,0 +1,5 @@ +package domain + +func (d domain) StartGCPJob() error { + panic("not implemented yet") +} diff --git a/apps/nodectrl/internal/domain/interface.go b/apps/nodectrl/internal/domain/interface.go new file mode 100644 index 000000000..63b634a85 --- /dev/null +++ b/apps/nodectrl/internal/domain/interface.go @@ -0,0 +1,9 @@ +package domain + +type ProviderClient interface { + NewNode() error + DeleteNode() error + + AttachNode() error + UnattachNode() error +} diff --git a/apps/nodectrl/internal/domain/main.go b/apps/nodectrl/internal/domain/main.go index 2cb7dd50a..d87149052 100644 --- a/apps/nodectrl/internal/domain/main.go +++ b/apps/nodectrl/internal/domain/main.go @@ -1,91 +1,50 @@ package domain import ( - "encoding/base64" - "errors" "fmt" - "gopkg.in/yaml.v3" - "kloudlite.io/pkg/config" - "go.uber.org/fx" + "kloudlite.io/apps/nodectrl/internal/env" ) -type domainI struct { - env *Env -} - -type KLConf struct { - Version string `yaml:"version"` - Values struct { - StorePath string `yaml:"storePath"` - TfTemplates string `yaml:"tfTemplatesPath"` - Secrets string `yaml:"secrets"` - SSHPath string `yaml:"sshPath"` - PubKey string `yaml:"pubkey"` - } `yaml:"spec"` +type domain struct { + env *env.Env } -func (d *domainI) getKlConf() (*KLConf, error) { - out, err := base64.StdEncoding.DecodeString(d.env.KLConfig) - if err != nil { - fmt.Println("here") - return nil, err - } - - var klConf KLConf - e := yaml.Unmarshal(out, &klConf) - if e != nil { - - return nil, e - } - - return &klConf, nil -} - -// startJob implements Domain -func (d *domainI) StartJob() error { - - switch d.env.Provider { +// StartJob implements Domain +func (d domain) StartJob() error { + switch d.env.CloudProvider { + case "aws": + if err := d.StartAwsJob(); err != nil { + return err + } + case "azure": + if err := d.StartAzureJob(); err != nil { + return err + } case "do": - if err := d.doWithDO(); err != nil { + if err := d.StartDoJob(); err != nil { return err } - - case "aws": - if err := d.doWithAWS(); err != nil { + case "gcp": + if err := d.StartGCPJob(); err != nil { return err } - + case "": + return fmt.Errorf("please provide cloud provider") default: - return errors.New("this type of provider not suported") - } - return nil -} + return fmt.Errorf("provided cloud provider %s not available now", d.env.CloudProvider) -func fxDomain(env *Env) Domain { - return &domainI{ - env: env, } + return nil } -type Env struct { - Config string `env:"NODE_CONFIG" required:"true"` - Provider string `env:"PROVIDER" required:"true"` - KLConfig string `env:"KL_CONFIG" required:"true"` - Labels string `env:"LABELS" required:"true"` - Taints string `env:"TAINTS" required:"true"` -} - -var Module = fx.Module( - "domain", - config.EnvFx[Env](), - fx.Provide(fxDomain), +var Module = fx.Module("domain", + fx.Provide( + func(env *env.Env) Domain { + return domain{ + env: env, + } + }, + ), ) - -/* -main - -> framework () - -> app () - -> domain (main logic) -*/ diff --git a/apps/nodectrl/internal/domain/terraform-utils/.dockerignore b/apps/nodectrl/internal/domain/terraform-utils/.dockerignore new file mode 100644 index 000000000..002bae2c9 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/.dockerignore @@ -0,0 +1,3 @@ +aws-test* +do-test* +consts.go diff --git a/apps/nodectrl/internal/domain/terraform-utils/aws-test.go b/apps/nodectrl/internal/domain/terraform-utils/aws-test.go new file mode 100644 index 000000000..d12bd9d0e --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/aws-test.go @@ -0,0 +1,53 @@ +package terraformutils + +import ( + "fmt" +) + +func testAwsClient() { + env := GetEnvOrDie() + + awsp := NewAWSProvider(AWSProvider{ + AccessKey: "***REMOVED***", + AccessSecret: "***REMOVED***", + AccountId: "kl-core", + }, AWSProviderEnv{ + StorePath: "/home/vision/tf", + TfTemplates: "/home/vision/kloudlite/api-go/pkg/infraClient/terraform", + Secrets: env.Secret, + SSHPath: "/home/vision/.ssh", + }) + + var err error + + node := AWSNode{ + NodeId: "aws-worker-01", + Region: "ap-south-1", + InstanceType: "m5.large", + VPC: "", + } + + if false { + + if err = awsp.NewNode(node); err != nil { + fmt.Println(err) + return + } + + if err = awsp.AttachNode(node); err != nil { + fmt.Println(err) + return + } + + } else { + + if err = awsp.DeleteNode(node); err != nil { + fmt.Println(err) + return + } + + } + + // time.Sleep(time.Second * 10) + +} diff --git a/apps/nodectrl/internal/domain/terraform-utils/aws.go b/apps/nodectrl/internal/domain/terraform-utils/aws.go new file mode 100644 index 000000000..da9273fcb --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/aws.go @@ -0,0 +1,305 @@ +package terraformutils + +import ( + "encoding/base64" + "fmt" + "os" + "os/exec" + "path" + "strings" + "time" + + "gopkg.in/yaml.v3" +) + +type awsProvider struct { + accessKey string + accessSecret string + + SSHPath string + accountId string + secrets string + providerDir string + storePath string + tfTemplates string + labels map[string]string + taints []string +} + +type AWSNode struct { + NodeId string + Region string + InstanceType string + VPC string + ImageId string +} + +type awsProviderClient interface { + NewNode(node AWSNode) error + DeleteNode(node AWSNode) error + + AttachNode(node AWSNode) error + UnattachNode(node AWSNode) error + + // mkdir(folder string) error + // rmdir(folder string) error + // getFolder(region, nodeId string) string + // initTFdir(region, nodeId string) error + // applyTF(region, nodeId string, values map[string]string) error + // destroyNode(folder string) error + // execCmd(cmd string) error +} + +// getFolder implements doProviderClient +func (a *awsProvider) getFolder(region string, nodeId string) string { + // eg -> /path/acc_id/do/blr1/node_id/do + + return path.Join(a.storePath, a.accountId, a.providerDir, region, nodeId) +} + +// initTFdir implements doProviderClient +func (d *awsProvider) initTFdir(node AWSNode) error { + + folder := d.getFolder(node.Region, node.NodeId) + + if err := execCmd(fmt.Sprintf("cp -r %s %s", fmt.Sprintf("%s/%s", d.tfTemplates, d.providerDir), folder), "initialize terraform"); err != nil { + return err + } + + cmd := exec.Command("terraform", "init") + cmd.Dir = path.Join(folder, d.providerDir) + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd.Run() +} + +type TalosAmi struct { + Cloud string + Version string + Region string + Arch string + Type string + Id string +} + +// NewNode implements awsProviderClient +func (a *awsProvider) NewNode(node AWSNode) error { + + values := map[string]string{} + + values["access_key"] = a.accessKey + values["secret_key"] = a.accessSecret + + values["region"] = node.Region + values["node_id"] = node.NodeId + values["instance_type"] = node.InstanceType + values["keys-path"] = a.SSHPath + values["ami"] = node.ImageId + + // making dir + if err := mkdir(a.getFolder(node.Region, node.NodeId)); err != nil { + return err + } + + // initialize directory + if err := a.initTFdir(node); err != nil { + return err + } + + // apply terraform + return applyTF(path.Join(a.getFolder(node.Region, node.NodeId), a.providerDir), values) + +} + +// AttachNode implements awsProviderClient +func (a *awsProvider) AttachNode(node AWSNode) error { + + var out, secretYaml []byte + var err error + + if out, err = getOutput(path.Join(a.getFolder(node.Region, node.NodeId), a.providerDir), "node-ip"); err != nil { + return err + } + + if secretYaml, err = base64.StdEncoding.DecodeString(a.secrets); err != nil { + return err + } + + var sec joinTokenSecret + + if err = yaml.Unmarshal(secretYaml, &sec); err != nil { + return err + } + + labels := func() []string { + l := []string{} + for k, v := range a.labels { + l = append(l, fmt.Sprintf("--node-label %s=%s", k, v)) + } + l = append(l, fmt.Sprintf("--node-label %s=%s", "kloudlite.io/public-ip", string(out))) + return l + }() + + count := 0 + + for { + if e := execCmd( + fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s ls", + fmt.Sprintf("%v/access", a.SSHPath), + string(out)), + "checking if node is ready "); e == nil { + break + } + + count++ + if count > 24 { + return fmt.Errorf("node is not ready even after 6 minutes") + } + time.Sleep(time.Second * 15) + } + + if err = execCmd(fmt.Sprintf("kubectl get node %s", node.NodeId), "checking if node attached"); err == nil { + fmt.Println("node already attached. clean exit") + return nil + } + + // // install k3s + // if e := execCmd( + // fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s sudo sh /tmp/k3s-install.sh", + // fmt.Sprintf("%v/access", d.SSHPath), string(out)), + // ""); e != nil { + // return e + // } + + // attach node + if e := execCmd( + fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s sudo sh /tmp/k3s-install.sh agent --server %s --token %s %s --node-name %s --node-external-ip %s --node-ip %s", + fmt.Sprintf("%v/access", a.SSHPath), string(out), sec.EndpointUrl, sec.JoinToken, + strings.Join(labels, " "), node.NodeId, string(out), string(out)), + "attaching to cluster"); e != nil { + return e + } + + count = 0 + for { + if err = execCmd(fmt.Sprintf("kubectl get node %s", node.NodeId), "checking if node attached"); err == nil { + fmt.Println("node attached successfully.") + break + } + + count++ + if count > 24 { + return fmt.Errorf("node not attached even after 6minutes") + } + time.Sleep(time.Second * 15) + } + + // "hostname": node.NodeId, + // "labels": strings.Join(labels, ","), + // TODO: needs to AttachNode here + return nil + +} + +// DeleteNode implements awsProviderClient +func (a *awsProvider) DeleteNode(node AWSNode) error { + var err error + + // time.Sleep(time.Minute * 2) + values := map[string]string{} + + //TODO: remove node from cluster after drain proceed following + + values["access_key"] = a.accessKey + values["secret_key"] = a.accessSecret + + values["region"] = node.Region + values["node_id"] = node.NodeId + values["instance_type"] = node.InstanceType + values["keys-path"] = a.SSHPath + values["ami"] = node.ImageId + + nodetfpath := path.Join(a.getFolder(node.Region, node.NodeId), a.providerDir) + + // check if dir present + if _, e := os.Stat(path.Join(nodetfpath, "init.sh")); e != nil && os.IsNotExist(e) { + fmt.Println("tf state not present nothing to do") + return nil + } + + // get node name + var out []byte + if out, err = getOutput(nodetfpath, "node-name"); err != nil { + return err + } else if strings.TrimSpace(string(out)) == "" { + fmt.Println("something went wrong, can't find node_name") + return nil + } + + // destroy node + return destroyNode(nodetfpath, values) + +} + +func (a *awsProvider) UnattachNode(node AWSNode) error { + var out []byte + var err error + + if out, err = getOutput(path.Join(a.getFolder(node.Region, node.NodeId), a.providerDir), "node-name"); err != nil { + return err + } else if strings.TrimSpace(string(out)) == "" { + fmt.Println("something went wrong, can't find node_name") + return nil + } + + if err = execCmd(fmt.Sprintf("kubectl get node %s", out), "checknode present"); err != nil { + fmt.Println("node not found may be already deleted") + return nil + } + + // drain node + if err = execCmd(fmt.Sprintf("kubectl taint nodes %s force=delete:NoExecute", node.NodeId), "drain node to delete"); err != nil { + return err + } + + fmt.Println("[#] waiting 10 seconds after drain") + time.Sleep(time.Second * 10) + + // delete node + return execCmd(fmt.Sprintf("kubectl delete node %s", out), + "delete node from cluster") +} + +type AWSProvider struct { + AccessKey string + AccessSecret string + AccountId string +} + +type AWSProviderEnv struct { + StorePath string + TfTemplates string + + Labels map[string]string + Taints []string + Secrets string + SSHPath string +} + +func NewAWSProvider(provider AWSProvider, p AWSProviderEnv) awsProviderClient { + return &awsProvider{ + accessKey: provider.AccessKey, + accessSecret: provider.AccessSecret, + accountId: provider.AccountId, + + providerDir: "aws", + secrets: p.Secrets, + storePath: p.StorePath, + tfTemplates: p.TfTemplates, + labels: p.Labels, + taints: p.Taints, + SSHPath: p.SSHPath, + } +} diff --git a/apps/nodectrl/internal/domain/terraform-utils/azure.go b/apps/nodectrl/internal/domain/terraform-utils/azure.go new file mode 100644 index 000000000..01a8bf613 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/azure.go @@ -0,0 +1,11 @@ +package terraformutils + +type azureProvider struct { + provider string +} + +func NewAzureProvider() *azureProvider { + return &azureProvider{ + provider: "", + } +} diff --git a/apps/nodectrl/internal/domain/terraform-utils/common.go b/apps/nodectrl/internal/domain/terraform-utils/common.go new file mode 100644 index 000000000..8712569d7 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/common.go @@ -0,0 +1,125 @@ +package terraformutils + +import ( + "encoding/csv" + "encoding/json" + "fmt" + "os" + "os/exec" + "strings" +) + +const ( + CLUSTER_ID = "kl" +) + +// rmTFdir implements doProviderClient +// func rmdir(folder string) error { +// return execCmd(fmt.Sprintf("rm -rf %q", folder), "") +// } + +// makeTFdir implements doProviderClient +func mkdir(folder string) error { + return execCmd(fmt.Sprintf("mkdir -p %q", folder), "mkdir ") +} + +// destroyNode implements doProviderClient +func destroyNode(folder string, values map[string]string) error { + vars := []string{"destroy", "-auto-approve"} + + for k, v := range values { + vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) + } + + cmd := exec.Command("terraform", vars...) + cmd.Dir = folder + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err := cmd.Run() + if err != nil { + fmt.Println(err) + return err + } + + return os.RemoveAll(folder) + + // return err +} + +func getOutput(folder, key string) ([]byte, error) { + vars := []string{"output", "-json"} + fmt.Printf("[#] terraform %s\n", strings.Join(vars, " ")) + cmd := exec.Command("terraform", vars...) + cmd.Dir = folder + + // cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + out, err := cmd.Output() + if err != nil { + return nil, err + + } + + // fmt.Println(string(out)) + + var resp map[string]struct { + Value string `json:"value"` + } + + err = json.Unmarshal(out, &resp) + if err != nil { + return nil, err + } + + return []byte(resp[key].Value), nil +} + +// applyTF implements doProviderClient +func applyTF(folder string, values map[string]string) error { + + vars := []string{"apply", "-auto-approve"} + + fmt.Printf("[#] terraform %s", strings.Join(vars, " ")) + + for k, v := range values { + vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) + } + + cmd := exec.Command("terraform", vars...) + cmd.Dir = folder + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + cmd.Dir = folder + + return cmd.Run() +} + +func execCmd(cmdString string, logStr string) error { + r := csv.NewReader(strings.NewReader(cmdString)) + r.Comma = ' ' + cmdArr, err := r.Read() + if err != nil { + return err + } + + if logStr != "" { + fmt.Printf("[#] %s\n", logStr) + } else { + fmt.Printf("[#] %s\n", strings.Join(cmdArr, " ")) + } + + cmd := exec.Command(cmdArr[0], cmdArr[1:]...) + cmd.Stderr = os.Stderr + // cmd.Stdout = os.Stdout + + if err := cmd.Run(); err != nil { + fmt.Printf("err occurred: %s\n", err.Error()) + return err + } + return nil +} diff --git a/apps/nodectrl/internal/domain/terraform-utils/do-test.go b/apps/nodectrl/internal/domain/terraform-utils/do-test.go new file mode 100644 index 000000000..77cfb6fb8 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/do-test.go @@ -0,0 +1,82 @@ +package terraformutils + +import ( + "fmt" + "time" + + "github.com/codingconcepts/env" +) + +type Env struct { + Secret string `env:"SECRETS" required:"true"` +} + +func GetEnvOrDie() *Env { + var ev Env + if err := env.Set(&ev); err != nil { + panic(err) + } + return &ev +} + +func testDoClient() { + env := GetEnvOrDie() + + dop := NewDOProvider(DoProvider{ + ApiToken: "***REMOVED***", + AccountId: "kl-core", + }, DoProviderEnv{ + StorePath: "/home/vision/tf", + TfTemplates: "/home/vision/kloudlite/api-go/pkg/infraClient/terraform", + Secrets: env.Secret, + Labels: map[string]string{ + // "kloudlite.io/region": "blr1", + + }, + + SSHPath: "/home/vision/.ssh", + }) + + var err error + + node := DoNode{ + Region: "blr1", + // Size: "s-4vcpu-8gb-amd", + // Size: "s-2vcpu-4gb-amd", + // Size: "s-1vcpu-1gb-amd", + Size: "c-2", + NodeId: "try-agent-01", + ImageId: "ubuntu-22-10-x64", + } + + // fmt.Println(node, err, dop) + + if false { + + if err = dop.NewNode(node); err != nil { + fmt.Println(err) + return + } + + for { + + if err = dop.AttachNode(node); err != nil { + fmt.Println(err) + + time.Sleep(time.Second * 5) + continue + } + + return + } + + } else { + + if err = dop.DeleteNode(node); err != nil { + fmt.Println(err) + return + } + + } + +} diff --git a/apps/nodectrl/internal/domain/terraform-utils/do.go b/apps/nodectrl/internal/domain/terraform-utils/do.go new file mode 100644 index 000000000..a07f20b1c --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/do.go @@ -0,0 +1,300 @@ +package terraformutils + +import ( + "encoding/base64" + "fmt" + "os" + "os/exec" + "path" + "strings" + "time" + + "gopkg.in/yaml.v3" +) + +type doProviderClient interface { + NewNode(node DoNode) error + DeleteNode(node DoNode) error + + AttachNode(node DoNode) error + UnattachNode(node DoNode) error + + // mkdir(folder string) error + // rmdir(folder string) error + // getFolder(region, nodeId string) string + // initTFdir(region, nodeId string) error + // applyTF(region, nodeId string, values map[string]string) error + // destroyNode(folder string) error + // execCmd(cmd string) error +} + +type DoNode struct { + Region string + Size string + NodeId string + ImageId string +} + +type joinTokenSecret struct { + JoinToken string `json:"joinToken" yaml:"joinToken"` + EndpointUrl string `json:"endpointUrl" yaml:"endPointUrl"` +} + +type doProvider struct { + apiToken string + accountId string + providerDir string + storePath string + tfTemplates string + SSHPath string + PubKey string + labels map[string]string + taints []string + secrets string +} + +// getFolder implements doProviderClient +func (d *doProvider) getFolder(region string, nodeId string) string { + // eg -> /path/do/blr1/acc_id/node_id + return path.Join(d.storePath, d.accountId, d.providerDir, region, nodeId) +} + +// initTFdir implements doProviderClient +func (d *doProvider) initTFdir(node DoNode) error { + + folder := d.getFolder(node.Region, node.NodeId) + + if err := execCmd(fmt.Sprintf("cp -r %s %s", fmt.Sprintf("%s/%s", d.tfTemplates, d.providerDir), folder), "initialize terraform"); err != nil { + return err + } + + cmd := exec.Command("terraform", "init") + cmd.Dir = path.Join(folder, d.providerDir) + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd.Run() +} + +// NewNode implements doProviderClient +func (d *doProvider) NewNode(node DoNode) error { + values := map[string]string{} + + values["cluster-id"] = CLUSTER_ID + + // values["keys-path"] = d.sshKeyPath + values["do-token"] = d.apiToken + values["accountId"] = d.accountId + + values["do-image-id"] = "ubuntu-22-10-x64" + values["nodeId"] = node.NodeId + values["size"] = node.Size + values["keys-path"] = d.SSHPath + + // making dir + if err := mkdir(d.getFolder(node.Region, node.NodeId)); err != nil { + return err + } + + // initialize directory + if err := d.initTFdir(node); err != nil { + return err + } + + tfPath := path.Join(d.getFolder(node.Region, node.NodeId), d.providerDir) + + // apply terraform + return applyTF(tfPath, values) +} + +// DeleteNode implements ProviderClient +func (d *doProvider) DeleteNode(node DoNode) error { + // time.Sleep(time.Minute * 2) + values := map[string]string{} + + //TODO: remove node from cluster after drain proceed following + + values["cluster-id"] = CLUSTER_ID + + // values["keys-path"] = d.sshKeyPath + values["do-token"] = d.apiToken + values["accountId"] = d.accountId + + // values["do-image-id"] = node.ImageId + values["do-image-id"] = "ubuntu-22-10-x64" + values["nodeId"] = node.NodeId + values["keys-path"] = d.SSHPath + + nodetfpath := path.Join(d.getFolder(node.Region, node.NodeId), d.providerDir) + + // check if dir present + if _, err := os.Stat(path.Join(nodetfpath, "init.sh")); err != nil && os.IsNotExist(err) { + fmt.Println("tf state not present nothing to do") + return nil + } + + // get node name + var out []byte + var err error + if out, err = getOutput(nodetfpath, "node-name"); err != nil { + return err + } else if strings.TrimSpace(string(out)) == "" { + fmt.Println("something went wrong, can't find node_name") + return nil + } + + // destroy node + return destroyNode(nodetfpath, values) +} + +// AttachNode implements ProviderClient +func (d *doProvider) AttachNode(node DoNode) error { + + var out, secretYaml []byte + var err error + + if out, err = getOutput(path.Join(d.getFolder(node.Region, node.NodeId), d.providerDir), "node-ip"); err != nil { + return err + } + + if secretYaml, err = base64.StdEncoding.DecodeString(d.secrets); err != nil { + fmt.Println("here", d.secrets) + return err + } + + var sec joinTokenSecret + + if err = yaml.Unmarshal(secretYaml, &sec); err != nil { + return err + } + + labels := func() []string { + l := []string{} + for k, v := range d.labels { + l = append(l, fmt.Sprintf("--node-label %s=%s", k, v)) + } + l = append(l, fmt.Sprintf("--node-label %s=%s", "kloudlite.io/public-ip", string(out))) + return l + }() + + // fmt.Println(labels) + + // "hostname": node.NodeId, + // "labels": strings.Join(labels, ","), + //check is node ready + count := 0 + + for { + if e := execCmd( + fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s ls", + fmt.Sprintf("%v/access", d.SSHPath), + string(out)), + "checking if node is ready"); e == nil { + break + } + + count++ + if count > 24 { + return fmt.Errorf("node is not ready even after 6 minutes") + } + time.Sleep(time.Second * 15) + } + + if err = execCmd(fmt.Sprintf("kubectl get node %s", node.NodeId), "checking if node attached"); err == nil { + fmt.Println("node already attached. clean exit") + return nil + } + + // // install k3s + // if e := execCmd( + // fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s sudo sh /tmp/k3s-install.sh", + // fmt.Sprintf("%v/access", d.SSHPath), string(out)), + // ""); e != nil { + // return e + // } + + // attach node + if e := execCmd( + fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s sudo sh /tmp/k3s-install.sh agent --server %s --token %s %s --node-name %s --node-external-ip %s", + fmt.Sprintf("%v/access", d.SSHPath), string(out), sec.EndpointUrl, sec.JoinToken, + strings.Join(labels, " "), node.NodeId, string(out)), + "attaching to cluster"); e != nil { + return e + } + + count = 0 + for { + if err = execCmd(fmt.Sprintf("kubectl get node %s", node.NodeId), "checking if node attached"); err == nil { + fmt.Println("node attached successfully.") + break + } + + count++ + if count > 24 { + return fmt.Errorf("node not attached even after 6minutes") + } + time.Sleep(time.Second * 15) + } + + return nil +} + +// UnattachNode implements doProviderClient +func (d *doProvider) UnattachNode(node DoNode) error { + var out []byte + var err error + + if out, err = getOutput(path.Join(d.getFolder(node.Region, node.NodeId), d.providerDir), "node-name"); err != nil { + return err + } else if strings.TrimSpace(string(out)) == "" { + fmt.Println("something went wrong, can't find node_name") + return nil + } + + if err = execCmd(fmt.Sprintf("kubectl get node %s", out), "checking if node attached"); err != nil { + fmt.Println("node not found may be already deleted") + return nil + } + + // drain node + if err = execCmd(fmt.Sprintf("kubectl taint nodes %s force=delete:NoExecute", node.NodeId), "drain node to delete"); err != nil { + return err + } + + fmt.Println("[#] waiting 10 seconds after drain") + time.Sleep(time.Second * 10) + + // delete node + return execCmd(fmt.Sprintf("kubectl delete node %s", out), + "delete node from cluster") +} + +type DoProvider struct { + ApiToken string + AccountId string +} + +type DoProviderEnv struct { + StorePath string + TfTemplates string + + SSHPath string + Secrets string + Labels map[string]string + Taints []string +} + +func NewDOProvider(provider DoProvider, p DoProviderEnv) doProviderClient { + return &doProvider{ + secrets: p.Secrets, + apiToken: provider.ApiToken, + accountId: provider.AccountId, + providerDir: "do", + storePath: p.StorePath, + tfTemplates: p.TfTemplates, + labels: p.Labels, + taints: p.Taints, + SSHPath: p.SSHPath, + } +} diff --git a/apps/nodectrl/internal/domain/terraform-utils/interface.go b/apps/nodectrl/internal/domain/terraform-utils/interface.go new file mode 100644 index 000000000..8e3cf7259 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/interface.go @@ -0,0 +1,9 @@ +package terraformutils + +type ProviderClient interface { + NewNode() error + DeleteNode() error + + AttachNode() error + UnattachNode() error +} diff --git a/apps/nodectrl/internal/domain/terraform-utils/main.go b/apps/nodectrl/internal/domain/terraform-utils/main.go new file mode 100644 index 000000000..801047eea --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/main.go @@ -0,0 +1,3 @@ +package terraformutils + + diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/aws/init.sh b/apps/nodectrl/internal/domain/terraform-utils/terraform/aws/init.sh new file mode 100644 index 000000000..fa313848b --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/terraform/aws/init.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cat >> /root/.ssh/authorized_keys << EOF +${pubkey} +EOF + +echo "curl -sfL https://get.k3s.io | sh -s - \$@" > /tmp/k3s-install.sh && chomod +x /tmp/k3s-install.sh diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/aws/resource.tf b/apps/nodectrl/internal/domain/terraform-utils/terraform/aws/resource.tf new file mode 100644 index 000000000..e1ec3ae56 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/terraform/aws/resource.tf @@ -0,0 +1,161 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.16" + } + } + + required_version = ">= 1.2.0" +} + +provider "aws" { + region = var.region + access_key = var.access_key + secret_key = var.secret_key +} + + +data "aws_ami" "latest-ubuntu" { + most_recent = true + owners = ["099720109477"] # Canonical + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } +} + + +resource "aws_security_group" "sg" { + # name = "${var.node_id}-sg" + + ingress { + from_port = 22 + protocol = "tcp" + to_port = 22 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 6443 + protocol = "tcp" + to_port = 6443 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 8472 + protocol = "udp" + to_port = 8472 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 9100 + protocol = "tcp" + to_port = 9100 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 51820 + protocol = "udp" + to_port = 51820 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 51821 + protocol = "udp" + to_port = 51821 + cidr_blocks = ["0.0.0.0/0"] + } + + + ingress { + from_port = 10250 + protocol = "tcp" + to_port = 10250 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 80 + protocol = "tcp" + to_port = 80 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 443 + protocol = "tcp" + to_port = 443 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 30000 + protocol = "tcp" + to_port = 32768 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 30000 + protocol = "udp" + to_port = 32768 + cidr_blocks = ["0.0.0.0/0"] + } + + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + # lifecycle { + # create_before_destroy = true + # } +} + + + +resource "aws_instance" "byoc-node" { + ami = var.ami == "" ? "${data.aws_ami.latest-ubuntu.id}" : var.ami + instance_type = var.instance_type + security_groups = [aws_security_group.sg.name] + + user_data = templatefile("./init.sh", { + pubkey = file("${var.keys-path}/access.pub") + # hostname = var.node_id + }) + + tags = { + Name = var.node_id + } + + root_block_device { + volume_size = 100 # in GB <<----- I increased this! + volume_type = "standard" + encrypted = false + # kms_key_id = data.aws_kms_key.customer_master_key.arn + } + +} + + +output "node-ip" { + value = aws_instance.byoc-node.public_ip +} + +output "node-name" { + value = var.node_id +} diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/aws/variables.tf b/apps/nodectrl/internal/domain/terraform-utils/terraform/aws/variables.tf new file mode 100644 index 000000000..456101f16 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/terraform/aws/variables.tf @@ -0,0 +1,31 @@ +variable "access_key" { + default = "" +} + +variable "secret_key" { + default = "" +} + +variable "region" { + default = "" +} + +variable "node_id" { + default = "" +} + +variable "instance_type" { + default = "" +} + +variable "pubkey" { + default = "" +} + +variable "keys-path" { + default = "" +} + +variable "ami" { + default = "" +} diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/do/init.sh b/apps/nodectrl/internal/domain/terraform-utils/terraform/do/init.sh new file mode 100644 index 000000000..e0a23d369 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/terraform/do/init.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cat > /root/.ssh/authorized_keys << EOF +${pubkey} +EOF + +echo "curl -sfL https://get.k3s.io | sh -s - \$@" > /tmp/k3s-install.sh && chomod +x /tmp/k3s-install.sh diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/do/resource.tf b/apps/nodectrl/internal/domain/terraform-utils/terraform/do/resource.tf new file mode 100644 index 000000000..792dfdea0 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/terraform/do/resource.tf @@ -0,0 +1,36 @@ +terraform { + required_providers { + digitalocean = { + source = "digitalocean/digitalocean" + version = "2.22.3" + } + tls = { + source = "hashicorp/tls" + version = "4.0.3" + } + } +} + +provider "digitalocean" { + token = var.do-token +} + +resource "digitalocean_droplet" "byoc-node" { + image = var.do-image-id + name = var.nodeId + region = var.region + size = var.size + ssh_keys = var.ssh_keys + user_data = templatefile("./init.sh", { + pubkey = file("${var.keys-path}/access.pub") + }) + +} + +output "node-ip" { + value = digitalocean_droplet.byoc-node.ipv4_address +} + +output "node-name" { + value = digitalocean_droplet.byoc-node.name +} diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/do/variables.tf b/apps/nodectrl/internal/domain/terraform-utils/terraform/do/variables.tf new file mode 100644 index 000000000..f78fac56b --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/terraform/do/variables.tf @@ -0,0 +1,40 @@ +variable "cluster-id" { + default = "kl" +} + +variable "do-token" { + default = "" +} + +variable "accountId" { + default = "" +} + +variable "nodeId" { + default = "" +} + +variable "size" { + default = "s-4vcpu-8gb" +} + +variable "region" { + default = "blr1" +} + +variable "keys-path" { + # default = "" +} + +# variable "pubkey" { +# # default = "" +# } + +variable "do-image-id" { + default = "ubuntu-22-10-x64" + # default = "105910703" +} + +variable "ssh_keys" { + default = ["25:d8:56:2b:70:15:43:c5:dd:e2:ff:d7:47:1b:68:22"] +} diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/e b/apps/nodectrl/internal/domain/terraform-utils/terraform/e new file mode 100644 index 000000000..fda47e4f0 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/terraform/e @@ -0,0 +1 @@ +export SECRETS=***REMOVED*** diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/secrets.yml b/apps/nodectrl/internal/domain/terraform-utils/terraform/secrets.yml new file mode 100644 index 000000000..8c5f7afb0 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/terraform/secrets.yml @@ -0,0 +1,2 @@ +joinToken: ***REMOVED*** +endPointUrl: ***REMOVED*** diff --git a/apps/nodectrl/internal/domain/terraform-utils/types.go b/apps/nodectrl/internal/domain/terraform-utils/types.go new file mode 100644 index 000000000..8e3cf7259 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform-utils/types.go @@ -0,0 +1,9 @@ +package terraformutils + +type ProviderClient interface { + NewNode() error + DeleteNode() error + + AttachNode() error + UnattachNode() error +} diff --git a/apps/nodectrl/internal/domain/terraform/aws/init.sh b/apps/nodectrl/internal/domain/terraform/aws/init.sh new file mode 100644 index 000000000..fa313848b --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform/aws/init.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cat >> /root/.ssh/authorized_keys << EOF +${pubkey} +EOF + +echo "curl -sfL https://get.k3s.io | sh -s - \$@" > /tmp/k3s-install.sh && chomod +x /tmp/k3s-install.sh diff --git a/apps/nodectrl/internal/domain/terraform/aws/resource.tf b/apps/nodectrl/internal/domain/terraform/aws/resource.tf new file mode 100644 index 000000000..e1ec3ae56 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform/aws/resource.tf @@ -0,0 +1,161 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.16" + } + } + + required_version = ">= 1.2.0" +} + +provider "aws" { + region = var.region + access_key = var.access_key + secret_key = var.secret_key +} + + +data "aws_ami" "latest-ubuntu" { + most_recent = true + owners = ["099720109477"] # Canonical + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } +} + + +resource "aws_security_group" "sg" { + # name = "${var.node_id}-sg" + + ingress { + from_port = 22 + protocol = "tcp" + to_port = 22 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 6443 + protocol = "tcp" + to_port = 6443 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 8472 + protocol = "udp" + to_port = 8472 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 9100 + protocol = "tcp" + to_port = 9100 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 51820 + protocol = "udp" + to_port = 51820 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 51821 + protocol = "udp" + to_port = 51821 + cidr_blocks = ["0.0.0.0/0"] + } + + + ingress { + from_port = 10250 + protocol = "tcp" + to_port = 10250 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 80 + protocol = "tcp" + to_port = 80 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 443 + protocol = "tcp" + to_port = 443 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 30000 + protocol = "tcp" + to_port = 32768 + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 30000 + protocol = "udp" + to_port = 32768 + cidr_blocks = ["0.0.0.0/0"] + } + + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + # lifecycle { + # create_before_destroy = true + # } +} + + + +resource "aws_instance" "byoc-node" { + ami = var.ami == "" ? "${data.aws_ami.latest-ubuntu.id}" : var.ami + instance_type = var.instance_type + security_groups = [aws_security_group.sg.name] + + user_data = templatefile("./init.sh", { + pubkey = file("${var.keys-path}/access.pub") + # hostname = var.node_id + }) + + tags = { + Name = var.node_id + } + + root_block_device { + volume_size = 100 # in GB <<----- I increased this! + volume_type = "standard" + encrypted = false + # kms_key_id = data.aws_kms_key.customer_master_key.arn + } + +} + + +output "node-ip" { + value = aws_instance.byoc-node.public_ip +} + +output "node-name" { + value = var.node_id +} diff --git a/apps/nodectrl/internal/domain/terraform/aws/variables.tf b/apps/nodectrl/internal/domain/terraform/aws/variables.tf new file mode 100644 index 000000000..456101f16 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform/aws/variables.tf @@ -0,0 +1,31 @@ +variable "access_key" { + default = "" +} + +variable "secret_key" { + default = "" +} + +variable "region" { + default = "" +} + +variable "node_id" { + default = "" +} + +variable "instance_type" { + default = "" +} + +variable "pubkey" { + default = "" +} + +variable "keys-path" { + default = "" +} + +variable "ami" { + default = "" +} diff --git a/apps/nodectrl/internal/domain/terraform/do/init.sh b/apps/nodectrl/internal/domain/terraform/do/init.sh new file mode 100644 index 000000000..e0a23d369 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform/do/init.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cat > /root/.ssh/authorized_keys << EOF +${pubkey} +EOF + +echo "curl -sfL https://get.k3s.io | sh -s - \$@" > /tmp/k3s-install.sh && chomod +x /tmp/k3s-install.sh diff --git a/apps/nodectrl/internal/domain/terraform/do/resource.tf b/apps/nodectrl/internal/domain/terraform/do/resource.tf new file mode 100644 index 000000000..792dfdea0 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform/do/resource.tf @@ -0,0 +1,36 @@ +terraform { + required_providers { + digitalocean = { + source = "digitalocean/digitalocean" + version = "2.22.3" + } + tls = { + source = "hashicorp/tls" + version = "4.0.3" + } + } +} + +provider "digitalocean" { + token = var.do-token +} + +resource "digitalocean_droplet" "byoc-node" { + image = var.do-image-id + name = var.nodeId + region = var.region + size = var.size + ssh_keys = var.ssh_keys + user_data = templatefile("./init.sh", { + pubkey = file("${var.keys-path}/access.pub") + }) + +} + +output "node-ip" { + value = digitalocean_droplet.byoc-node.ipv4_address +} + +output "node-name" { + value = digitalocean_droplet.byoc-node.name +} diff --git a/apps/nodectrl/internal/domain/terraform/do/variables.tf b/apps/nodectrl/internal/domain/terraform/do/variables.tf new file mode 100644 index 000000000..f78fac56b --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform/do/variables.tf @@ -0,0 +1,40 @@ +variable "cluster-id" { + default = "kl" +} + +variable "do-token" { + default = "" +} + +variable "accountId" { + default = "" +} + +variable "nodeId" { + default = "" +} + +variable "size" { + default = "s-4vcpu-8gb" +} + +variable "region" { + default = "blr1" +} + +variable "keys-path" { + # default = "" +} + +# variable "pubkey" { +# # default = "" +# } + +variable "do-image-id" { + default = "ubuntu-22-10-x64" + # default = "105910703" +} + +variable "ssh_keys" { + default = ["25:d8:56:2b:70:15:43:c5:dd:e2:ff:d7:47:1b:68:22"] +} diff --git a/apps/nodectrl/internal/domain/terraform/e b/apps/nodectrl/internal/domain/terraform/e new file mode 100644 index 000000000..fda47e4f0 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform/e @@ -0,0 +1 @@ +export SECRETS=***REMOVED*** diff --git a/apps/nodectrl/internal/domain/terraform/secrets.yml b/apps/nodectrl/internal/domain/terraform/secrets.yml new file mode 100644 index 000000000..8c5f7afb0 --- /dev/null +++ b/apps/nodectrl/internal/domain/terraform/secrets.yml @@ -0,0 +1,2 @@ +joinToken: ***REMOVED*** +endPointUrl: ***REMOVED*** diff --git a/apps/nodectrl/internal/domain/utils/common.go b/apps/nodectrl/internal/domain/utils/common.go new file mode 100644 index 000000000..4a45630c6 --- /dev/null +++ b/apps/nodectrl/internal/domain/utils/common.go @@ -0,0 +1,125 @@ +package utils + +import ( + "encoding/csv" + "encoding/json" + "fmt" + "os" + "os/exec" + "strings" +) + +func ExecCmd(cmdString string, logStr string) error { + r := csv.NewReader(strings.NewReader(cmdString)) + r.Comma = ' ' + cmdArr, err := r.Read() + if err != nil { + return err + } + + if logStr != "" { + fmt.Printf("[#] %s\n", logStr) + } else { + fmt.Printf("[#] %s\n", strings.Join(cmdArr, " ")) + } + + cmd := exec.Command(cmdArr[0], cmdArr[1:]...) + cmd.Stderr = os.Stderr + // cmd.Stdout = os.Stdout + + if err := cmd.Run(); err != nil { + fmt.Printf("err occurred: %s\n", err.Error()) + return err + } + return nil +} + +const ( + CLUSTER_ID = "kl" +) + +// rmTFdir implements doProviderClient +// func rmdir(folder string) error { +// return execCmd(fmt.Sprintf("rm -rf %q", folder), "") +// } + +// makeTFdir implements doProviderClient +func Mkdir(folder string) error { + return ExecCmd(fmt.Sprintf("mkdir -p %q", folder), "mkdir ") +} + +// destroyNode implements doProviderClient +func DestroyNode(folder string, values map[string]string) error { + vars := []string{"destroy", "-auto-approve"} + + for k, v := range values { + vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) + } + + cmd := exec.Command("terraform", vars...) + cmd.Dir = folder + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err := cmd.Run() + if err != nil { + fmt.Println(err) + return err + } + + return os.RemoveAll(folder) + + // return err +} + +func getOutput(folder, key string) ([]byte, error) { + vars := []string{"output", "-json"} + fmt.Printf("[#] terraform %s\n", strings.Join(vars, " ")) + cmd := exec.Command("terraform", vars...) + cmd.Dir = folder + + // cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + out, err := cmd.Output() + if err != nil { + return nil, err + + } + + // fmt.Println(string(out)) + + var resp map[string]struct { + Value string `json:"value"` + } + + err = json.Unmarshal(out, &resp) + if err != nil { + return nil, err + } + + return []byte(resp[key].Value), nil +} + +// applyTF implements doProviderClient +func ApplyTF(folder string, values map[string]string) error { + + vars := []string{"apply", "-auto-approve"} + + fmt.Printf("[#] terraform %s", strings.Join(vars, " ")) + + for k, v := range values { + vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) + } + + cmd := exec.Command("terraform", vars...) + cmd.Dir = folder + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + cmd.Dir = folder + + return cmd.Run() +} diff --git a/apps/nodectrl/internal/domain/utils/dump.go b/apps/nodectrl/internal/domain/utils/dump.go new file mode 100644 index 000000000..73fab6859 --- /dev/null +++ b/apps/nodectrl/internal/domain/utils/dump.go @@ -0,0 +1,127 @@ +package utils + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + "time" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/gridfs" + "go.mongodb.org/mongo-driver/mongo/options" +) + +func Upload(bucket *gridfs.Bucket) error { + fileName := "ram.zip" + file, err := os.Open(fileName) + if err != nil { + return err + } + + uploadOpts := options.GridFSUpload().SetMetadata(bson.D{{"metadata tag", "first"}}) + objectID, err := bucket.UploadFromStream(fileName, io.Reader(file), + uploadOpts) + if err != nil { + return err + } + fmt.Printf("New file uploaded with ID %s", objectID) + return nil + +} + +func getFiles(bucket *gridfs.Bucket) error { + filter := bson.D{{}} + cursor, err := bucket.Find(filter) + if err != nil { + return err + } + type gridfsFile struct { + Id string `bson:"_id"` + Name string `bson:"filename"` + Length int64 `bson:"length"` + } + var foundFiles []gridfsFile + if err = cursor.All(context.TODO(), &foundFiles); err != nil { + panic(err) + } + for _, file := range foundFiles { + fmt.Printf("filename: %s, length: %d, id: %s\n", file.Name, file.Length, file.Id) + } + + return nil +} + +func Delete(bucket *gridfs.Bucket, id string) error { + _id, err := primitive.ObjectIDFromHex(id) + if err != nil { + return err + } + if err := bucket.Delete(_id); err != nil { + return err + } + return nil +} + +func Download(bucket *gridfs.Bucket, id string) error { + + _id, err := primitive.ObjectIDFromHex(id) + if err != nil { + return err + } + fileBuffer := bytes.NewBuffer(nil) + if _, err := bucket.DownloadToStream(_id, fileBuffer); err != nil { + panic(err) + } + + if err := os.WriteFile("out/hello.zip", fileBuffer.Bytes(), os.ModePerm); err != nil { + return err + } + return nil + +} + +func Update(bucket *gridfs.Bucket, id string) error { + if err := Delete(bucket, id); err != nil { + return err + } + // bucket.GetChunksCollection().UpdateByID(ctx, id , update , opts ...*options.UpdateOptions) + return nil +} + +func M() error { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017")) + + if err != nil { + return err + } + + db := client.Database("myfiles") + bucket, err := gridfs.NewBucket(db) + if err != nil { + return err + } + + // if err := Upload(bucket); err != nil { + // return err + // } + + if err := getFiles(bucket); err != nil { + return err + } + + if err := Download(bucket, "6475cdb55687ddf79ce71c6b"); err != nil { + return err + } + + return nil +} + +func DumpSample() error { + return M() +} diff --git a/apps/nodectrl/internal/domain/utils/zipper.go b/apps/nodectrl/internal/domain/utils/zipper.go new file mode 100644 index 000000000..5a3754427 --- /dev/null +++ b/apps/nodectrl/internal/domain/utils/zipper.go @@ -0,0 +1,190 @@ +package utils + +import ( + "archive/zip" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "strings" + + "github.com/otiai10/copy" +) + +func ZipSource(source, target string) error { + // 1. Create a ZIP file and zip.Writer + f, err := os.Create(target) + if err != nil { + return err + } + defer f.Close() + + writer := zip.NewWriter(f) + defer writer.Close() + + // 2. Go through all the files of the source + return filepath.Walk(source, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // 3. Create a local file header + header, err := zip.FileInfoHeader(info) + if err != nil { + return err + } + + // set compression + header.Method = zip.Deflate + + // 4. Set relative path of a file as the header name + header.Name, err = filepath.Rel(filepath.Dir(source), path) + if err != nil { + return err + } + if info.IsDir() { + header.Name += "/" + } + + // 5. Create writer for the file header and save content of the file + headerWriter, err := writer.CreateHeader(header) + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + + _, err = io.Copy(headerWriter, f) + return err + }) +} + +func Unzip(src string, destination string) ([]string, error) { + + var filenames []string + r, err := zip.OpenReader(src) + + if err != nil { + return filenames, err + } + defer r.Close() + + for _, f := range r.File { + // Store "path/filename" for returning and using later on + fpath := filepath.Join(destination, f.Name) + + // Checking for any invalid file paths + if !strings.HasPrefix(fpath, filepath.Clean(destination)+string(os.PathSeparator)) { + return filenames, fmt.Errorf("%s is an illegal filepath", fpath) + } + + filenames = append(filenames, fpath) + + if f.FileInfo().IsDir() { + + os.MkdirAll(fpath, os.ModePerm) + continue + } + + if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { + return filenames, err + } + + outFile, err := os.OpenFile(fpath, + os.O_WRONLY|os.O_CREATE|os.O_TRUNC, + f.Mode()) + + if err != nil { + return filenames, err + } + + rc, err := f.Open() + if err != nil { + return filenames, err + } + + _, err = io.Copy(outFile, rc) + + outFile.Close() + rc.Close() + + if err != nil { + return filenames, err + } + } + + return filenames, nil +} + +func ExtractZip(src, destination string) error { + if _, err := os.Stat(destination); err == nil { + if er := os.RemoveAll(destination); er != nil { + return err + } + } + + if _, err := os.Stat(src); err != nil { + if e := os.Mkdir(destination, os.ModePerm); e != nil { + return e + } + } else { + + tempDirName, err := ioutil.TempDir("/tmp", "zip_") + if err != nil { + return err + } + defer os.RemoveAll(tempDirName) + + if names, err := Unzip(src, tempDirName); err != nil { + return err + } else { + fmt.Println(names) + if err := copy.Copy(path.Join(tempDirName, destination), destination); err != nil { + return err + } + + } + + } + + return nil +} + +func MutateOperation() error { + + file, err := ioutil.TempFile("out", "prefix_") + if err != nil { + return err + } + + return os.WriteFile(file.Name(), []byte("hi"), os.ModePerm) +} + +func ZipSample() error { + zipName, dirName := "ram.zip", "out" + if err := ExtractZip(zipName, dirName); err != nil { + return err + } + + if err := MutateOperation(); err != nil { + return err + } + + defer func() { + if err := ZipSource(dirName, zipName); err != nil { + log.Fatal(err) + } + }() + return nil +} diff --git a/apps/nodectrl/internal/env/env.go b/apps/nodectrl/internal/env/env.go new file mode 100644 index 000000000..b5fc71e03 --- /dev/null +++ b/apps/nodectrl/internal/env/env.go @@ -0,0 +1,27 @@ +package env + +import "github.com/codingconcepts/env" + +type Env struct { + CloudProvider string `env:"CLOUD_PROVIDER" required:"true"` + Action string `env:"ACTION" required:"true"` + + NodeConfig string `env:"NODE_CONFIG" required:"true"` + ProviderConfig string `env:"PROVIDER_CONFIG" required:"true"` + + AWSProviderConfig string `env:"AWS_CONFIG"` + GCPProviderConfig string `env:"GCP_CONFIG"` + AzureProviderConfig string `env:"AZURE_CONFIG"` + DoProviderConfig string `env:"DO_CONFIG"` + + DBUrl string `env:"DB_URL" required:"true"` + DBName string `env:"DB_NAME" required:"true"` +} + +func LoadEnv() (*Env, error) { + var e Env + if err := env.Set(&e); err != nil { + return nil, err + } + return &e, nil +} diff --git a/apps/nodectrl/internal/framework/main.go b/apps/nodectrl/internal/framework/main.go index 7dd720d14..bffad1d19 100644 --- a/apps/nodectrl/internal/framework/main.go +++ b/apps/nodectrl/internal/framework/main.go @@ -1,24 +1,10 @@ package framework import ( - "fmt" - "os" - "go.uber.org/fx" "kloudlite.io/apps/nodectrl/internal/app" - "kloudlite.io/apps/nodectrl/internal/domain" ) -var Module = fx.Module( - "framework", +var Module = fx.Module("framework", app.Module, - fx.Invoke(func(d domain.Domain, shutdowner fx.Shutdowner) { - err := d.StartJob() - if err != nil { - fmt.Println(err) - os.Exit(1) - } else { - shutdowner.Shutdown() - } - }), ) diff --git a/apps/nodectrl/main.go b/apps/nodectrl/main.go index 64ad2e0a2..02ddc0276 100644 --- a/apps/nodectrl/main.go +++ b/apps/nodectrl/main.go @@ -4,6 +4,7 @@ import ( "flag" "go.uber.org/fx" + "kloudlite.io/apps/nodectrl/internal/env" "kloudlite.io/apps/nodectrl/internal/framework" "kloudlite.io/pkg/logging" ) @@ -13,13 +14,13 @@ func main() { flag.BoolVar(&isDev, "dev", false, "--dev") flag.Parse() fx.New( - framework.Module, + fx.Provide(env.LoadEnv), + // fx.NopLogger, fx.Provide( func() (logging.Logger, error) { - return logging.New(&logging.Options{Name: "auth", Dev: isDev}) + return logging.New(&logging.Options{Name: "nodectrl", Dev: isDev}) }, ), - // fx.NopLogger, + framework.Module, ).Run() } - diff --git a/apps/nodectrl/.dockerignore b/apps/nodectrl2/.dockerignore similarity index 100% rename from apps/nodectrl/.dockerignore rename to apps/nodectrl2/.dockerignore diff --git a/apps/nodectrl2/Dockerfile b/apps/nodectrl2/Dockerfile new file mode 100644 index 000000000..57b0cf83b --- /dev/null +++ b/apps/nodectrl2/Dockerfile @@ -0,0 +1,29 @@ +# syntax=docker/dockerfile:1.4 +FROM golang:1.18.3-alpine3.16 AS base +USER 1001 +ENV GOPATH=/tmp/go +ENV GOCACHE=/tmp/go-cache +WORKDIR /tmp/app +COPY --chown=1001 --from=project-root ./go.mod ./go.sum ./tools.go ./ +RUN go mod download -x +COPY --chown=1001 --from=project-root pkg ./pkg +ARG APP +RUN mkdir -p ./apps/$APP +WORKDIR /tmp/app/apps/$APP +COPY --chown=1001 ./ ./ +RUN CGO_ENABLED=0 go build -tags musl -o /tmp/bin/$APP ./main.go +RUN chmod +x /tmp/bin/$APP + +#FROM gcr.io/distroless/static-debian11 +FROM alpine +RUN adduser -D -h /home/nonroot nonroot +RUN apk add curl git openssh-client +RUN cd bin && curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && chmod +x kubectl +RUN cd bin && curl -o tf.zip https://releases.hashicorp.com/terraform/1.2.9/terraform_1.2.9_linux_amd64.zip && unzip tf.zip +# RUN cd bin && curl -L0 -o talosctl https://github.com/siderolabs/talos/releases/download/v1.2.3/talosctl-linux-amd64 && chmod +x talosctl +USER nonroot +WORKDIR /tmp/app +COPY --chown=nonroot --from=base /tmp/bin/nodectrl ./nodectrl +COPY --chown=nonroot --from=project-root ./pkg/infraClient/terraform /templates/terraform +# COPY --chown=nonroot --from=project-root ./pkg/infraClient/templates /tmp/app/pkg/infraClient/templates +CMD ["./nodectrl"] diff --git a/apps/nodectrl2/Taskfile.yml b/apps/nodectrl2/Taskfile.yml new file mode 100644 index 000000000..c19312618 --- /dev/null +++ b/apps/nodectrl2/Taskfile.yml @@ -0,0 +1,27 @@ +version: "3" + +dotenv: + - .secrets/env + +tasks: + run: + sources: + - ./internal/**/*.go + - ./main.go + cmds: + # - go run -tags dynamic main.go --dev + - nodemon -e go --signal SIGKILL --exec 'go run main.go --dev || exit 1' + + docker-build: + vars: + APP: nodectrl + IMAGE: registry.kloudlite.io/kloudlite/{{.EnvName}}/{{.APP}}:{{.Tag}} + preconditions: + - sh: '[[ -n "{{.Tag}}" ]]' + msg: 'var Tag must have a value' + + - sh: '[[ "{{.EnvName}}" == "development" ]] || [[ "{{.EnvName}}" == "staging" ]] || [[ "{{.EnvName}}" == "production" ]]' + msg: 'var EnvName must have one of [development, staging, production] as its value' + cmds: + - docker buildx build -f ./Dockerfile -t {{.IMAGE}} . --build-arg APP={{.APP}} --platform linux/amd64 --build-context project-root=../.. + - docker push {{.IMAGE}} diff --git a/apps/nodectrl2/internal/app/main.go b/apps/nodectrl2/internal/app/main.go new file mode 100644 index 000000000..dec13f138 --- /dev/null +++ b/apps/nodectrl2/internal/app/main.go @@ -0,0 +1,11 @@ +package app + +import ( + "go.uber.org/fx" + "kloudlite.io/apps/nodecontroller/internal/domain" +) + +var Module = fx.Module( + "app", + domain.Module, +) diff --git a/apps/nodectrl2/internal/domain/aws.go b/apps/nodectrl2/internal/domain/aws.go new file mode 100644 index 000000000..4b324380c --- /dev/null +++ b/apps/nodectrl2/internal/domain/aws.go @@ -0,0 +1,103 @@ +package domain + +import ( + "encoding/base64" + "encoding/json" + "errors" + "fmt" + + "gopkg.in/yaml.v3" + infraclient "kloudlite.io/pkg/infraClient" +) + +type awsConfig struct { + Version string `yaml:"version"` + Action string `yaml:"action"` + Provider string `yaml:"provider"` + Spec struct { + Provider struct { + AccessKey string `yaml:"accessKey"` + AccessSecret string `yaml:"accessSecret"` + AccountId string `yaml:"accountId"` + } `yaml:"provider"` + Node struct { + Region string `yaml:"region"` + InstanceType string `yaml:"instanceType"` + NodeId string `yaml:"nodeId"` + VPC string `yaml:"vpc"` + } `yaml:"node"` + } `yaml:"spec"` +} + +func (d *domainI) doWithAWS() error { + + out, err := base64.StdEncoding.DecodeString(d.env.Config) + if err != nil { + return err + } + var awsConf awsConfig + e := yaml.Unmarshal(out, &awsConf) + if e != nil { + return e + } + klConf, err := d.getKlConf() + if err != nil { + return err + } + + labels := map[string]string{} + if e := json.Unmarshal([]byte(d.env.Labels), &labels); e != nil { + fmt.Println(e) + } + + taints := []string{} + if e := json.Unmarshal([]byte(d.env.Taints), &taints); e != nil { + fmt.Println(e) + } + + awsProvider := infraclient.NewAWSProvider(infraclient.AWSProvider{ + AccessKey: awsConf.Spec.Provider.AccessKey, + AccessSecret: awsConf.Spec.Provider.AccessSecret, + AccountId: awsConf.Spec.Provider.AccountId, + }, infraclient.AWSProviderEnv{ + StorePath: klConf.Values.StorePath, + TfTemplates: klConf.Values.TfTemplates, + Labels: labels, + Taints: taints, + Secrets: klConf.Values.Secrets, + SSHPath: klConf.Values.SSHPath, + }) + + awsNode := infraclient.AWSNode{ + NodeId: awsConf.Spec.Node.NodeId, + Region: awsConf.Spec.Node.Region, + InstanceType: awsConf.Spec.Node.InstanceType, + VPC: awsConf.Spec.Node.VPC, + } + + // return nil + + switch awsConf.Action { + case "create": + err = awsProvider.NewNode(awsNode) + if err != nil { + return err + } + err = awsProvider.AttachNode(awsNode) + if err != nil { + return err + } + + case "delete": + err = awsProvider.DeleteNode(awsNode) + + if err != nil { + return err + } + + default: + return errors.New("wrong action") + } + + return nil +} diff --git a/apps/nodectrl2/internal/domain/do.go b/apps/nodectrl2/internal/domain/do.go new file mode 100644 index 000000000..d0bea72b4 --- /dev/null +++ b/apps/nodectrl2/internal/domain/do.go @@ -0,0 +1,106 @@ +package domain + +import ( + "encoding/base64" + "encoding/json" + "errors" + "fmt" + + "gopkg.in/yaml.v3" + infraclient "kloudlite.io/pkg/infraClient" +) + +type doConfig struct { + Version string `yaml:"version"` + Action string `yaml:"action"` + Provider string `yaml:"provider"` + Spec struct { + Provider struct { + ApiToken string `yaml:"apiToken"` + AccountId string `yaml:"accountId"` + } `yaml:"provider"` + Node struct { + Region string `yaml:"region"` + Size string `yaml:"size"` + NodeId string `yaml:"nodeId"` + ImageId string `yaml:"imageId"` + } `yaml:"node"` + } `yaml:"spec"` +} + +func (d *domainI) doWithDO() error { + + out, err := base64.StdEncoding.DecodeString(d.env.Config) + if err != nil { + fmt.Println("here") + return err + } + + var doConf doConfig + e := yaml.Unmarshal(out, &doConf) + if e != nil { + return e + } + klConf, err := d.getKlConf() + if err != nil { + fmt.Println("here") + return err + } + + labels := map[string]string{} + if e := json.Unmarshal([]byte(d.env.Labels), &labels); e != nil { + fmt.Println(e) + } + + taints := []string{} + if e := json.Unmarshal([]byte(d.env.Taints), &taints); e != nil { + fmt.Println(e) + } + + doProvider := infraclient.NewDOProvider(infraclient.DoProvider{ + ApiToken: doConf.Spec.Provider.ApiToken, + AccountId: doConf.Spec.Provider.AccountId, + }, infraclient.DoProviderEnv{ + StorePath: klConf.Values.StorePath, + TfTemplates: klConf.Values.TfTemplates, + Secrets: klConf.Values.Secrets, + Labels: labels, + Taints: taints, + SSHPath: klConf.Values.SSHPath, + }) + + doNode := infraclient.DoNode{ + Region: doConf.Spec.Node.Region, + Size: doConf.Spec.Node.Size, + NodeId: doConf.Spec.Node.NodeId, + ImageId: doConf.Spec.Node.ImageId, + } + + // return nil + + switch doConf.Action { + case "create": + if err = doProvider.NewNode(doNode); err != nil { + return err + } + + if err = doProvider.AttachNode(doNode); err != nil { + return err + } + + case "delete": + + if err = doProvider.UnattachNode(doNode); err != nil { + return err + } + + if err = doProvider.DeleteNode(doNode); err != nil { + return err + } + + default: + return errors.New("wrong action") + } + + return nil +} diff --git a/apps/nodectrl2/internal/domain/main.go b/apps/nodectrl2/internal/domain/main.go new file mode 100644 index 000000000..2cb7dd50a --- /dev/null +++ b/apps/nodectrl2/internal/domain/main.go @@ -0,0 +1,91 @@ +package domain + +import ( + "encoding/base64" + "errors" + "fmt" + + "gopkg.in/yaml.v3" + "kloudlite.io/pkg/config" + + "go.uber.org/fx" +) + +type domainI struct { + env *Env +} + +type KLConf struct { + Version string `yaml:"version"` + Values struct { + StorePath string `yaml:"storePath"` + TfTemplates string `yaml:"tfTemplatesPath"` + Secrets string `yaml:"secrets"` + SSHPath string `yaml:"sshPath"` + PubKey string `yaml:"pubkey"` + } `yaml:"spec"` +} + +func (d *domainI) getKlConf() (*KLConf, error) { + out, err := base64.StdEncoding.DecodeString(d.env.KLConfig) + if err != nil { + fmt.Println("here") + return nil, err + } + + var klConf KLConf + e := yaml.Unmarshal(out, &klConf) + if e != nil { + + return nil, e + } + + return &klConf, nil +} + +// startJob implements Domain +func (d *domainI) StartJob() error { + + switch d.env.Provider { + case "do": + if err := d.doWithDO(); err != nil { + return err + } + + case "aws": + if err := d.doWithAWS(); err != nil { + return err + } + + default: + return errors.New("this type of provider not suported") + } + return nil +} + +func fxDomain(env *Env) Domain { + return &domainI{ + env: env, + } +} + +type Env struct { + Config string `env:"NODE_CONFIG" required:"true"` + Provider string `env:"PROVIDER" required:"true"` + KLConfig string `env:"KL_CONFIG" required:"true"` + Labels string `env:"LABELS" required:"true"` + Taints string `env:"TAINTS" required:"true"` +} + +var Module = fx.Module( + "domain", + config.EnvFx[Env](), + fx.Provide(fxDomain), +) + +/* +main + -> framework () + -> app () + -> domain (main logic) +*/ diff --git a/apps/nodectrl2/internal/domain/port.go b/apps/nodectrl2/internal/domain/port.go new file mode 100644 index 000000000..184633284 --- /dev/null +++ b/apps/nodectrl2/internal/domain/port.go @@ -0,0 +1,5 @@ +package domain + +type Domain interface { + StartJob() error +} diff --git a/apps/nodectrl2/internal/framework/main.go b/apps/nodectrl2/internal/framework/main.go new file mode 100644 index 000000000..7dd720d14 --- /dev/null +++ b/apps/nodectrl2/internal/framework/main.go @@ -0,0 +1,24 @@ +package framework + +import ( + "fmt" + "os" + + "go.uber.org/fx" + "kloudlite.io/apps/nodectrl/internal/app" + "kloudlite.io/apps/nodectrl/internal/domain" +) + +var Module = fx.Module( + "framework", + app.Module, + fx.Invoke(func(d domain.Domain, shutdowner fx.Shutdowner) { + err := d.StartJob() + if err != nil { + fmt.Println(err) + os.Exit(1) + } else { + shutdowner.Shutdown() + } + }), +) diff --git a/apps/nodectrl2/main.go b/apps/nodectrl2/main.go new file mode 100644 index 000000000..64ad2e0a2 --- /dev/null +++ b/apps/nodectrl2/main.go @@ -0,0 +1,25 @@ +package main + +import ( + "flag" + + "go.uber.org/fx" + "kloudlite.io/apps/nodectrl/internal/framework" + "kloudlite.io/pkg/logging" +) + +func main() { + var isDev bool + flag.BoolVar(&isDev, "dev", false, "--dev") + flag.Parse() + fx.New( + framework.Module, + fx.Provide( + func() (logging.Logger, error) { + return logging.New(&logging.Options{Name: "auth", Dev: isDev}) + }, + ), + // fx.NopLogger, + ).Run() +} + diff --git a/apps/nodectrl/task.md b/apps/nodectrl2/task.md similarity index 100% rename from apps/nodectrl/task.md rename to apps/nodectrl2/task.md diff --git a/go.mod b/go.mod index f401e6218..def330fbd 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/kloudlite/wg-operator v0.0.0-20230329090407-183297dc23b8 github.com/matoous/go-nanoid/v2 v2.0.0 github.com/miekg/dns v1.1.41 + github.com/otiai10/copy v1.11.0 github.com/pkg/errors v0.9.1 github.com/sendgrid/sendgrid-go v3.11.1+incompatible github.com/signintech/gopdf v0.12.0 @@ -95,7 +96,6 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/graphql-go/graphql v0.8.1 github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect diff --git a/go.sum b/go.sum index eb3cb8ca6..7c002a11c 100644 --- a/go.sum +++ b/go.sum @@ -257,8 +257,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graphql-go/graphql v0.8.1 h1:p7/Ou/WpmulocJeEx7wjQy611rtXGQaAcXGqanuMMgc= -github.com/graphql-go/graphql v0.8.1/go.mod h1:nKiHzRM0qopJEwCITUuIsxk9PlVlwIiiI8pnJEhordQ= github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -368,6 +366,9 @@ github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/openzipkin/zipkin-go v0.3.0 h1:XtuXmOLIXLjiU2XduuWREDT0LOKtSgos/g7i7RYyoZQ= +github.com/otiai10/copy v1.11.0 h1:OKBD80J/mLBrwnzXqGtFCzprFSGioo30JcmR4APsNwc= +github.com/otiai10/copy v1.11.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= +github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/phpdave11/gofpdi v1.0.11 h1:wsBNx+3S0wy1dEp6fzv281S74ogZGgIdYWV2PugWgho= github.com/phpdave11/gofpdi v1.0.11/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= From 62e9ba48eb2cf41e8d0d12bb00452af9d17221a1 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Wed, 31 May 2023 17:22:03 +0530 Subject: [PATCH 2/3] :construction: wip --- apps/nodectrl/internal/app/main.go | 40 ++++- apps/nodectrl/internal/domain/aws.go | 159 +++++++----------- apps/nodectrl/internal/domain/main.go | 36 +--- apps/nodectrl/internal/domain/port.go | 1 - .../internal/domain/provider-client-fx.go | 43 +++++ apps/nodectrl/internal/domain/utils/common.go | 26 +++ apps/nodectrl/internal/env/env.go | 10 +- apps/nodectrl/internal/framework/main.go | 17 +- apps/nodectrl/main.go | 2 +- pkg/mongo-gridfs/gridfs.go | 40 +++++ pkg/mongo-gridfs/main.go | 49 ++++++ 11 files changed, 277 insertions(+), 146 deletions(-) create mode 100644 apps/nodectrl/internal/domain/provider-client-fx.go create mode 100644 pkg/mongo-gridfs/gridfs.go create mode 100644 pkg/mongo-gridfs/main.go diff --git a/apps/nodectrl/internal/app/main.go b/apps/nodectrl/internal/app/main.go index 925673c10..bb403769e 100644 --- a/apps/nodectrl/internal/app/main.go +++ b/apps/nodectrl/internal/app/main.go @@ -1,23 +1,47 @@ package app import ( + "context" "fmt" - "os" "go.uber.org/fx" "kloudlite.io/apps/nodectrl/internal/domain" + "kloudlite.io/apps/nodectrl/internal/env" ) var Module = fx.Module("app", domain.Module, fx.Invoke( - func(d domain.Domain, shutdowner fx.Shutdowner) { - if err := d.StartJob(); err != nil { - fmt.Println(err) - os.Exit(1) - } else { - shutdowner.Shutdown() - } + func(env *env.Env, pc domain.ProviderClient, shutdowner fx.Shutdowner, lifecycle fx.Lifecycle) { + lifecycle.Append(fx.Hook{ + OnStart: func(context.Context) error { + switch env.Action { + case "create": + + fmt.Println("needs to create node") + if err := pc.NewNode(); err != nil { + return err + } + case "delete": + fmt.Println("needs to delete node") + if err := pc.DeleteNode(); err != nil { + return err + } + + case "": + return fmt.Errorf("ACTION not provided, supported actions {create, delete} ") + default: + return fmt.Errorf("not supported actions '%s' please provide one of supported action like { create, delete }", env.Action) + + } + shutdowner.Shutdown() + return nil + }, + OnStop: func(context.Context) error { + return nil + }, + }) + }, ), ) diff --git a/apps/nodectrl/internal/domain/aws.go b/apps/nodectrl/internal/domain/aws.go index 6532355ae..8275ff470 100644 --- a/apps/nodectrl/internal/domain/aws.go +++ b/apps/nodectrl/internal/domain/aws.go @@ -1,28 +1,22 @@ package domain import ( - "fmt" - "os" - "os/exec" - "path" - - "gopkg.in/yaml.v3" - "kloudlite.io/apps/nodectrl/internal/domain/utils" + mongogridfs "kloudlite.io/pkg/mongo-gridfs" ) type CommonProviderData struct { - StorePath string `yaml:"storePath"` + // StorePath string `yaml:"storePath"` TfTemplates string `yaml:"tfTemplates"` Labels map[string]string `yaml:"labels"` Taints []string `yaml:"taints"` - Secrets string `yaml:"secrets"` - SSHPath string `yaml:"sshPath"` + // Secrets string `yaml:"secrets"` + SSHPath string `yaml:"sshPath"` } -type awsProviderConfig struct { - accessKey string `yaml:"accessKey"` - accessSecret string `yaml:"accessSecret"` - accountId string `yaml:"accountId"` +type AwsProviderConfig struct { + AccessKey string `yaml:"accessKey"` + AccessSecret string `yaml:"accessSecret"` + AccountId string `yaml:"accountId"` } type AWSNode struct { @@ -34,6 +28,7 @@ type AWSNode struct { } type awsClient struct { + gfs mongogridfs.GridFs node AWSNode accessKey string @@ -49,30 +44,30 @@ type awsClient struct { taints []string } -// getFolder implements doProviderClient -func (a awsClient) getFolder() string { - // eg -> /path/acc_id/do/blr1/node_id/do - - return path.Join(a.storePath, a.accountId, a.providerDir, a.node.Region, a.node.NodeId) -} - -// initTFdir implements doProviderClient -func (d awsClient) initTFdir() error { - - folder := d.getFolder() - - if err := utils.ExecCmd(fmt.Sprintf("cp -r %s %s", fmt.Sprintf("%s/%s", d.tfTemplates, d.providerDir), folder), "initialize terraform"); err != nil { - return err - } - - cmd := exec.Command("terraform", "init") - cmd.Dir = path.Join(folder, d.providerDir) - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - return cmd.Run() -} +// // getFolder implements doProviderClient +// func (a awsClient) getFolder() string { +// // eg -> /path/acc_id/do/blr1/node_id/do +// +// return path.Join(a.storePath, a.accountId, a.providerDir, a.node.Region, a.node.NodeId) +// } + +// // initTFdir implements doProviderClient +// func (d awsClient) initTFdir() error { +// +// folder := d.getFolder() +// +// if err := utils.ExecCmd(fmt.Sprintf("cp -r %s %s", fmt.Sprintf("%s/%s", d.tfTemplates, d.providerDir), folder), "initialize terraform"); err != nil { +// return err +// } +// +// cmd := exec.Command("terraform", "init") +// cmd.Dir = path.Join(folder, d.providerDir) +// +// cmd.Stdout = os.Stdout +// cmd.Stderr = os.Stderr +// +// return cmd.Run() +// } // NewNode implements ProviderClient func (a awsClient) NewNode() error { @@ -88,21 +83,29 @@ func (a awsClient) NewNode() error { values["keys-path"] = a.SSHPath values["ami"] = a.node.ImageId - return nil + /* + steps: + - check if state present in db + - if present load that to working dir + - else initialize new tf dir + - apply terraform + - upload the final state with defer + */ // making dir - if err := utils.Mkdir(a.getFolder()); err != nil { - return err - } - - // initialize directory - if err := a.initTFdir(); err != nil { - return err - } - - // apply terraform - return utils.ApplyTF(path.Join(a.getFolder(), a.providerDir), values) - + // if err := utils.Mkdir(a.getFolder()); err != nil { + // return err + // } + // + // // initialize directory + // if err := a.initTFdir(); err != nil { + // return err + // } + // + // // apply terraform + // return utils.ApplyTF(path.Join(a.getFolder(), a.providerDir), values) + // + return nil } // AttachNode implements ProviderClientkkk @@ -120,61 +123,19 @@ func (awsClient) UnattachNode() error { panic("unimplemented") } -func NewAwsProviderClient(node AWSNode, cpd CommonProviderData, apc awsProviderConfig) ProviderClient { +func NewAwsProviderClient(node AWSNode, cpd CommonProviderData, apc AwsProviderConfig) ProviderClient { return awsClient{ node: node, - accessKey: apc.accessKey, - accessSecret: apc.accessSecret, - accountId: apc.accountId, + accessKey: apc.AccessKey, + accessSecret: apc.AccessSecret, + accountId: apc.AccountId, providerDir: "aws", - secrets: cpd.Secrets, - storePath: cpd.StorePath, + // secrets: cpd.Secrets, + // storePath: cpd.StorePath, tfTemplates: cpd.TfTemplates, labels: cpd.Labels, taints: cpd.Taints, SSHPath: cpd.SSHPath, } } - -func (d domain) StartAwsJob() error { - - node := AWSNode{} - - if err := yaml.Unmarshal([]byte(d.env.NodeConfig), &node); err != nil { - return err - } - - cpd := CommonProviderData{} - - if err := yaml.Unmarshal([]byte(d.env.ProviderConfig), &cpd); err != nil { - return err - } - - apc := awsProviderConfig{} - - if err := yaml.Unmarshal([]byte(d.env.AWSProviderConfig), &apc); err != nil { - return err - } - - pc := NewAwsProviderClient(node, cpd, apc) - - switch d.env.Action { - case "create": - fmt.Println("needs to create node") - if err := pc.NewNode(); err != nil { - return err - } - case "delete": - fmt.Println("needs to delete node") - if err := pc.DeleteNode(); err != nil { - return err - } - case "": - return fmt.Errorf("ACTION not provided, supported actions {create, delete} ") - default: - return fmt.Errorf("not supported actions '%s' please provide one of supported action like { create, delete }", d.env.Action) - } - fmt.Println("aws job started") - return nil -} diff --git a/apps/nodectrl/internal/domain/main.go b/apps/nodectrl/internal/domain/main.go index d87149052..afb8613fb 100644 --- a/apps/nodectrl/internal/domain/main.go +++ b/apps/nodectrl/internal/domain/main.go @@ -1,50 +1,24 @@ package domain import ( - "fmt" - "go.uber.org/fx" "kloudlite.io/apps/nodectrl/internal/env" + mongogridfs "kloudlite.io/pkg/mongo-gridfs" ) type domain struct { env *env.Env -} - -// StartJob implements Domain -func (d domain) StartJob() error { - switch d.env.CloudProvider { - case "aws": - if err := d.StartAwsJob(); err != nil { - return err - } - case "azure": - if err := d.StartAzureJob(); err != nil { - return err - } - case "do": - if err := d.StartDoJob(); err != nil { - return err - } - case "gcp": - if err := d.StartGCPJob(); err != nil { - return err - } - case "": - return fmt.Errorf("please provide cloud provider") - default: - return fmt.Errorf("provided cloud provider %s not available now", d.env.CloudProvider) - - } - return nil + gfs mongogridfs.GridFs } var Module = fx.Module("domain", fx.Provide( - func(env *env.Env) Domain { + func(env *env.Env, gfs mongogridfs.GridFs) Domain { return domain{ env: env, + gfs: gfs, } }, ), + ProviderClientFx, ) diff --git a/apps/nodectrl/internal/domain/port.go b/apps/nodectrl/internal/domain/port.go index 184633284..fa83c82de 100644 --- a/apps/nodectrl/internal/domain/port.go +++ b/apps/nodectrl/internal/domain/port.go @@ -1,5 +1,4 @@ package domain type Domain interface { - StartJob() error } diff --git a/apps/nodectrl/internal/domain/provider-client-fx.go b/apps/nodectrl/internal/domain/provider-client-fx.go new file mode 100644 index 000000000..378c60498 --- /dev/null +++ b/apps/nodectrl/internal/domain/provider-client-fx.go @@ -0,0 +1,43 @@ +package domain + +import ( + "go.uber.org/fx" + "kloudlite.io/apps/nodectrl/internal/domain/utils" + "kloudlite.io/apps/nodectrl/internal/env" +) + +var ProviderClientFx = fx.Module("provider-client-fx", + fx.Provide(func(env *env.Env) (ProviderClient, error) { + + cpd := CommonProviderData{} + + if err := utils.Base64YamlDecode(env.ProviderConfig, &cpd); err != nil { + return nil, err + } + + switch env.CloudProvider { + case "aws": + + node := AWSNode{} + + if err := utils.Base64YamlDecode(env.NodeConfig, &node); err != nil { + return nil, err + } + + apc := AwsProviderConfig{} + + if err := utils.Base64YamlDecode(env.AWSProviderConfig, &apc); err != nil { + return nil, err + } + + return NewAwsProviderClient(node, cpd, apc), nil + case "azure": + panic("not implemented") + case "do": + panic("not implemented") + case "gcp": + panic("not implemented") + } + return awsClient{}, nil + }), +) diff --git a/apps/nodectrl/internal/domain/utils/common.go b/apps/nodectrl/internal/domain/utils/common.go index 4a45630c6..7bfd6c020 100644 --- a/apps/nodectrl/internal/domain/utils/common.go +++ b/apps/nodectrl/internal/domain/utils/common.go @@ -1,14 +1,40 @@ package utils import ( + "encoding/base64" "encoding/csv" "encoding/json" "fmt" "os" "os/exec" "strings" + + "gopkg.in/yaml.v2" ) +func Base64YamlDecode(in string, out interface{}) error { + rawDecodedText, err := base64.StdEncoding.DecodeString(in) + if err != nil { + return err + } + + fmt.Println(string(rawDecodedText)) + + return yaml.Unmarshal(rawDecodedText, out) +} + +func ColorText(text interface{}, code int) string { + return fmt.Sprintf("\033[38;05;%dm%v\033[0m", code, text) +} + +func DownloadDir() error { + return nil +} + +func UploadDir() error { + return nil +} + func ExecCmd(cmdString string, logStr string) error { r := csv.NewReader(strings.NewReader(cmdString)) r.Comma = ' ' diff --git a/apps/nodectrl/internal/env/env.go b/apps/nodectrl/internal/env/env.go index b5fc71e03..bea846015 100644 --- a/apps/nodectrl/internal/env/env.go +++ b/apps/nodectrl/internal/env/env.go @@ -9,13 +9,13 @@ type Env struct { NodeConfig string `env:"NODE_CONFIG" required:"true"` ProviderConfig string `env:"PROVIDER_CONFIG" required:"true"` - AWSProviderConfig string `env:"AWS_CONFIG"` - GCPProviderConfig string `env:"GCP_CONFIG"` - AzureProviderConfig string `env:"AZURE_CONFIG"` - DoProviderConfig string `env:"DO_CONFIG"` - DBUrl string `env:"DB_URL" required:"true"` DBName string `env:"DB_NAME" required:"true"` + + AWSProviderConfig string `env:"AWS_PROVIDER_CONFIG"` + GCPProviderConfig string `env:"GCP_PROVIDER_CONFIG"` + AzureProviderConfig string `env:"AZURE_PROVIDER_CONFIG"` + DoProviderConfig string `env:"DO_PROVIDER_CONFIG"` } func LoadEnv() (*Env, error) { diff --git a/apps/nodectrl/internal/framework/main.go b/apps/nodectrl/internal/framework/main.go index bffad1d19..853d00a9f 100644 --- a/apps/nodectrl/internal/framework/main.go +++ b/apps/nodectrl/internal/framework/main.go @@ -3,8 +3,23 @@ package framework import ( "go.uber.org/fx" "kloudlite.io/apps/nodectrl/internal/app" + "kloudlite.io/apps/nodectrl/internal/env" + mongogridfs "kloudlite.io/pkg/mongo-gridfs" ) -var Module = fx.Module("framework", +type fm struct { + env *env.Env +} + +func (fm *fm) GetMongoConfig() (url string, dbName string) { + return fm.env.DBUrl, fm.env.DBName +} + +var Module = fx.Module( + "framework", + fx.Provide(func(env *env.Env) *fm { + return &fm{env} + }), + mongogridfs.NewMongoGridFsClientFx[*fm](), app.Module, ) diff --git a/apps/nodectrl/main.go b/apps/nodectrl/main.go index 02ddc0276..c8ee11abc 100644 --- a/apps/nodectrl/main.go +++ b/apps/nodectrl/main.go @@ -15,7 +15,7 @@ func main() { flag.Parse() fx.New( fx.Provide(env.LoadEnv), - // fx.NopLogger, + fx.NopLogger, fx.Provide( func() (logging.Logger, error) { return logging.New(&logging.Options{Name: "nodectrl", Dev: isDev}) diff --git a/pkg/mongo-gridfs/gridfs.go b/pkg/mongo-gridfs/gridfs.go new file mode 100644 index 000000000..2789fb216 --- /dev/null +++ b/pkg/mongo-gridfs/gridfs.go @@ -0,0 +1,40 @@ +package mongogridfs + +import "go.mongodb.org/mongo-driver/mongo/gridfs" + +type gfs struct { + bucket *gridfs.Bucket +} + +type GridFs interface { + Upload() error + Download() error + Delete() error + Fetch() error + Search() error +} + +// Delete implements GridFs +func (*gfs) Delete() error { + panic("unimplemented") +} + +// Download implements GridFs +func (*gfs) Download() error { + panic("unimplemented") +} + +// Fetch implements GridFs +func (*gfs) Fetch() error { + panic("unimplemented") +} + +// Search implements GridFs +func (*gfs) Search() error { + panic("unimplemented") +} + +// Upload implements GridFs +func (*gfs) Upload() error { + panic("unimplemented") +} diff --git a/pkg/mongo-gridfs/main.go b/pkg/mongo-gridfs/main.go new file mode 100644 index 000000000..0abdb2566 --- /dev/null +++ b/pkg/mongo-gridfs/main.go @@ -0,0 +1,49 @@ +package mongogridfs + +import ( + "context" + "time" + + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/gridfs" + "go.mongodb.org/mongo-driver/mongo/options" + "go.uber.org/fx" +) + +type MongoConfig interface { + GetMongoConfig() (url string, dbName string) +} + +func NewMongoGridFsClientFx[T MongoConfig]() fx.Option { + return fx.Module("mongodb-gridfs", + fx.Provide( + func(env T) (*gridfs.Bucket, GridFs, error) { + + ctx, cancel := context.WithTimeout( + context.Background(), + 10*time.Second, + ) + defer cancel() + + url, dbName := env.GetMongoConfig() + + client, err := mongo.Connect(ctx, options.Client().ApplyURI(url)) + if err != nil { + return nil, nil, err + } + + db := client.Database(dbName) + bucket, err := gridfs.NewBucket(db) + if err != nil { + return nil, nil, err + } + + gridfs := &gfs{ + bucket: bucket, + } + + return bucket, gridfs, nil + }, + ), + ) +} From ec05859657c0260dd1b0ebb20101064403fb071c Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Thu, 1 Jun 2023 19:35:58 +0530 Subject: [PATCH 3/3] :sparkles: new improved nodectrl written nodectrl with support of digital ocean and aws with feature of creation and deletion added. --- apps/nodecontroller/.dockerignore | 1 - apps/nodecontroller/Dockerfile | 29 -- apps/nodecontroller/Taskfile.yml | 61 ---- apps/nodecontroller/internal/app/main.go | 11 - apps/nodecontroller/internal/domain/aws.go | 86 ----- apps/nodecontroller/internal/domain/do.go | 86 ----- apps/nodecontroller/internal/domain/main.go | 88 ----- apps/nodecontroller/internal/domain/port.go | 5 - .../nodecontroller/internal/framework/main.go | 24 -- apps/nodecontroller/main.go | 24 -- apps/nodecontroller/task.md | 11 - apps/nodectrl/internal/app/main.go | 51 ++- apps/nodectrl/internal/domain/aws.go | 141 -------- apps/nodectrl/internal/domain/aws/main.go | 162 ++++++++++ .../internal/domain/{ => azure}/azure.go | 0 apps/nodectrl/internal/domain/common.go | 101 ------ .../nodectrl/internal/domain/common/common.go | 8 + .../internal/domain/common/interface.go | 10 + apps/nodectrl/internal/domain/do.go | 5 - apps/nodectrl/internal/domain/do/main.go | 155 +++++++++ .../nodectrl/internal/domain/{ => gcp}/gcp.go | 0 apps/nodectrl/internal/domain/interface.go | 9 - .../internal/domain/provider-client-fx.go | 32 +- .../domain/terraform-utils/.dockerignore | 3 - .../domain/terraform-utils/aws-test.go | 53 --- .../internal/domain/terraform-utils/aws.go | 305 ------------------ .../internal/domain/terraform-utils/azure.go | 11 - .../internal/domain/terraform-utils/common.go | 125 ------- .../domain/terraform-utils/do-test.go | 82 ----- .../internal/domain/terraform-utils/do.go | 300 ----------------- .../domain/terraform-utils/interface.go | 9 - .../internal/domain/terraform-utils/main.go | 3 - .../domain/terraform-utils/terraform/e | 1 - .../terraform-utils/terraform/secrets.yml | 2 - .../internal/domain/terraform-utils/types.go | 9 - .../internal/domain/terraform/aws/init.sh | 7 - .../internal/domain/terraform/aws/resource.tf | 161 --------- .../domain/terraform/aws/variables.tf | 31 -- .../internal/domain/terraform/do/init.sh | 7 - .../internal/domain/terraform/do/resource.tf | 36 --- .../internal/domain/terraform/do/variables.tf | 40 --- apps/nodectrl/internal/domain/terraform/e | 1 - .../internal/domain/terraform/secrets.yml | 2 - apps/nodectrl/internal/domain/utils/common.go | 206 ++++++++++-- apps/nodectrl/internal/domain/utils/dump.go | 127 -------- apps/nodectrl/internal/domain/utils/zipper.go | 6 +- apps/nodectrl/main.go | 2 +- .../terraform/aws/init.sh | 0 .../terraform/aws/resource.tf | 0 .../terraform/aws/variables.tf | 0 .../terraform-utils => }/terraform/do/init.sh | 0 .../terraform/do/resource.tf | 0 .../terraform/do/variables.tf | 0 apps/nodectrl2/.dockerignore | 1 - apps/nodectrl2/Dockerfile | 29 -- apps/nodectrl2/Taskfile.yml | 27 -- apps/nodectrl2/internal/app/main.go | 11 - apps/nodectrl2/internal/domain/aws.go | 103 ------ apps/nodectrl2/internal/domain/do.go | 106 ------ apps/nodectrl2/internal/domain/main.go | 91 ------ apps/nodectrl2/internal/domain/port.go | 5 - apps/nodectrl2/internal/framework/main.go | 24 -- apps/nodectrl2/main.go | 25 -- apps/nodectrl2/task.md | 11 - go.mod | 5 +- go.sum | 6 + pkg/infraClient/.dockerignore | 3 - pkg/infraClient/aws-test.go | 53 --- pkg/infraClient/aws.go | 305 ------------------ pkg/infraClient/azure.go | 11 - pkg/infraClient/common.go | 125 ------- pkg/infraClient/do-test.go | 82 ----- pkg/infraClient/do.go | 300 ----------------- pkg/infraClient/interface.go | 8 - pkg/infraClient/main.go | 6 - pkg/infraClient/terraform/aws/init.sh | 7 - pkg/infraClient/terraform/aws/resource.tf | 161 --------- pkg/infraClient/terraform/aws/variables.tf | 31 -- pkg/infraClient/terraform/do/init.sh | 7 - pkg/infraClient/terraform/do/resource.tf | 36 --- pkg/infraClient/terraform/do/variables.tf | 40 --- pkg/infraClient/terraform/e | 1 - pkg/infraClient/terraform/secrets.yml | 2 - pkg/mongo-gridfs/gridfs.go | 138 +++++++- 84 files changed, 712 insertions(+), 3677 deletions(-) delete mode 100644 apps/nodecontroller/.dockerignore delete mode 100644 apps/nodecontroller/Dockerfile delete mode 100644 apps/nodecontroller/Taskfile.yml delete mode 100644 apps/nodecontroller/internal/app/main.go delete mode 100644 apps/nodecontroller/internal/domain/aws.go delete mode 100644 apps/nodecontroller/internal/domain/do.go delete mode 100644 apps/nodecontroller/internal/domain/main.go delete mode 100644 apps/nodecontroller/internal/domain/port.go delete mode 100644 apps/nodecontroller/internal/framework/main.go delete mode 100644 apps/nodecontroller/main.go delete mode 100644 apps/nodecontroller/task.md delete mode 100644 apps/nodectrl/internal/domain/aws.go create mode 100644 apps/nodectrl/internal/domain/aws/main.go rename apps/nodectrl/internal/domain/{ => azure}/azure.go (100%) delete mode 100644 apps/nodectrl/internal/domain/common.go create mode 100644 apps/nodectrl/internal/domain/common/common.go create mode 100644 apps/nodectrl/internal/domain/common/interface.go delete mode 100644 apps/nodectrl/internal/domain/do.go create mode 100644 apps/nodectrl/internal/domain/do/main.go rename apps/nodectrl/internal/domain/{ => gcp}/gcp.go (100%) delete mode 100644 apps/nodectrl/internal/domain/interface.go delete mode 100644 apps/nodectrl/internal/domain/terraform-utils/.dockerignore delete mode 100644 apps/nodectrl/internal/domain/terraform-utils/aws-test.go delete mode 100644 apps/nodectrl/internal/domain/terraform-utils/aws.go delete mode 100644 apps/nodectrl/internal/domain/terraform-utils/azure.go delete mode 100644 apps/nodectrl/internal/domain/terraform-utils/common.go delete mode 100644 apps/nodectrl/internal/domain/terraform-utils/do-test.go delete mode 100644 apps/nodectrl/internal/domain/terraform-utils/do.go delete mode 100644 apps/nodectrl/internal/domain/terraform-utils/interface.go delete mode 100644 apps/nodectrl/internal/domain/terraform-utils/main.go delete mode 100644 apps/nodectrl/internal/domain/terraform-utils/terraform/e delete mode 100644 apps/nodectrl/internal/domain/terraform-utils/terraform/secrets.yml delete mode 100644 apps/nodectrl/internal/domain/terraform-utils/types.go delete mode 100644 apps/nodectrl/internal/domain/terraform/aws/init.sh delete mode 100644 apps/nodectrl/internal/domain/terraform/aws/resource.tf delete mode 100644 apps/nodectrl/internal/domain/terraform/aws/variables.tf delete mode 100644 apps/nodectrl/internal/domain/terraform/do/init.sh delete mode 100644 apps/nodectrl/internal/domain/terraform/do/resource.tf delete mode 100644 apps/nodectrl/internal/domain/terraform/do/variables.tf delete mode 100644 apps/nodectrl/internal/domain/terraform/e delete mode 100644 apps/nodectrl/internal/domain/terraform/secrets.yml delete mode 100644 apps/nodectrl/internal/domain/utils/dump.go rename apps/nodectrl/{internal/domain/terraform-utils => }/terraform/aws/init.sh (100%) rename apps/nodectrl/{internal/domain/terraform-utils => }/terraform/aws/resource.tf (100%) rename apps/nodectrl/{internal/domain/terraform-utils => }/terraform/aws/variables.tf (100%) rename apps/nodectrl/{internal/domain/terraform-utils => }/terraform/do/init.sh (100%) rename apps/nodectrl/{internal/domain/terraform-utils => }/terraform/do/resource.tf (100%) rename apps/nodectrl/{internal/domain/terraform-utils => }/terraform/do/variables.tf (100%) delete mode 100644 apps/nodectrl2/.dockerignore delete mode 100644 apps/nodectrl2/Dockerfile delete mode 100644 apps/nodectrl2/Taskfile.yml delete mode 100644 apps/nodectrl2/internal/app/main.go delete mode 100644 apps/nodectrl2/internal/domain/aws.go delete mode 100644 apps/nodectrl2/internal/domain/do.go delete mode 100644 apps/nodectrl2/internal/domain/main.go delete mode 100644 apps/nodectrl2/internal/domain/port.go delete mode 100644 apps/nodectrl2/internal/framework/main.go delete mode 100644 apps/nodectrl2/main.go delete mode 100644 apps/nodectrl2/task.md delete mode 100644 pkg/infraClient/.dockerignore delete mode 100644 pkg/infraClient/aws-test.go delete mode 100644 pkg/infraClient/aws.go delete mode 100644 pkg/infraClient/azure.go delete mode 100644 pkg/infraClient/common.go delete mode 100644 pkg/infraClient/do-test.go delete mode 100644 pkg/infraClient/do.go delete mode 100644 pkg/infraClient/interface.go delete mode 100644 pkg/infraClient/main.go delete mode 100644 pkg/infraClient/terraform/aws/init.sh delete mode 100644 pkg/infraClient/terraform/aws/resource.tf delete mode 100644 pkg/infraClient/terraform/aws/variables.tf delete mode 100644 pkg/infraClient/terraform/do/init.sh delete mode 100644 pkg/infraClient/terraform/do/resource.tf delete mode 100644 pkg/infraClient/terraform/do/variables.tf delete mode 100644 pkg/infraClient/terraform/e delete mode 100644 pkg/infraClient/terraform/secrets.yml diff --git a/apps/nodecontroller/.dockerignore b/apps/nodecontroller/.dockerignore deleted file mode 100644 index c8899319b..000000000 --- a/apps/nodecontroller/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -*secrets diff --git a/apps/nodecontroller/Dockerfile b/apps/nodecontroller/Dockerfile deleted file mode 100644 index a867105fb..000000000 --- a/apps/nodecontroller/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -# syntax=docker/dockerfile:1.4 -FROM golang:1.18.3-alpine3.16 AS base -USER 1001 -ENV GOPATH=/tmp/go -ENV GOCACHE=/tmp/go-cache -WORKDIR /tmp/app -COPY --chown=1001 --from=project-root ./go.mod ./go.sum ./tools.go ./ -RUN go mod download -x -COPY --chown=1001 --from=project-root pkg ./pkg -ARG APP -RUN mkdir -p ./apps/$APP -WORKDIR /tmp/app/apps/$APP -COPY --chown=1001 ./ ./ -RUN CGO_ENABLED=0 go build -tags musl -o /tmp/bin/$APP ./main.go -RUN chmod +x /tmp/bin/$APP - -#FROM gcr.io/distroless/static-debian11 -FROM alpine -RUN adduser -D -h /home/nonroot nonroot -RUN apk add curl git openssh-client -# RUN cd bin && curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && chmod +x kubectl -RUN cd bin && curl -o tf.zip https://releases.hashicorp.com/terraform/1.2.9/terraform_1.2.9_linux_amd64.zip && unzip tf.zip -# RUN cd bin && curl -L0 -o talosctl https://github.com/siderolabs/talos/releases/download/v1.2.3/talosctl-linux-amd64 && chmod +x talosctl -USER nonroot -WORKDIR /tmp/app -COPY --chown=nonroot --from=base /tmp/bin/nodecontroller ./nodecontroller -COPY --chown=nonroot --from=project-root ./pkg/infraClient/terraform /templates/terraform -# COPY --chown=nonroot --from=project-root ./pkg/infraClient/templates /tmp/app/pkg/infraClient/templates -CMD ["./nodecontroller"] diff --git a/apps/nodecontroller/Taskfile.yml b/apps/nodecontroller/Taskfile.yml deleted file mode 100644 index 3b0fb80a3..000000000 --- a/apps/nodecontroller/Taskfile.yml +++ /dev/null @@ -1,61 +0,0 @@ -version: "3" - -dotenv: - - .secrets/env - -tasks: - run: - sources: - - ./internal/**/*.go - - ./main.go - cmds: - # - go run -tags dynamic main.go --dev - - nodemon -e go --signal SIGKILL --exec 'go run main.go --dev || exit 1' - - docker-build: - vars: - APP: nodecontroller - IMAGE: registry.kloudlite.io/kloudlite/{{.EnvName}}/{{.APP}}:{{.Tag}} - preconditions: - - sh: '[[ -n "{{.Tag}}" ]]' - msg: 'var Tag must have a value' - - - sh: '[[ "{{.EnvName}}" == "development" ]] || [[ "{{.EnvName}}" == "staging" ]] || [[ "{{.EnvName}}" == "production" ]]' - msg: 'var EnvName must have one of [development, staging, production] as its value' - cmds: - - docker buildx build -f ./Dockerfile -t {{.IMAGE}} . --build-arg APP={{.APP}} --platform linux/amd64 --build-context project-root=../.. - - docker push {{.IMAGE}} - - local-build: - preconditions: - - sh: '[ -n "{{.EnvName}}" ]' - msg: 'var EnvName must have a value' - - sh: '[ -n "{{.Tag}}" ]' - msg: 'var Tag must have a value' - vars: - APP: nodecontroller - IMAGE: registry.kloudlite.io/kloudlite/{{.EnvName}}/{{.APP}}:{{.Tag}} - env: - CGO_ENABLED: 0 - GOOS: linux - GOARCH: amd64 - silent: true - cmds: - - |+ - lineNumbers=$(cat Dockerfile | grep -i '^FROM' -n | tail +2 | awk -F: '{print $1}') - - startLineNo=$(echo "$lineNumbers" | head -n+1) - finalLineNo=$(echo "$lineNumbers" | tail -1) - - tDir=$(mktemp -d) - - nDockerfile=$(cat Dockerfile | tail --lines=+$startLineNo | grep -i --invert-match 'from=base') - echo "$nDockerfile" | sed "1 i # syntax=docker/dockerfile:1.4" > $tDir/Dockerfile.base - - cat $tDir/Dockerfile.base | sed "10 i COPY --from=local-builder ./{{.APP}} ./{{.APP}}" > $tDir/Dockerfile - cat $tDir/Dockerfile - - CGO_ENABLED=0 go build -o $tDir/{{.APP}} . - - docker buildx build -f $tDir/Dockerfile -t {{.IMAGE}} . --build-context local-builder=${tDir} --build-context project-root=../.. - docker push {{.IMAGE}} diff --git a/apps/nodecontroller/internal/app/main.go b/apps/nodecontroller/internal/app/main.go deleted file mode 100644 index dec13f138..000000000 --- a/apps/nodecontroller/internal/app/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package app - -import ( - "go.uber.org/fx" - "kloudlite.io/apps/nodecontroller/internal/domain" -) - -var Module = fx.Module( - "app", - domain.Module, -) diff --git a/apps/nodecontroller/internal/domain/aws.go b/apps/nodecontroller/internal/domain/aws.go deleted file mode 100644 index 73b7df7cb..000000000 --- a/apps/nodecontroller/internal/domain/aws.go +++ /dev/null @@ -1,86 +0,0 @@ -package domain - -import ( - "encoding/base64" - "errors" - - "gopkg.in/yaml.v3" - infraclient "kloudlite.io/pkg/infraClient" -) - -type awsConfig struct { - Version string `yaml:"version"` - Action string `yaml:"action"` - Provider string `yaml:"provider"` - Spec struct { - Provider struct { - AccessKey string `yaml:"accessKey"` - AccessSecret string `yaml:"accessSecret"` - AccountId string `yaml:"accountId"` - } `yaml:"provider"` - Node struct { - Region string `yaml:"region"` - InstanceType string `yaml:"instanceType"` - NodeId string `yaml:"nodeId"` - VPC string `yaml:"vpc"` - ImageId string `yaml:"imageId"` - } `yaml:"node"` - } `yaml:"spec"` -} - -func (d *domainI) doWithAWS() error { - - out, err := base64.StdEncoding.DecodeString(d.env.Config) - if err != nil { - return err - } - var awsConf awsConfig - e := yaml.Unmarshal(out, &awsConf) - if e != nil { - return e - } - klConf, err := d.getKlConf() - if err != nil { - return err - } - - awsProvider := infraclient.NewAWSProvider(infraclient.AWSProvider{ - AccessKey: awsConf.Spec.Provider.AccessKey, - AccessSecret: awsConf.Spec.Provider.AccessSecret, - AccountId: awsConf.Spec.Provider.AccountId, - }, infraclient.AWSProviderEnv{ - StorePath: klConf.Values.StorePath, - TfTemplates: klConf.Values.TfTemplates, - SSHPath: klConf.Values.SSHPath, - }) - - awsNode := infraclient.AWSNode{ - NodeId: awsConf.Spec.Node.NodeId, - Region: awsConf.Spec.Node.Region, - InstanceType: awsConf.Spec.Node.InstanceType, - VPC: awsConf.Spec.Node.VPC, - ImageId: awsConf.Spec.Node.ImageId, - } - - // return nil - - switch awsConf.Action { - case "create": - err = awsProvider.NewNode(awsNode) - if err != nil { - return err - } - - case "delete": - err = awsProvider.DeleteNode(awsNode) - - if err != nil { - return err - } - - default: - return errors.New("wrong action") - } - - return nil -} diff --git a/apps/nodecontroller/internal/domain/do.go b/apps/nodecontroller/internal/domain/do.go deleted file mode 100644 index b193522eb..000000000 --- a/apps/nodecontroller/internal/domain/do.go +++ /dev/null @@ -1,86 +0,0 @@ -package domain - -import ( - "encoding/base64" - "errors" - "fmt" - - "gopkg.in/yaml.v3" - infraclient "kloudlite.io/pkg/infraClient" -) - -type doConfig struct { - Version string `yaml:"version"` - Action string `yaml:"action"` - Provider string `yaml:"provider"` - Spec struct { - Provider struct { - ApiToken string `yaml:"apiToken"` - AccountId string `yaml:"accountId"` - } `yaml:"provider"` - Node struct { - Region string `yaml:"region"` - Size string `yaml:"size"` - NodeId string `yaml:"nodeId"` - ImageId string `yaml:"imageId"` - } `yaml:"node"` - } `yaml:"spec"` -} - -func (d *domainI) doWithDO() error { - - out, err := base64.StdEncoding.DecodeString(d.env.Config) - if err != nil { - fmt.Println("here") - return err - } - - var doConf doConfig - e := yaml.Unmarshal(out, &doConf) - if e != nil { - return e - } - klConf, err := d.getKlConf() - if err != nil { - fmt.Println("here") - return err - } - - doProvider := infraclient.NewDOProvider(infraclient.DoProvider{ - ApiToken: doConf.Spec.Provider.ApiToken, - AccountId: doConf.Spec.Provider.AccountId, - }, infraclient.DoProviderEnv{ - StorePath: klConf.Values.StorePath, - TfTemplates: klConf.Values.TfTemplates, - SSHPath: klConf.Values.SSHPath, - }) - - doNode := infraclient.DoNode{ - Region: doConf.Spec.Node.Region, - Size: doConf.Spec.Node.Size, - NodeId: doConf.Spec.Node.NodeId, - ImageId: doConf.Spec.Node.ImageId, - } - - // return nil - - switch doConf.Action { - case "create": - err = doProvider.NewNode(doNode) - if err != nil { - return err - } - - case "delete": - err = doProvider.DeleteNode(doNode) - - if err != nil { - return err - } - - default: - return errors.New("wrong action") - } - - return nil -} diff --git a/apps/nodecontroller/internal/domain/main.go b/apps/nodecontroller/internal/domain/main.go deleted file mode 100644 index 9ec955892..000000000 --- a/apps/nodecontroller/internal/domain/main.go +++ /dev/null @@ -1,88 +0,0 @@ -package domain - -import ( - "encoding/base64" - "errors" - "fmt" - - "gopkg.in/yaml.v3" - "kloudlite.io/pkg/config" - - "go.uber.org/fx" -) - -type domainI struct { - env *Env -} - -type KLConf struct { - Version string `yaml:"version"` - Values struct { - StorePath string `yaml:"storePath"` - TfTemplates string `yaml:"tfTemplatesPath"` - SSHPath string `yaml:"sshPath"` - PubKey string `yaml:"pubkey"` - } `yaml:"spec"` -} - -func (d *domainI) getKlConf() (*KLConf, error) { - out, err := base64.StdEncoding.DecodeString(d.env.KLConfig) - if err != nil { - fmt.Println("here") - return nil, err - } - - var klConf KLConf - e := yaml.Unmarshal(out, &klConf) - if e != nil { - - return nil, e - } - - return &klConf, nil -} - -// startJob implements Domain -func (d *domainI) StartJob() error { - - switch d.env.Provider { - case "do": - if err := d.doWithDO(); err != nil { - return err - } - - case "aws": - if err := d.doWithAWS(); err != nil { - return err - } - - default: - return errors.New("this type of provider not suported") - } - return nil -} - -func fxDomain(env *Env) Domain { - return &domainI{ - env: env, - } -} - -type Env struct { - Config string `env:"NODE_CONFIG" required:"true"` - Provider string `env:"PROVIDER" required:"true"` - KLConfig string `env:"KL_CONFIG" required:"true"` -} - -var Module = fx.Module( - "domain", - config.EnvFx[Env](), - fx.Provide(fxDomain), -) - -/* -main - -> framework () - -> app () - -> domain (main logic) -*/ diff --git a/apps/nodecontroller/internal/domain/port.go b/apps/nodecontroller/internal/domain/port.go deleted file mode 100644 index 184633284..000000000 --- a/apps/nodecontroller/internal/domain/port.go +++ /dev/null @@ -1,5 +0,0 @@ -package domain - -type Domain interface { - StartJob() error -} diff --git a/apps/nodecontroller/internal/framework/main.go b/apps/nodecontroller/internal/framework/main.go deleted file mode 100644 index 715725db1..000000000 --- a/apps/nodecontroller/internal/framework/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package framework - -import ( - "fmt" - "os" - - "go.uber.org/fx" - "kloudlite.io/apps/nodecontroller/internal/app" - "kloudlite.io/apps/nodecontroller/internal/domain" -) - -var Module = fx.Module( - "framework", - app.Module, - fx.Invoke(func(d domain.Domain, shutdowner fx.Shutdowner) { - err := d.StartJob() - if err != nil { - fmt.Println(err) - os.Exit(1) - } else { - shutdowner.Shutdown() - } - }), -) diff --git a/apps/nodecontroller/main.go b/apps/nodecontroller/main.go deleted file mode 100644 index 08e57e786..000000000 --- a/apps/nodecontroller/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "flag" - - "go.uber.org/fx" - "kloudlite.io/apps/nodecontroller/internal/framework" - "kloudlite.io/pkg/logging" -) - -func main() { - var isDev bool - flag.BoolVar(&isDev, "dev", false, "--dev") - flag.Parse() - fx.New( - framework.Module, - fx.Provide( - func() (logging.Logger, error) { - return logging.New(&logging.Options{Name: "auth", Dev: isDev}) - }, - ), - // fx.NopLogger, - ).Run() -} diff --git a/apps/nodecontroller/task.md b/apps/nodecontroller/task.md deleted file mode 100644 index 9ab842f47..000000000 --- a/apps/nodecontroller/task.md +++ /dev/null @@ -1,11 +0,0 @@ -# Tasks - -[ ] lib to create node - [ ] digital ocean - [ ] aws - [ ] azure - [ ] gcp -[ ] read message from kakfa -[ ] execute the message -[ ] commit the message as processed ( according to condition ) -[ ] apply the status in resource annotation of the resource diff --git a/apps/nodectrl/internal/app/main.go b/apps/nodectrl/internal/app/main.go index bb403769e..aff934b6d 100644 --- a/apps/nodectrl/internal/app/main.go +++ b/apps/nodectrl/internal/app/main.go @@ -6,35 +6,56 @@ import ( "go.uber.org/fx" "kloudlite.io/apps/nodectrl/internal/domain" + "kloudlite.io/apps/nodectrl/internal/domain/common" + "kloudlite.io/apps/nodectrl/internal/domain/utils" "kloudlite.io/apps/nodectrl/internal/env" ) var Module = fx.Module("app", domain.Module, fx.Invoke( - func(env *env.Env, pc domain.ProviderClient, shutdowner fx.Shutdowner, lifecycle fx.Lifecycle) { + func(env *env.Env, pc common.ProviderClient, shutdowner fx.Shutdowner, lifecycle fx.Lifecycle) { lifecycle.Append(fx.Hook{ OnStart: func(context.Context) error { - switch env.Action { - case "create": - fmt.Println("needs to create node") - if err := pc.NewNode(); err != nil { + go func() error { + ctx := context.Background() + if err := utils.SetupGetWorkDir(); err != nil { return err } - case "delete": - fmt.Println("needs to delete node") - if err := pc.DeleteNode(); err != nil { + + err := func() error { + switch env.Action { + case "create": + + fmt.Println("needs to create node") + if err := pc.NewNode(ctx); err != nil { + return err + } + case "delete": + fmt.Println("needs to delete node") + if err := pc.DeleteNode(ctx); err != nil { + return err + } + + case "": + return fmt.Errorf("ACTION not provided, supported actions {create, delete} ") + default: + return fmt.Errorf("not supported actions '%s' please provide one of supported action like { create, delete }", env.Action) + + } + fmt.Println(utils.ColorText("\n🙃 Successfully Exited 🙃\n", 5)) + shutdowner.Shutdown() + return nil + }() + + if err != nil { + fmt.Println(utils.ColorText(fmt.Sprint("\n", "Error: ", err, "\n"), 1)) return err } + return nil + }() - case "": - return fmt.Errorf("ACTION not provided, supported actions {create, delete} ") - default: - return fmt.Errorf("not supported actions '%s' please provide one of supported action like { create, delete }", env.Action) - - } - shutdowner.Shutdown() return nil }, OnStop: func(context.Context) error { diff --git a/apps/nodectrl/internal/domain/aws.go b/apps/nodectrl/internal/domain/aws.go deleted file mode 100644 index 8275ff470..000000000 --- a/apps/nodectrl/internal/domain/aws.go +++ /dev/null @@ -1,141 +0,0 @@ -package domain - -import ( - mongogridfs "kloudlite.io/pkg/mongo-gridfs" -) - -type CommonProviderData struct { - // StorePath string `yaml:"storePath"` - TfTemplates string `yaml:"tfTemplates"` - Labels map[string]string `yaml:"labels"` - Taints []string `yaml:"taints"` - // Secrets string `yaml:"secrets"` - SSHPath string `yaml:"sshPath"` -} - -type AwsProviderConfig struct { - AccessKey string `yaml:"accessKey"` - AccessSecret string `yaml:"accessSecret"` - AccountId string `yaml:"accountId"` -} - -type AWSNode struct { - NodeId string `yaml:"nodeId"` - Region string `yaml:"region"` - InstanceType string `yaml:"instanceType"` - VPC string `yaml:"vpc"` - ImageId string `yaml:"imageId"` -} - -type awsClient struct { - gfs mongogridfs.GridFs - node AWSNode - - accessKey string - accessSecret string - - SSHPath string - accountId string - secrets string - providerDir string - storePath string - tfTemplates string - labels map[string]string - taints []string -} - -// // getFolder implements doProviderClient -// func (a awsClient) getFolder() string { -// // eg -> /path/acc_id/do/blr1/node_id/do -// -// return path.Join(a.storePath, a.accountId, a.providerDir, a.node.Region, a.node.NodeId) -// } - -// // initTFdir implements doProviderClient -// func (d awsClient) initTFdir() error { -// -// folder := d.getFolder() -// -// if err := utils.ExecCmd(fmt.Sprintf("cp -r %s %s", fmt.Sprintf("%s/%s", d.tfTemplates, d.providerDir), folder), "initialize terraform"); err != nil { -// return err -// } -// -// cmd := exec.Command("terraform", "init") -// cmd.Dir = path.Join(folder, d.providerDir) -// -// cmd.Stdout = os.Stdout -// cmd.Stderr = os.Stderr -// -// return cmd.Run() -// } - -// NewNode implements ProviderClient -func (a awsClient) NewNode() error { - - values := map[string]string{} - - values["access_key"] = a.accessKey - values["secret_key"] = a.accessSecret - - values["region"] = a.node.Region - values["node_id"] = a.node.NodeId - values["instance_type"] = a.node.InstanceType - values["keys-path"] = a.SSHPath - values["ami"] = a.node.ImageId - - /* - steps: - - check if state present in db - - if present load that to working dir - - else initialize new tf dir - - apply terraform - - upload the final state with defer - */ - - // making dir - // if err := utils.Mkdir(a.getFolder()); err != nil { - // return err - // } - // - // // initialize directory - // if err := a.initTFdir(); err != nil { - // return err - // } - // - // // apply terraform - // return utils.ApplyTF(path.Join(a.getFolder(), a.providerDir), values) - // - return nil -} - -// AttachNode implements ProviderClientkkk -func (awsClient) AttachNode() error { - panic("unimplemented") -} - -// DeleteNode implements ProviderClient -func (awsClient) DeleteNode() error { - panic("unimplemented") -} - -// UnattachNode implements ProviderClient -func (awsClient) UnattachNode() error { - panic("unimplemented") -} - -func NewAwsProviderClient(node AWSNode, cpd CommonProviderData, apc AwsProviderConfig) ProviderClient { - return awsClient{ - node: node, - accessKey: apc.AccessKey, - accessSecret: apc.AccessSecret, - accountId: apc.AccountId, - - providerDir: "aws", - // secrets: cpd.Secrets, - // storePath: cpd.StorePath, - tfTemplates: cpd.TfTemplates, - labels: cpd.Labels, - taints: cpd.Taints, - SSHPath: cpd.SSHPath, - } -} diff --git a/apps/nodectrl/internal/domain/aws/main.go b/apps/nodectrl/internal/domain/aws/main.go new file mode 100644 index 000000000..85e6dcac4 --- /dev/null +++ b/apps/nodectrl/internal/domain/aws/main.go @@ -0,0 +1,162 @@ +package aws + +import ( + "fmt" + "path" + "time" + + "golang.org/x/net/context" + "kloudlite.io/apps/nodectrl/internal/domain/common" + "kloudlite.io/apps/nodectrl/internal/domain/utils" + mongogridfs "kloudlite.io/pkg/mongo-gridfs" +) + +type AwsProviderConfig struct { + AccessKey string `yaml:"accessKey"` + AccessSecret string `yaml:"accessSecret"` + AccountId string `yaml:"accountId"` +} + +type AWSNode struct { + NodeId string `yaml:"nodeId"` + Region string `yaml:"region"` + InstanceType string `yaml:"instanceType"` + VPC string `yaml:"vpc"` + ImageId string `yaml:"imageId"` +} + +type awsClient struct { + gfs mongogridfs.GridFs + node AWSNode + + accessKey string + accessSecret string + + SSHPath string + accountId string + providerDir string + tfTemplates string + labels map[string]string + taints []string +} + +func parseValues(a awsClient) map[string]string { + values := map[string]string{} + + values["access_key"] = a.accessKey + values["secret_key"] = a.accessSecret + + values["region"] = a.node.Region + values["node_id"] = a.node.NodeId + values["instance_type"] = a.node.InstanceType + values["keys-path"] = a.SSHPath + values["ami"] = a.node.ImageId + + return values +} + +func (a awsClient) SaveToDbGuranteed(ctx context.Context) { + for { + if err := utils.SaveToDb(ctx, a.node.NodeId, a.gfs); err == nil { + break + } else { + fmt.Println(err) + } + time.Sleep(time.Second * 20) + } +} + +// NewNode implements ProviderClient +func (a awsClient) NewNode(ctx context.Context) error { + + values := parseValues(a) + + /* + steps: + - check if state present in db + - if present load that to working dir + - else initialize new tf dir + - apply terraform + - upload the final state with defer + */ + + if err := utils.MakeTfWorkFileReady(ctx, a.node.NodeId, path.Join(a.tfTemplates, "aws"), a.gfs, true); err != nil { + return err + } + + defer a.SaveToDbGuranteed(ctx) + + // upload the final state to the db, upsert if db is already present + + // apply the tf file + if err := func() error { + if err := utils.InitTFdir(path.Join(utils.Workdir, a.node.NodeId)); err != nil { + return err + } + + if err := utils.ApplyTF(path.Join(utils.Workdir, a.node.NodeId), values); err != nil { + return err + } + + return nil + }(); err != nil { + return err + } + + return nil +} + +// DeleteNode implements ProviderClient +func (a awsClient) DeleteNode(ctx context.Context) error { + + values := parseValues(a) + + /* + steps: + - check if state present in db + - if present load that to working dir + - else initialize new tf dir + - destroy node with terraform + - delete final state + */ + + if err := utils.MakeTfWorkFileReady(ctx, a.node.NodeId, path.Join(a.tfTemplates, "aws"), a.gfs, false); err != nil { + return err + } + + // destroy the tf file + if err := func() error { + if err := utils.DestroyNode(a.node.NodeId, values); err != nil { + return err + } + + return nil + }(); err != nil { + return err + } + + filename := fmt.Sprintf("%s.zip", a.node.NodeId) + + if err := a.gfs.DeleteAllWithFilename(filename); err != nil { + return err + } + + return nil +} + +func NewAwsProviderClient(node AWSNode, cpd common.CommonProviderData, apc AwsProviderConfig, gfs mongogridfs.GridFs) common.ProviderClient { + return awsClient{ + node: node, + gfs: gfs, + + accessKey: apc.AccessKey, + accessSecret: apc.AccessSecret, + accountId: apc.AccountId, + + providerDir: "aws", + tfTemplates: cpd.TfTemplates, + labels: cpd.Labels, + taints: cpd.Taints, + SSHPath: cpd.SSHPath, + } +} diff --git a/apps/nodectrl/internal/domain/azure.go b/apps/nodectrl/internal/domain/azure/azure.go similarity index 100% rename from apps/nodectrl/internal/domain/azure.go rename to apps/nodectrl/internal/domain/azure/azure.go diff --git a/apps/nodectrl/internal/domain/common.go b/apps/nodectrl/internal/domain/common.go deleted file mode 100644 index 1b7f3f926..000000000 --- a/apps/nodectrl/internal/domain/common.go +++ /dev/null @@ -1,101 +0,0 @@ -package domain - -import ( - "encoding/json" - "fmt" - "os" - "os/exec" - "strings" - - "kloudlite.io/apps/nodectrl/internal/domain/utils" -) - -const ( - CLUSTER_ID = "kl" -) - -// rmTFdir implements doProviderClient -// func rmdir(folder string) error { -// return execCmd(fmt.Sprintf("rm -rf %q", folder), "") -// } - -// makeTFdir implements doProviderClient -func mkdir(folder string) error { - return utils.ExecCmd(fmt.Sprintf("mkdir -p %q", folder), "mkdir ") -} - -// destroyNode implements doProviderClient -func destroyNode(folder string, values map[string]string) error { - vars := []string{"destroy", "-auto-approve"} - - for k, v := range values { - vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) - } - - cmd := exec.Command("terraform", vars...) - cmd.Dir = folder - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err := cmd.Run() - if err != nil { - fmt.Println(err) - return err - } - - return os.RemoveAll(folder) - - // return err -} - -func getOutput(folder, key string) ([]byte, error) { - vars := []string{"output", "-json"} - fmt.Printf("[#] terraform %s\n", strings.Join(vars, " ")) - cmd := exec.Command("terraform", vars...) - cmd.Dir = folder - - // cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - out, err := cmd.Output() - if err != nil { - return nil, err - - } - - // fmt.Println(string(out)) - - var resp map[string]struct { - Value string `json:"value"` - } - - err = json.Unmarshal(out, &resp) - if err != nil { - return nil, err - } - - return []byte(resp[key].Value), nil -} - -// applyTF implements doProviderClient -func applyTF(folder string, values map[string]string) error { - - vars := []string{"apply", "-auto-approve"} - - fmt.Printf("[#] terraform %s", strings.Join(vars, " ")) - - for k, v := range values { - vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) - } - - cmd := exec.Command("terraform", vars...) - cmd.Dir = folder - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - cmd.Dir = folder - - return cmd.Run() -} diff --git a/apps/nodectrl/internal/domain/common/common.go b/apps/nodectrl/internal/domain/common/common.go new file mode 100644 index 000000000..ec8c16d93 --- /dev/null +++ b/apps/nodectrl/internal/domain/common/common.go @@ -0,0 +1,8 @@ +package common + +type CommonProviderData struct { + TfTemplates string `yaml:"tfTemplates"` + Labels map[string]string `yaml:"labels"` + Taints []string `yaml:"taints"` + SSHPath string `yaml:"sshPath"` +} diff --git a/apps/nodectrl/internal/domain/common/interface.go b/apps/nodectrl/internal/domain/common/interface.go new file mode 100644 index 000000000..a23c9ba46 --- /dev/null +++ b/apps/nodectrl/internal/domain/common/interface.go @@ -0,0 +1,10 @@ +package common + +import "context" + +type ProviderClient interface { + NewNode(ctx context.Context) error + DeleteNode(ctx context.Context) error + + SaveToDbGuranteed(ctx context.Context) +} diff --git a/apps/nodectrl/internal/domain/do.go b/apps/nodectrl/internal/domain/do.go deleted file mode 100644 index 929ec17c1..000000000 --- a/apps/nodectrl/internal/domain/do.go +++ /dev/null @@ -1,5 +0,0 @@ -package domain - -func (d domain) StartDoJob() error { - panic("not implemented yet") -} diff --git a/apps/nodectrl/internal/domain/do/main.go b/apps/nodectrl/internal/domain/do/main.go new file mode 100644 index 000000000..96847bab5 --- /dev/null +++ b/apps/nodectrl/internal/domain/do/main.go @@ -0,0 +1,155 @@ +package do + +import ( + "context" + "fmt" + "path" + "time" + + "kloudlite.io/apps/nodectrl/internal/domain/common" + "kloudlite.io/apps/nodectrl/internal/domain/utils" + mongogridfs "kloudlite.io/pkg/mongo-gridfs" +) + +type DoProviderConfig struct { + ApiToken string `yaml:"apiToken"` + AccountId string `yaml:"accountId"` +} + +type DoNode struct { + Region string `yaml:"region"` + Size string `yaml:"size"` + NodeId string `yaml:"nodeId"` + ImageId string `yaml:"imageId"` +} + +type doClient struct { + gfs mongogridfs.GridFs + node DoNode + + apiToken string + + SSHPath string + accountId string + providerDir string + tfTemplates string + labels map[string]string + taints []string +} + +func parseValues(d doClient) map[string]string { + values := map[string]string{} + + values["do-token"] = d.apiToken + values["accountId"] = d.accountId + + values["do-image-id"] = "ubuntu-22-10-x64" + values["nodeId"] = d.node.NodeId + values["size"] = d.node.Size + values["keys-path"] = d.SSHPath + + return values +} + +// SaveToDbGuranteed implements ProviderClient +func (d doClient) SaveToDbGuranteed(ctx context.Context) { + for { + if err := utils.SaveToDb(ctx, d.node.NodeId, d.gfs); err == nil { + break + } else { + fmt.Println(err) + } + time.Sleep(time.Second * 20) + } +} + +// NewNode implements ProviderClient +func (d doClient) NewNode(ctx context.Context) error { + values := parseValues(d) + + /* + steps: + - check if state present in db + - if present load that to working dir + - else initialize new tf dir + - apply terraform + - upload the final state with defer + */ + + if err := utils.MakeTfWorkFileReady(ctx, d.node.NodeId, path.Join(d.tfTemplates, "do"), d.gfs, true); err != nil { + return err + } + + defer d.SaveToDbGuranteed(ctx) + + // apply the tf file + if err := func() error { + if err := utils.InitTFdir(path.Join(utils.Workdir, d.node.NodeId)); err != nil { + return err + } + + if err := utils.ApplyTF(path.Join(utils.Workdir, d.node.NodeId), values); err != nil { + return err + } + + return nil + }(); err != nil { + return err + } + + return nil +} + +// DeleteNode implements ProviderClient +func (d doClient) DeleteNode(ctx context.Context) error { + values := parseValues(d) + + /* + steps: + - check if state present in db + - if present load that to working dir + - else initialize new tf dir + - destroy node with terraform + - delete final state + */ + + if err := utils.MakeTfWorkFileReady(ctx, d.node.NodeId, path.Join(d.tfTemplates, "aws"), d.gfs, false); err != nil { + return err + } + + // destroy the tf file + if err := func() error { + if err := utils.DestroyNode(d.node.NodeId, values); err != nil { + return err + } + + return nil + }(); err != nil { + return err + } + + filename := fmt.Sprintf("%s.zip", d.node.NodeId) + + if err := d.gfs.DeleteAllWithFilename(filename); err != nil { + return err + } + + return nil +} + +func NewDoProviderClient(node DoNode, cpd common.CommonProviderData, dpc DoProviderConfig, gfs mongogridfs.GridFs) common.ProviderClient { + return doClient{ + node: node, + gfs: gfs, + + apiToken: dpc.ApiToken, + accountId: dpc.AccountId, + + providerDir: "do", + + tfTemplates: cpd.TfTemplates, + labels: cpd.Labels, + taints: cpd.Taints, + SSHPath: cpd.SSHPath, + } +} diff --git a/apps/nodectrl/internal/domain/gcp.go b/apps/nodectrl/internal/domain/gcp/gcp.go similarity index 100% rename from apps/nodectrl/internal/domain/gcp.go rename to apps/nodectrl/internal/domain/gcp/gcp.go diff --git a/apps/nodectrl/internal/domain/interface.go b/apps/nodectrl/internal/domain/interface.go deleted file mode 100644 index 63b634a85..000000000 --- a/apps/nodectrl/internal/domain/interface.go +++ /dev/null @@ -1,9 +0,0 @@ -package domain - -type ProviderClient interface { - NewNode() error - DeleteNode() error - - AttachNode() error - UnattachNode() error -} diff --git a/apps/nodectrl/internal/domain/provider-client-fx.go b/apps/nodectrl/internal/domain/provider-client-fx.go index 378c60498..790ee8cac 100644 --- a/apps/nodectrl/internal/domain/provider-client-fx.go +++ b/apps/nodectrl/internal/domain/provider-client-fx.go @@ -2,14 +2,18 @@ package domain import ( "go.uber.org/fx" + "kloudlite.io/apps/nodectrl/internal/domain/aws" + "kloudlite.io/apps/nodectrl/internal/domain/common" + "kloudlite.io/apps/nodectrl/internal/domain/do" "kloudlite.io/apps/nodectrl/internal/domain/utils" "kloudlite.io/apps/nodectrl/internal/env" + mongogridfs "kloudlite.io/pkg/mongo-gridfs" ) var ProviderClientFx = fx.Module("provider-client-fx", - fx.Provide(func(env *env.Env) (ProviderClient, error) { + fx.Provide(func(env *env.Env, gfs mongogridfs.GridFs) (common.ProviderClient, error) { - cpd := CommonProviderData{} + cpd := common.CommonProviderData{} if err := utils.Base64YamlDecode(env.ProviderConfig, &cpd); err != nil { return nil, err @@ -18,26 +22,40 @@ var ProviderClientFx = fx.Module("provider-client-fx", switch env.CloudProvider { case "aws": - node := AWSNode{} + node := aws.AWSNode{} if err := utils.Base64YamlDecode(env.NodeConfig, &node); err != nil { return nil, err } - apc := AwsProviderConfig{} + apc := aws.AwsProviderConfig{} if err := utils.Base64YamlDecode(env.AWSProviderConfig, &apc); err != nil { return nil, err } - return NewAwsProviderClient(node, cpd, apc), nil + return aws.NewAwsProviderClient(node, cpd, apc, gfs), nil case "azure": panic("not implemented") case "do": - panic("not implemented") + + node := do.DoNode{} + + if err := utils.Base64YamlDecode(env.NodeConfig, &node); err != nil { + return nil, err + } + + dpc := do.DoProviderConfig{} + + if err := utils.Base64YamlDecode(env.DoProviderConfig, &dpc); err != nil { + return nil, err + } + + return do.NewDoProviderClient(node, cpd, dpc, gfs), nil case "gcp": panic("not implemented") } - return awsClient{}, nil + + return nil, nil }), ) diff --git a/apps/nodectrl/internal/domain/terraform-utils/.dockerignore b/apps/nodectrl/internal/domain/terraform-utils/.dockerignore deleted file mode 100644 index 002bae2c9..000000000 --- a/apps/nodectrl/internal/domain/terraform-utils/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -aws-test* -do-test* -consts.go diff --git a/apps/nodectrl/internal/domain/terraform-utils/aws-test.go b/apps/nodectrl/internal/domain/terraform-utils/aws-test.go deleted file mode 100644 index d12bd9d0e..000000000 --- a/apps/nodectrl/internal/domain/terraform-utils/aws-test.go +++ /dev/null @@ -1,53 +0,0 @@ -package terraformutils - -import ( - "fmt" -) - -func testAwsClient() { - env := GetEnvOrDie() - - awsp := NewAWSProvider(AWSProvider{ - AccessKey: "***REMOVED***", - AccessSecret: "***REMOVED***", - AccountId: "kl-core", - }, AWSProviderEnv{ - StorePath: "/home/vision/tf", - TfTemplates: "/home/vision/kloudlite/api-go/pkg/infraClient/terraform", - Secrets: env.Secret, - SSHPath: "/home/vision/.ssh", - }) - - var err error - - node := AWSNode{ - NodeId: "aws-worker-01", - Region: "ap-south-1", - InstanceType: "m5.large", - VPC: "", - } - - if false { - - if err = awsp.NewNode(node); err != nil { - fmt.Println(err) - return - } - - if err = awsp.AttachNode(node); err != nil { - fmt.Println(err) - return - } - - } else { - - if err = awsp.DeleteNode(node); err != nil { - fmt.Println(err) - return - } - - } - - // time.Sleep(time.Second * 10) - -} diff --git a/apps/nodectrl/internal/domain/terraform-utils/aws.go b/apps/nodectrl/internal/domain/terraform-utils/aws.go deleted file mode 100644 index da9273fcb..000000000 --- a/apps/nodectrl/internal/domain/terraform-utils/aws.go +++ /dev/null @@ -1,305 +0,0 @@ -package terraformutils - -import ( - "encoding/base64" - "fmt" - "os" - "os/exec" - "path" - "strings" - "time" - - "gopkg.in/yaml.v3" -) - -type awsProvider struct { - accessKey string - accessSecret string - - SSHPath string - accountId string - secrets string - providerDir string - storePath string - tfTemplates string - labels map[string]string - taints []string -} - -type AWSNode struct { - NodeId string - Region string - InstanceType string - VPC string - ImageId string -} - -type awsProviderClient interface { - NewNode(node AWSNode) error - DeleteNode(node AWSNode) error - - AttachNode(node AWSNode) error - UnattachNode(node AWSNode) error - - // mkdir(folder string) error - // rmdir(folder string) error - // getFolder(region, nodeId string) string - // initTFdir(region, nodeId string) error - // applyTF(region, nodeId string, values map[string]string) error - // destroyNode(folder string) error - // execCmd(cmd string) error -} - -// getFolder implements doProviderClient -func (a *awsProvider) getFolder(region string, nodeId string) string { - // eg -> /path/acc_id/do/blr1/node_id/do - - return path.Join(a.storePath, a.accountId, a.providerDir, region, nodeId) -} - -// initTFdir implements doProviderClient -func (d *awsProvider) initTFdir(node AWSNode) error { - - folder := d.getFolder(node.Region, node.NodeId) - - if err := execCmd(fmt.Sprintf("cp -r %s %s", fmt.Sprintf("%s/%s", d.tfTemplates, d.providerDir), folder), "initialize terraform"); err != nil { - return err - } - - cmd := exec.Command("terraform", "init") - cmd.Dir = path.Join(folder, d.providerDir) - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - return cmd.Run() -} - -type TalosAmi struct { - Cloud string - Version string - Region string - Arch string - Type string - Id string -} - -// NewNode implements awsProviderClient -func (a *awsProvider) NewNode(node AWSNode) error { - - values := map[string]string{} - - values["access_key"] = a.accessKey - values["secret_key"] = a.accessSecret - - values["region"] = node.Region - values["node_id"] = node.NodeId - values["instance_type"] = node.InstanceType - values["keys-path"] = a.SSHPath - values["ami"] = node.ImageId - - // making dir - if err := mkdir(a.getFolder(node.Region, node.NodeId)); err != nil { - return err - } - - // initialize directory - if err := a.initTFdir(node); err != nil { - return err - } - - // apply terraform - return applyTF(path.Join(a.getFolder(node.Region, node.NodeId), a.providerDir), values) - -} - -// AttachNode implements awsProviderClient -func (a *awsProvider) AttachNode(node AWSNode) error { - - var out, secretYaml []byte - var err error - - if out, err = getOutput(path.Join(a.getFolder(node.Region, node.NodeId), a.providerDir), "node-ip"); err != nil { - return err - } - - if secretYaml, err = base64.StdEncoding.DecodeString(a.secrets); err != nil { - return err - } - - var sec joinTokenSecret - - if err = yaml.Unmarshal(secretYaml, &sec); err != nil { - return err - } - - labels := func() []string { - l := []string{} - for k, v := range a.labels { - l = append(l, fmt.Sprintf("--node-label %s=%s", k, v)) - } - l = append(l, fmt.Sprintf("--node-label %s=%s", "kloudlite.io/public-ip", string(out))) - return l - }() - - count := 0 - - for { - if e := execCmd( - fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s ls", - fmt.Sprintf("%v/access", a.SSHPath), - string(out)), - "checking if node is ready "); e == nil { - break - } - - count++ - if count > 24 { - return fmt.Errorf("node is not ready even after 6 minutes") - } - time.Sleep(time.Second * 15) - } - - if err = execCmd(fmt.Sprintf("kubectl get node %s", node.NodeId), "checking if node attached"); err == nil { - fmt.Println("node already attached. clean exit") - return nil - } - - // // install k3s - // if e := execCmd( - // fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s sudo sh /tmp/k3s-install.sh", - // fmt.Sprintf("%v/access", d.SSHPath), string(out)), - // ""); e != nil { - // return e - // } - - // attach node - if e := execCmd( - fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s sudo sh /tmp/k3s-install.sh agent --server %s --token %s %s --node-name %s --node-external-ip %s --node-ip %s", - fmt.Sprintf("%v/access", a.SSHPath), string(out), sec.EndpointUrl, sec.JoinToken, - strings.Join(labels, " "), node.NodeId, string(out), string(out)), - "attaching to cluster"); e != nil { - return e - } - - count = 0 - for { - if err = execCmd(fmt.Sprintf("kubectl get node %s", node.NodeId), "checking if node attached"); err == nil { - fmt.Println("node attached successfully.") - break - } - - count++ - if count > 24 { - return fmt.Errorf("node not attached even after 6minutes") - } - time.Sleep(time.Second * 15) - } - - // "hostname": node.NodeId, - // "labels": strings.Join(labels, ","), - // TODO: needs to AttachNode here - return nil - -} - -// DeleteNode implements awsProviderClient -func (a *awsProvider) DeleteNode(node AWSNode) error { - var err error - - // time.Sleep(time.Minute * 2) - values := map[string]string{} - - //TODO: remove node from cluster after drain proceed following - - values["access_key"] = a.accessKey - values["secret_key"] = a.accessSecret - - values["region"] = node.Region - values["node_id"] = node.NodeId - values["instance_type"] = node.InstanceType - values["keys-path"] = a.SSHPath - values["ami"] = node.ImageId - - nodetfpath := path.Join(a.getFolder(node.Region, node.NodeId), a.providerDir) - - // check if dir present - if _, e := os.Stat(path.Join(nodetfpath, "init.sh")); e != nil && os.IsNotExist(e) { - fmt.Println("tf state not present nothing to do") - return nil - } - - // get node name - var out []byte - if out, err = getOutput(nodetfpath, "node-name"); err != nil { - return err - } else if strings.TrimSpace(string(out)) == "" { - fmt.Println("something went wrong, can't find node_name") - return nil - } - - // destroy node - return destroyNode(nodetfpath, values) - -} - -func (a *awsProvider) UnattachNode(node AWSNode) error { - var out []byte - var err error - - if out, err = getOutput(path.Join(a.getFolder(node.Region, node.NodeId), a.providerDir), "node-name"); err != nil { - return err - } else if strings.TrimSpace(string(out)) == "" { - fmt.Println("something went wrong, can't find node_name") - return nil - } - - if err = execCmd(fmt.Sprintf("kubectl get node %s", out), "checknode present"); err != nil { - fmt.Println("node not found may be already deleted") - return nil - } - - // drain node - if err = execCmd(fmt.Sprintf("kubectl taint nodes %s force=delete:NoExecute", node.NodeId), "drain node to delete"); err != nil { - return err - } - - fmt.Println("[#] waiting 10 seconds after drain") - time.Sleep(time.Second * 10) - - // delete node - return execCmd(fmt.Sprintf("kubectl delete node %s", out), - "delete node from cluster") -} - -type AWSProvider struct { - AccessKey string - AccessSecret string - AccountId string -} - -type AWSProviderEnv struct { - StorePath string - TfTemplates string - - Labels map[string]string - Taints []string - Secrets string - SSHPath string -} - -func NewAWSProvider(provider AWSProvider, p AWSProviderEnv) awsProviderClient { - return &awsProvider{ - accessKey: provider.AccessKey, - accessSecret: provider.AccessSecret, - accountId: provider.AccountId, - - providerDir: "aws", - secrets: p.Secrets, - storePath: p.StorePath, - tfTemplates: p.TfTemplates, - labels: p.Labels, - taints: p.Taints, - SSHPath: p.SSHPath, - } -} diff --git a/apps/nodectrl/internal/domain/terraform-utils/azure.go b/apps/nodectrl/internal/domain/terraform-utils/azure.go deleted file mode 100644 index 01a8bf613..000000000 --- a/apps/nodectrl/internal/domain/terraform-utils/azure.go +++ /dev/null @@ -1,11 +0,0 @@ -package terraformutils - -type azureProvider struct { - provider string -} - -func NewAzureProvider() *azureProvider { - return &azureProvider{ - provider: "", - } -} diff --git a/apps/nodectrl/internal/domain/terraform-utils/common.go b/apps/nodectrl/internal/domain/terraform-utils/common.go deleted file mode 100644 index 8712569d7..000000000 --- a/apps/nodectrl/internal/domain/terraform-utils/common.go +++ /dev/null @@ -1,125 +0,0 @@ -package terraformutils - -import ( - "encoding/csv" - "encoding/json" - "fmt" - "os" - "os/exec" - "strings" -) - -const ( - CLUSTER_ID = "kl" -) - -// rmTFdir implements doProviderClient -// func rmdir(folder string) error { -// return execCmd(fmt.Sprintf("rm -rf %q", folder), "") -// } - -// makeTFdir implements doProviderClient -func mkdir(folder string) error { - return execCmd(fmt.Sprintf("mkdir -p %q", folder), "mkdir ") -} - -// destroyNode implements doProviderClient -func destroyNode(folder string, values map[string]string) error { - vars := []string{"destroy", "-auto-approve"} - - for k, v := range values { - vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) - } - - cmd := exec.Command("terraform", vars...) - cmd.Dir = folder - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err := cmd.Run() - if err != nil { - fmt.Println(err) - return err - } - - return os.RemoveAll(folder) - - // return err -} - -func getOutput(folder, key string) ([]byte, error) { - vars := []string{"output", "-json"} - fmt.Printf("[#] terraform %s\n", strings.Join(vars, " ")) - cmd := exec.Command("terraform", vars...) - cmd.Dir = folder - - // cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - out, err := cmd.Output() - if err != nil { - return nil, err - - } - - // fmt.Println(string(out)) - - var resp map[string]struct { - Value string `json:"value"` - } - - err = json.Unmarshal(out, &resp) - if err != nil { - return nil, err - } - - return []byte(resp[key].Value), nil -} - -// applyTF implements doProviderClient -func applyTF(folder string, values map[string]string) error { - - vars := []string{"apply", "-auto-approve"} - - fmt.Printf("[#] terraform %s", strings.Join(vars, " ")) - - for k, v := range values { - vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) - } - - cmd := exec.Command("terraform", vars...) - cmd.Dir = folder - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - cmd.Dir = folder - - return cmd.Run() -} - -func execCmd(cmdString string, logStr string) error { - r := csv.NewReader(strings.NewReader(cmdString)) - r.Comma = ' ' - cmdArr, err := r.Read() - if err != nil { - return err - } - - if logStr != "" { - fmt.Printf("[#] %s\n", logStr) - } else { - fmt.Printf("[#] %s\n", strings.Join(cmdArr, " ")) - } - - cmd := exec.Command(cmdArr[0], cmdArr[1:]...) - cmd.Stderr = os.Stderr - // cmd.Stdout = os.Stdout - - if err := cmd.Run(); err != nil { - fmt.Printf("err occurred: %s\n", err.Error()) - return err - } - return nil -} diff --git a/apps/nodectrl/internal/domain/terraform-utils/do-test.go b/apps/nodectrl/internal/domain/terraform-utils/do-test.go deleted file mode 100644 index 77cfb6fb8..000000000 --- a/apps/nodectrl/internal/domain/terraform-utils/do-test.go +++ /dev/null @@ -1,82 +0,0 @@ -package terraformutils - -import ( - "fmt" - "time" - - "github.com/codingconcepts/env" -) - -type Env struct { - Secret string `env:"SECRETS" required:"true"` -} - -func GetEnvOrDie() *Env { - var ev Env - if err := env.Set(&ev); err != nil { - panic(err) - } - return &ev -} - -func testDoClient() { - env := GetEnvOrDie() - - dop := NewDOProvider(DoProvider{ - ApiToken: "***REMOVED***", - AccountId: "kl-core", - }, DoProviderEnv{ - StorePath: "/home/vision/tf", - TfTemplates: "/home/vision/kloudlite/api-go/pkg/infraClient/terraform", - Secrets: env.Secret, - Labels: map[string]string{ - // "kloudlite.io/region": "blr1", - - }, - - SSHPath: "/home/vision/.ssh", - }) - - var err error - - node := DoNode{ - Region: "blr1", - // Size: "s-4vcpu-8gb-amd", - // Size: "s-2vcpu-4gb-amd", - // Size: "s-1vcpu-1gb-amd", - Size: "c-2", - NodeId: "try-agent-01", - ImageId: "ubuntu-22-10-x64", - } - - // fmt.Println(node, err, dop) - - if false { - - if err = dop.NewNode(node); err != nil { - fmt.Println(err) - return - } - - for { - - if err = dop.AttachNode(node); err != nil { - fmt.Println(err) - - time.Sleep(time.Second * 5) - continue - } - - return - } - - } else { - - if err = dop.DeleteNode(node); err != nil { - fmt.Println(err) - return - } - - } - -} diff --git a/apps/nodectrl/internal/domain/terraform-utils/do.go b/apps/nodectrl/internal/domain/terraform-utils/do.go deleted file mode 100644 index a07f20b1c..000000000 --- a/apps/nodectrl/internal/domain/terraform-utils/do.go +++ /dev/null @@ -1,300 +0,0 @@ -package terraformutils - -import ( - "encoding/base64" - "fmt" - "os" - "os/exec" - "path" - "strings" - "time" - - "gopkg.in/yaml.v3" -) - -type doProviderClient interface { - NewNode(node DoNode) error - DeleteNode(node DoNode) error - - AttachNode(node DoNode) error - UnattachNode(node DoNode) error - - // mkdir(folder string) error - // rmdir(folder string) error - // getFolder(region, nodeId string) string - // initTFdir(region, nodeId string) error - // applyTF(region, nodeId string, values map[string]string) error - // destroyNode(folder string) error - // execCmd(cmd string) error -} - -type DoNode struct { - Region string - Size string - NodeId string - ImageId string -} - -type joinTokenSecret struct { - JoinToken string `json:"joinToken" yaml:"joinToken"` - EndpointUrl string `json:"endpointUrl" yaml:"endPointUrl"` -} - -type doProvider struct { - apiToken string - accountId string - providerDir string - storePath string - tfTemplates string - SSHPath string - PubKey string - labels map[string]string - taints []string - secrets string -} - -// getFolder implements doProviderClient -func (d *doProvider) getFolder(region string, nodeId string) string { - // eg -> /path/do/blr1/acc_id/node_id - return path.Join(d.storePath, d.accountId, d.providerDir, region, nodeId) -} - -// initTFdir implements doProviderClient -func (d *doProvider) initTFdir(node DoNode) error { - - folder := d.getFolder(node.Region, node.NodeId) - - if err := execCmd(fmt.Sprintf("cp -r %s %s", fmt.Sprintf("%s/%s", d.tfTemplates, d.providerDir), folder), "initialize terraform"); err != nil { - return err - } - - cmd := exec.Command("terraform", "init") - cmd.Dir = path.Join(folder, d.providerDir) - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - return cmd.Run() -} - -// NewNode implements doProviderClient -func (d *doProvider) NewNode(node DoNode) error { - values := map[string]string{} - - values["cluster-id"] = CLUSTER_ID - - // values["keys-path"] = d.sshKeyPath - values["do-token"] = d.apiToken - values["accountId"] = d.accountId - - values["do-image-id"] = "ubuntu-22-10-x64" - values["nodeId"] = node.NodeId - values["size"] = node.Size - values["keys-path"] = d.SSHPath - - // making dir - if err := mkdir(d.getFolder(node.Region, node.NodeId)); err != nil { - return err - } - - // initialize directory - if err := d.initTFdir(node); err != nil { - return err - } - - tfPath := path.Join(d.getFolder(node.Region, node.NodeId), d.providerDir) - - // apply terraform - return applyTF(tfPath, values) -} - -// DeleteNode implements ProviderClient -func (d *doProvider) DeleteNode(node DoNode) error { - // time.Sleep(time.Minute * 2) - values := map[string]string{} - - //TODO: remove node from cluster after drain proceed following - - values["cluster-id"] = CLUSTER_ID - - // values["keys-path"] = d.sshKeyPath - values["do-token"] = d.apiToken - values["accountId"] = d.accountId - - // values["do-image-id"] = node.ImageId - values["do-image-id"] = "ubuntu-22-10-x64" - values["nodeId"] = node.NodeId - values["keys-path"] = d.SSHPath - - nodetfpath := path.Join(d.getFolder(node.Region, node.NodeId), d.providerDir) - - // check if dir present - if _, err := os.Stat(path.Join(nodetfpath, "init.sh")); err != nil && os.IsNotExist(err) { - fmt.Println("tf state not present nothing to do") - return nil - } - - // get node name - var out []byte - var err error - if out, err = getOutput(nodetfpath, "node-name"); err != nil { - return err - } else if strings.TrimSpace(string(out)) == "" { - fmt.Println("something went wrong, can't find node_name") - return nil - } - - // destroy node - return destroyNode(nodetfpath, values) -} - -// AttachNode implements ProviderClient -func (d *doProvider) AttachNode(node DoNode) error { - - var out, secretYaml []byte - var err error - - if out, err = getOutput(path.Join(d.getFolder(node.Region, node.NodeId), d.providerDir), "node-ip"); err != nil { - return err - } - - if secretYaml, err = base64.StdEncoding.DecodeString(d.secrets); err != nil { - fmt.Println("here", d.secrets) - return err - } - - var sec joinTokenSecret - - if err = yaml.Unmarshal(secretYaml, &sec); err != nil { - return err - } - - labels := func() []string { - l := []string{} - for k, v := range d.labels { - l = append(l, fmt.Sprintf("--node-label %s=%s", k, v)) - } - l = append(l, fmt.Sprintf("--node-label %s=%s", "kloudlite.io/public-ip", string(out))) - return l - }() - - // fmt.Println(labels) - - // "hostname": node.NodeId, - // "labels": strings.Join(labels, ","), - //check is node ready - count := 0 - - for { - if e := execCmd( - fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s ls", - fmt.Sprintf("%v/access", d.SSHPath), - string(out)), - "checking if node is ready"); e == nil { - break - } - - count++ - if count > 24 { - return fmt.Errorf("node is not ready even after 6 minutes") - } - time.Sleep(time.Second * 15) - } - - if err = execCmd(fmt.Sprintf("kubectl get node %s", node.NodeId), "checking if node attached"); err == nil { - fmt.Println("node already attached. clean exit") - return nil - } - - // // install k3s - // if e := execCmd( - // fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s sudo sh /tmp/k3s-install.sh", - // fmt.Sprintf("%v/access", d.SSHPath), string(out)), - // ""); e != nil { - // return e - // } - - // attach node - if e := execCmd( - fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s sudo sh /tmp/k3s-install.sh agent --server %s --token %s %s --node-name %s --node-external-ip %s", - fmt.Sprintf("%v/access", d.SSHPath), string(out), sec.EndpointUrl, sec.JoinToken, - strings.Join(labels, " "), node.NodeId, string(out)), - "attaching to cluster"); e != nil { - return e - } - - count = 0 - for { - if err = execCmd(fmt.Sprintf("kubectl get node %s", node.NodeId), "checking if node attached"); err == nil { - fmt.Println("node attached successfully.") - break - } - - count++ - if count > 24 { - return fmt.Errorf("node not attached even after 6minutes") - } - time.Sleep(time.Second * 15) - } - - return nil -} - -// UnattachNode implements doProviderClient -func (d *doProvider) UnattachNode(node DoNode) error { - var out []byte - var err error - - if out, err = getOutput(path.Join(d.getFolder(node.Region, node.NodeId), d.providerDir), "node-name"); err != nil { - return err - } else if strings.TrimSpace(string(out)) == "" { - fmt.Println("something went wrong, can't find node_name") - return nil - } - - if err = execCmd(fmt.Sprintf("kubectl get node %s", out), "checking if node attached"); err != nil { - fmt.Println("node not found may be already deleted") - return nil - } - - // drain node - if err = execCmd(fmt.Sprintf("kubectl taint nodes %s force=delete:NoExecute", node.NodeId), "drain node to delete"); err != nil { - return err - } - - fmt.Println("[#] waiting 10 seconds after drain") - time.Sleep(time.Second * 10) - - // delete node - return execCmd(fmt.Sprintf("kubectl delete node %s", out), - "delete node from cluster") -} - -type DoProvider struct { - ApiToken string - AccountId string -} - -type DoProviderEnv struct { - StorePath string - TfTemplates string - - SSHPath string - Secrets string - Labels map[string]string - Taints []string -} - -func NewDOProvider(provider DoProvider, p DoProviderEnv) doProviderClient { - return &doProvider{ - secrets: p.Secrets, - apiToken: provider.ApiToken, - accountId: provider.AccountId, - providerDir: "do", - storePath: p.StorePath, - tfTemplates: p.TfTemplates, - labels: p.Labels, - taints: p.Taints, - SSHPath: p.SSHPath, - } -} diff --git a/apps/nodectrl/internal/domain/terraform-utils/interface.go b/apps/nodectrl/internal/domain/terraform-utils/interface.go deleted file mode 100644 index 8e3cf7259..000000000 --- a/apps/nodectrl/internal/domain/terraform-utils/interface.go +++ /dev/null @@ -1,9 +0,0 @@ -package terraformutils - -type ProviderClient interface { - NewNode() error - DeleteNode() error - - AttachNode() error - UnattachNode() error -} diff --git a/apps/nodectrl/internal/domain/terraform-utils/main.go b/apps/nodectrl/internal/domain/terraform-utils/main.go deleted file mode 100644 index 801047eea..000000000 --- a/apps/nodectrl/internal/domain/terraform-utils/main.go +++ /dev/null @@ -1,3 +0,0 @@ -package terraformutils - - diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/e b/apps/nodectrl/internal/domain/terraform-utils/terraform/e deleted file mode 100644 index fda47e4f0..000000000 --- a/apps/nodectrl/internal/domain/terraform-utils/terraform/e +++ /dev/null @@ -1 +0,0 @@ -export SECRETS=***REMOVED*** diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/secrets.yml b/apps/nodectrl/internal/domain/terraform-utils/terraform/secrets.yml deleted file mode 100644 index 8c5f7afb0..000000000 --- a/apps/nodectrl/internal/domain/terraform-utils/terraform/secrets.yml +++ /dev/null @@ -1,2 +0,0 @@ -joinToken: ***REMOVED*** -endPointUrl: ***REMOVED*** diff --git a/apps/nodectrl/internal/domain/terraform-utils/types.go b/apps/nodectrl/internal/domain/terraform-utils/types.go deleted file mode 100644 index 8e3cf7259..000000000 --- a/apps/nodectrl/internal/domain/terraform-utils/types.go +++ /dev/null @@ -1,9 +0,0 @@ -package terraformutils - -type ProviderClient interface { - NewNode() error - DeleteNode() error - - AttachNode() error - UnattachNode() error -} diff --git a/apps/nodectrl/internal/domain/terraform/aws/init.sh b/apps/nodectrl/internal/domain/terraform/aws/init.sh deleted file mode 100644 index fa313848b..000000000 --- a/apps/nodectrl/internal/domain/terraform/aws/init.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -cat >> /root/.ssh/authorized_keys << EOF -${pubkey} -EOF - -echo "curl -sfL https://get.k3s.io | sh -s - \$@" > /tmp/k3s-install.sh && chomod +x /tmp/k3s-install.sh diff --git a/apps/nodectrl/internal/domain/terraform/aws/resource.tf b/apps/nodectrl/internal/domain/terraform/aws/resource.tf deleted file mode 100644 index e1ec3ae56..000000000 --- a/apps/nodectrl/internal/domain/terraform/aws/resource.tf +++ /dev/null @@ -1,161 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 4.16" - } - } - - required_version = ">= 1.2.0" -} - -provider "aws" { - region = var.region - access_key = var.access_key - secret_key = var.secret_key -} - - -data "aws_ami" "latest-ubuntu" { - most_recent = true - owners = ["099720109477"] # Canonical - - filter { - name = "name" - values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] - } - - filter { - name = "virtualization-type" - values = ["hvm"] - } -} - - -resource "aws_security_group" "sg" { - # name = "${var.node_id}-sg" - - ingress { - from_port = 22 - protocol = "tcp" - to_port = 22 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 6443 - protocol = "tcp" - to_port = 6443 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 8472 - protocol = "udp" - to_port = 8472 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 9100 - protocol = "tcp" - to_port = 9100 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 51820 - protocol = "udp" - to_port = 51820 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 51821 - protocol = "udp" - to_port = 51821 - cidr_blocks = ["0.0.0.0/0"] - } - - - ingress { - from_port = 10250 - protocol = "tcp" - to_port = 10250 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 80 - protocol = "tcp" - to_port = 80 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 443 - protocol = "tcp" - to_port = 443 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 30000 - protocol = "tcp" - to_port = 32768 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 30000 - protocol = "udp" - to_port = 32768 - cidr_blocks = ["0.0.0.0/0"] - } - - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - - # lifecycle { - # create_before_destroy = true - # } -} - - - -resource "aws_instance" "byoc-node" { - ami = var.ami == "" ? "${data.aws_ami.latest-ubuntu.id}" : var.ami - instance_type = var.instance_type - security_groups = [aws_security_group.sg.name] - - user_data = templatefile("./init.sh", { - pubkey = file("${var.keys-path}/access.pub") - # hostname = var.node_id - }) - - tags = { - Name = var.node_id - } - - root_block_device { - volume_size = 100 # in GB <<----- I increased this! - volume_type = "standard" - encrypted = false - # kms_key_id = data.aws_kms_key.customer_master_key.arn - } - -} - - -output "node-ip" { - value = aws_instance.byoc-node.public_ip -} - -output "node-name" { - value = var.node_id -} diff --git a/apps/nodectrl/internal/domain/terraform/aws/variables.tf b/apps/nodectrl/internal/domain/terraform/aws/variables.tf deleted file mode 100644 index 456101f16..000000000 --- a/apps/nodectrl/internal/domain/terraform/aws/variables.tf +++ /dev/null @@ -1,31 +0,0 @@ -variable "access_key" { - default = "" -} - -variable "secret_key" { - default = "" -} - -variable "region" { - default = "" -} - -variable "node_id" { - default = "" -} - -variable "instance_type" { - default = "" -} - -variable "pubkey" { - default = "" -} - -variable "keys-path" { - default = "" -} - -variable "ami" { - default = "" -} diff --git a/apps/nodectrl/internal/domain/terraform/do/init.sh b/apps/nodectrl/internal/domain/terraform/do/init.sh deleted file mode 100644 index e0a23d369..000000000 --- a/apps/nodectrl/internal/domain/terraform/do/init.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -cat > /root/.ssh/authorized_keys << EOF -${pubkey} -EOF - -echo "curl -sfL https://get.k3s.io | sh -s - \$@" > /tmp/k3s-install.sh && chomod +x /tmp/k3s-install.sh diff --git a/apps/nodectrl/internal/domain/terraform/do/resource.tf b/apps/nodectrl/internal/domain/terraform/do/resource.tf deleted file mode 100644 index 792dfdea0..000000000 --- a/apps/nodectrl/internal/domain/terraform/do/resource.tf +++ /dev/null @@ -1,36 +0,0 @@ -terraform { - required_providers { - digitalocean = { - source = "digitalocean/digitalocean" - version = "2.22.3" - } - tls = { - source = "hashicorp/tls" - version = "4.0.3" - } - } -} - -provider "digitalocean" { - token = var.do-token -} - -resource "digitalocean_droplet" "byoc-node" { - image = var.do-image-id - name = var.nodeId - region = var.region - size = var.size - ssh_keys = var.ssh_keys - user_data = templatefile("./init.sh", { - pubkey = file("${var.keys-path}/access.pub") - }) - -} - -output "node-ip" { - value = digitalocean_droplet.byoc-node.ipv4_address -} - -output "node-name" { - value = digitalocean_droplet.byoc-node.name -} diff --git a/apps/nodectrl/internal/domain/terraform/do/variables.tf b/apps/nodectrl/internal/domain/terraform/do/variables.tf deleted file mode 100644 index f78fac56b..000000000 --- a/apps/nodectrl/internal/domain/terraform/do/variables.tf +++ /dev/null @@ -1,40 +0,0 @@ -variable "cluster-id" { - default = "kl" -} - -variable "do-token" { - default = "" -} - -variable "accountId" { - default = "" -} - -variable "nodeId" { - default = "" -} - -variable "size" { - default = "s-4vcpu-8gb" -} - -variable "region" { - default = "blr1" -} - -variable "keys-path" { - # default = "" -} - -# variable "pubkey" { -# # default = "" -# } - -variable "do-image-id" { - default = "ubuntu-22-10-x64" - # default = "105910703" -} - -variable "ssh_keys" { - default = ["25:d8:56:2b:70:15:43:c5:dd:e2:ff:d7:47:1b:68:22"] -} diff --git a/apps/nodectrl/internal/domain/terraform/e b/apps/nodectrl/internal/domain/terraform/e deleted file mode 100644 index fda47e4f0..000000000 --- a/apps/nodectrl/internal/domain/terraform/e +++ /dev/null @@ -1 +0,0 @@ -export SECRETS=***REMOVED*** diff --git a/apps/nodectrl/internal/domain/terraform/secrets.yml b/apps/nodectrl/internal/domain/terraform/secrets.yml deleted file mode 100644 index 8c5f7afb0..000000000 --- a/apps/nodectrl/internal/domain/terraform/secrets.yml +++ /dev/null @@ -1,2 +0,0 @@ -joinToken: ***REMOVED*** -endPointUrl: ***REMOVED*** diff --git a/apps/nodectrl/internal/domain/utils/common.go b/apps/nodectrl/internal/domain/utils/common.go index 7bfd6c020..3436df7f7 100644 --- a/apps/nodectrl/internal/domain/utils/common.go +++ b/apps/nodectrl/internal/domain/utils/common.go @@ -1,15 +1,23 @@ package utils import ( + "context" "encoding/base64" "encoding/csv" "encoding/json" "fmt" "os" "os/exec" + "path" "strings" + "github.com/containerd/continuity/fs" "gopkg.in/yaml.v2" + mongogridfs "kloudlite.io/pkg/mongo-gridfs" +) + +const ( + Workdir string = "/tmp/tf-workdir" ) func Base64YamlDecode(in string, out interface{}) error { @@ -23,6 +31,117 @@ func Base64YamlDecode(in string, out interface{}) error { return yaml.Unmarshal(rawDecodedText, out) } +func SaveToDb(ctx context.Context, nodeId string, gfs mongogridfs.GridFs) error { + /* + Steps: + - compress the workdir into zip + - check if file present. if yes, upsert file else upload file + */ + + dir := path.Join(Workdir, nodeId) + filename := fmt.Sprintf("%s.zip", nodeId) + + // compress the workdir and upsert to db + if err := func() error { + if _, err := os.Stat(dir); err != nil { + return err + } + + source := fmt.Sprintf("%s.zip", dir) + + // compress + if err := ZipSource(dir, source); err != nil { + return err + } + + if err := gfs.Upsert(ctx, filename, source); err != nil { + return err + } + + return nil + }(); err != nil { + fmt.Println(ColorText(fmt.Sprint("Error: ", err), 1)) + return err + } + + return nil +} + +const ( + enableClear bool = false +) + +func CreateNodeWorkDir(nodeId string) error { + dir := path.Join(Workdir, nodeId) + if _, err := os.Stat(dir); err != nil { + return os.Mkdir(dir, os.ModePerm) + } + + if enableClear { + if err := os.RemoveAll(dir); err != nil { + return err + } + + return os.Mkdir(dir, os.ModePerm) + } else { + return nil + } +} + +func SetupGetWorkDir() error { + if _, err := os.Stat(Workdir); err != nil { + return os.Mkdir(Workdir, os.ModePerm) + } + return nil +} + +func MakeTfWorkFileReady(ctx context.Context, nodeId, tfPath string, gfs mongogridfs.GridFs, createIfNotExists bool) error { + + filename := fmt.Sprintf("%s.zip", nodeId) + // check if file exists in db + gf, err := gfs.FetchFileRef(ctx, filename) + if err != nil { + return err + } + + // not found create new dir + if gf == nil { + if !createIfNotExists { + return fmt.Errorf("no state file found with the nodeId %s to operate", nodeId) + } + + if err := CreateNodeWorkDir(nodeId); err != nil { + return err + } + + // a.tfTemplates + if err := fs.CopyDir(path.Join(Workdir, nodeId), tfPath); err != nil { + return err + } + + return nil + } + + // found file in db, download and extract to the workdir + fmt.Println(gf.Name, "found, extract it by downloading") + + source := path.Join(Workdir, filename) + // Download from db + if err := gfs.Download(ctx, filename, source); err != nil { + return err + } + + if s, err := Unzip(source, path.Join(Workdir)); err != nil { + return err + } else { + for _, v := range s { + fmt.Print(v, " \n") + } + } + + return nil +} + func ColorText(text interface{}, code int) string { return fmt.Sprintf("\033[38;05;%dm%v\033[0m", code, text) } @@ -74,31 +193,6 @@ func Mkdir(folder string) error { return ExecCmd(fmt.Sprintf("mkdir -p %q", folder), "mkdir ") } -// destroyNode implements doProviderClient -func DestroyNode(folder string, values map[string]string) error { - vars := []string{"destroy", "-auto-approve"} - - for k, v := range values { - vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) - } - - cmd := exec.Command("terraform", vars...) - cmd.Dir = folder - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err := cmd.Run() - if err != nil { - fmt.Println(err) - return err - } - - return os.RemoveAll(folder) - - // return err -} - func getOutput(folder, key string) ([]byte, error) { vars := []string{"output", "-json"} fmt.Printf("[#] terraform %s\n", strings.Join(vars, " ")) @@ -128,6 +222,16 @@ func getOutput(folder, key string) ([]byte, error) { return []byte(resp[key].Value), nil } +func InitTFdir(dir string) error { + cmd := exec.Command("terraform", "init") + cmd.Dir = dir + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd.Run() +} + // applyTF implements doProviderClient func ApplyTF(folder string, values map[string]string) error { @@ -149,3 +253,55 @@ func ApplyTF(folder string, values map[string]string) error { return cmd.Run() } + +// destroyNode implements doProviderClient +func DestroyNode(nodeId string, values map[string]string) error { + dest := path.Join(Workdir, nodeId) + vars := []string{"destroy", "-auto-approve"} + + for k, v := range values { + vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) + } + + cmd := exec.Command("terraform", vars...) + cmd.Dir = dest + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err := cmd.Run() + if err != nil { + fmt.Println(err) + return err + } + return nil +} + +func GetOutput(folder, key string) ([]byte, error) { + vars := []string{"output", "-json"} + fmt.Printf("[#] terraform %s\n", strings.Join(vars, " ")) + cmd := exec.Command("terraform", vars...) + cmd.Dir = folder + + // cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + out, err := cmd.Output() + if err != nil { + return nil, err + + } + + // fmt.Println(string(out)) + + var resp map[string]struct { + Value string `json:"value"` + } + + err = json.Unmarshal(out, &resp) + if err != nil { + return nil, err + } + + return []byte(resp[key].Value), nil +} diff --git a/apps/nodectrl/internal/domain/utils/dump.go b/apps/nodectrl/internal/domain/utils/dump.go deleted file mode 100644 index 73fab6859..000000000 --- a/apps/nodectrl/internal/domain/utils/dump.go +++ /dev/null @@ -1,127 +0,0 @@ -package utils - -import ( - "bytes" - "context" - "fmt" - "io" - "os" - "time" - - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/gridfs" - "go.mongodb.org/mongo-driver/mongo/options" -) - -func Upload(bucket *gridfs.Bucket) error { - fileName := "ram.zip" - file, err := os.Open(fileName) - if err != nil { - return err - } - - uploadOpts := options.GridFSUpload().SetMetadata(bson.D{{"metadata tag", "first"}}) - objectID, err := bucket.UploadFromStream(fileName, io.Reader(file), - uploadOpts) - if err != nil { - return err - } - fmt.Printf("New file uploaded with ID %s", objectID) - return nil - -} - -func getFiles(bucket *gridfs.Bucket) error { - filter := bson.D{{}} - cursor, err := bucket.Find(filter) - if err != nil { - return err - } - type gridfsFile struct { - Id string `bson:"_id"` - Name string `bson:"filename"` - Length int64 `bson:"length"` - } - var foundFiles []gridfsFile - if err = cursor.All(context.TODO(), &foundFiles); err != nil { - panic(err) - } - for _, file := range foundFiles { - fmt.Printf("filename: %s, length: %d, id: %s\n", file.Name, file.Length, file.Id) - } - - return nil -} - -func Delete(bucket *gridfs.Bucket, id string) error { - _id, err := primitive.ObjectIDFromHex(id) - if err != nil { - return err - } - if err := bucket.Delete(_id); err != nil { - return err - } - return nil -} - -func Download(bucket *gridfs.Bucket, id string) error { - - _id, err := primitive.ObjectIDFromHex(id) - if err != nil { - return err - } - fileBuffer := bytes.NewBuffer(nil) - if _, err := bucket.DownloadToStream(_id, fileBuffer); err != nil { - panic(err) - } - - if err := os.WriteFile("out/hello.zip", fileBuffer.Bytes(), os.ModePerm); err != nil { - return err - } - return nil - -} - -func Update(bucket *gridfs.Bucket, id string) error { - if err := Delete(bucket, id); err != nil { - return err - } - // bucket.GetChunksCollection().UpdateByID(ctx, id , update , opts ...*options.UpdateOptions) - return nil -} - -func M() error { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017")) - - if err != nil { - return err - } - - db := client.Database("myfiles") - bucket, err := gridfs.NewBucket(db) - if err != nil { - return err - } - - // if err := Upload(bucket); err != nil { - // return err - // } - - if err := getFiles(bucket); err != nil { - return err - } - - if err := Download(bucket, "6475cdb55687ddf79ce71c6b"); err != nil { - return err - } - - return nil -} - -func DumpSample() error { - return M() -} diff --git a/apps/nodectrl/internal/domain/utils/zipper.go b/apps/nodectrl/internal/domain/utils/zipper.go index 5a3754427..2accfe1d0 100644 --- a/apps/nodectrl/internal/domain/utils/zipper.go +++ b/apps/nodectrl/internal/domain/utils/zipper.go @@ -161,7 +161,7 @@ func ExtractZip(src, destination string) error { return nil } -func MutateOperation() error { +func mutateOperation() error { file, err := ioutil.TempFile("out", "prefix_") if err != nil { @@ -171,13 +171,13 @@ func MutateOperation() error { return os.WriteFile(file.Name(), []byte("hi"), os.ModePerm) } -func ZipSample() error { +func TestZip() error { zipName, dirName := "ram.zip", "out" if err := ExtractZip(zipName, dirName); err != nil { return err } - if err := MutateOperation(); err != nil { + if err := mutateOperation(); err != nil { return err } diff --git a/apps/nodectrl/main.go b/apps/nodectrl/main.go index c8ee11abc..02ddc0276 100644 --- a/apps/nodectrl/main.go +++ b/apps/nodectrl/main.go @@ -15,7 +15,7 @@ func main() { flag.Parse() fx.New( fx.Provide(env.LoadEnv), - fx.NopLogger, + // fx.NopLogger, fx.Provide( func() (logging.Logger, error) { return logging.New(&logging.Options{Name: "nodectrl", Dev: isDev}) diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/aws/init.sh b/apps/nodectrl/terraform/aws/init.sh similarity index 100% rename from apps/nodectrl/internal/domain/terraform-utils/terraform/aws/init.sh rename to apps/nodectrl/terraform/aws/init.sh diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/aws/resource.tf b/apps/nodectrl/terraform/aws/resource.tf similarity index 100% rename from apps/nodectrl/internal/domain/terraform-utils/terraform/aws/resource.tf rename to apps/nodectrl/terraform/aws/resource.tf diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/aws/variables.tf b/apps/nodectrl/terraform/aws/variables.tf similarity index 100% rename from apps/nodectrl/internal/domain/terraform-utils/terraform/aws/variables.tf rename to apps/nodectrl/terraform/aws/variables.tf diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/do/init.sh b/apps/nodectrl/terraform/do/init.sh similarity index 100% rename from apps/nodectrl/internal/domain/terraform-utils/terraform/do/init.sh rename to apps/nodectrl/terraform/do/init.sh diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/do/resource.tf b/apps/nodectrl/terraform/do/resource.tf similarity index 100% rename from apps/nodectrl/internal/domain/terraform-utils/terraform/do/resource.tf rename to apps/nodectrl/terraform/do/resource.tf diff --git a/apps/nodectrl/internal/domain/terraform-utils/terraform/do/variables.tf b/apps/nodectrl/terraform/do/variables.tf similarity index 100% rename from apps/nodectrl/internal/domain/terraform-utils/terraform/do/variables.tf rename to apps/nodectrl/terraform/do/variables.tf diff --git a/apps/nodectrl2/.dockerignore b/apps/nodectrl2/.dockerignore deleted file mode 100644 index c8899319b..000000000 --- a/apps/nodectrl2/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -*secrets diff --git a/apps/nodectrl2/Dockerfile b/apps/nodectrl2/Dockerfile deleted file mode 100644 index 57b0cf83b..000000000 --- a/apps/nodectrl2/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -# syntax=docker/dockerfile:1.4 -FROM golang:1.18.3-alpine3.16 AS base -USER 1001 -ENV GOPATH=/tmp/go -ENV GOCACHE=/tmp/go-cache -WORKDIR /tmp/app -COPY --chown=1001 --from=project-root ./go.mod ./go.sum ./tools.go ./ -RUN go mod download -x -COPY --chown=1001 --from=project-root pkg ./pkg -ARG APP -RUN mkdir -p ./apps/$APP -WORKDIR /tmp/app/apps/$APP -COPY --chown=1001 ./ ./ -RUN CGO_ENABLED=0 go build -tags musl -o /tmp/bin/$APP ./main.go -RUN chmod +x /tmp/bin/$APP - -#FROM gcr.io/distroless/static-debian11 -FROM alpine -RUN adduser -D -h /home/nonroot nonroot -RUN apk add curl git openssh-client -RUN cd bin && curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && chmod +x kubectl -RUN cd bin && curl -o tf.zip https://releases.hashicorp.com/terraform/1.2.9/terraform_1.2.9_linux_amd64.zip && unzip tf.zip -# RUN cd bin && curl -L0 -o talosctl https://github.com/siderolabs/talos/releases/download/v1.2.3/talosctl-linux-amd64 && chmod +x talosctl -USER nonroot -WORKDIR /tmp/app -COPY --chown=nonroot --from=base /tmp/bin/nodectrl ./nodectrl -COPY --chown=nonroot --from=project-root ./pkg/infraClient/terraform /templates/terraform -# COPY --chown=nonroot --from=project-root ./pkg/infraClient/templates /tmp/app/pkg/infraClient/templates -CMD ["./nodectrl"] diff --git a/apps/nodectrl2/Taskfile.yml b/apps/nodectrl2/Taskfile.yml deleted file mode 100644 index c19312618..000000000 --- a/apps/nodectrl2/Taskfile.yml +++ /dev/null @@ -1,27 +0,0 @@ -version: "3" - -dotenv: - - .secrets/env - -tasks: - run: - sources: - - ./internal/**/*.go - - ./main.go - cmds: - # - go run -tags dynamic main.go --dev - - nodemon -e go --signal SIGKILL --exec 'go run main.go --dev || exit 1' - - docker-build: - vars: - APP: nodectrl - IMAGE: registry.kloudlite.io/kloudlite/{{.EnvName}}/{{.APP}}:{{.Tag}} - preconditions: - - sh: '[[ -n "{{.Tag}}" ]]' - msg: 'var Tag must have a value' - - - sh: '[[ "{{.EnvName}}" == "development" ]] || [[ "{{.EnvName}}" == "staging" ]] || [[ "{{.EnvName}}" == "production" ]]' - msg: 'var EnvName must have one of [development, staging, production] as its value' - cmds: - - docker buildx build -f ./Dockerfile -t {{.IMAGE}} . --build-arg APP={{.APP}} --platform linux/amd64 --build-context project-root=../.. - - docker push {{.IMAGE}} diff --git a/apps/nodectrl2/internal/app/main.go b/apps/nodectrl2/internal/app/main.go deleted file mode 100644 index dec13f138..000000000 --- a/apps/nodectrl2/internal/app/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package app - -import ( - "go.uber.org/fx" - "kloudlite.io/apps/nodecontroller/internal/domain" -) - -var Module = fx.Module( - "app", - domain.Module, -) diff --git a/apps/nodectrl2/internal/domain/aws.go b/apps/nodectrl2/internal/domain/aws.go deleted file mode 100644 index 4b324380c..000000000 --- a/apps/nodectrl2/internal/domain/aws.go +++ /dev/null @@ -1,103 +0,0 @@ -package domain - -import ( - "encoding/base64" - "encoding/json" - "errors" - "fmt" - - "gopkg.in/yaml.v3" - infraclient "kloudlite.io/pkg/infraClient" -) - -type awsConfig struct { - Version string `yaml:"version"` - Action string `yaml:"action"` - Provider string `yaml:"provider"` - Spec struct { - Provider struct { - AccessKey string `yaml:"accessKey"` - AccessSecret string `yaml:"accessSecret"` - AccountId string `yaml:"accountId"` - } `yaml:"provider"` - Node struct { - Region string `yaml:"region"` - InstanceType string `yaml:"instanceType"` - NodeId string `yaml:"nodeId"` - VPC string `yaml:"vpc"` - } `yaml:"node"` - } `yaml:"spec"` -} - -func (d *domainI) doWithAWS() error { - - out, err := base64.StdEncoding.DecodeString(d.env.Config) - if err != nil { - return err - } - var awsConf awsConfig - e := yaml.Unmarshal(out, &awsConf) - if e != nil { - return e - } - klConf, err := d.getKlConf() - if err != nil { - return err - } - - labels := map[string]string{} - if e := json.Unmarshal([]byte(d.env.Labels), &labels); e != nil { - fmt.Println(e) - } - - taints := []string{} - if e := json.Unmarshal([]byte(d.env.Taints), &taints); e != nil { - fmt.Println(e) - } - - awsProvider := infraclient.NewAWSProvider(infraclient.AWSProvider{ - AccessKey: awsConf.Spec.Provider.AccessKey, - AccessSecret: awsConf.Spec.Provider.AccessSecret, - AccountId: awsConf.Spec.Provider.AccountId, - }, infraclient.AWSProviderEnv{ - StorePath: klConf.Values.StorePath, - TfTemplates: klConf.Values.TfTemplates, - Labels: labels, - Taints: taints, - Secrets: klConf.Values.Secrets, - SSHPath: klConf.Values.SSHPath, - }) - - awsNode := infraclient.AWSNode{ - NodeId: awsConf.Spec.Node.NodeId, - Region: awsConf.Spec.Node.Region, - InstanceType: awsConf.Spec.Node.InstanceType, - VPC: awsConf.Spec.Node.VPC, - } - - // return nil - - switch awsConf.Action { - case "create": - err = awsProvider.NewNode(awsNode) - if err != nil { - return err - } - err = awsProvider.AttachNode(awsNode) - if err != nil { - return err - } - - case "delete": - err = awsProvider.DeleteNode(awsNode) - - if err != nil { - return err - } - - default: - return errors.New("wrong action") - } - - return nil -} diff --git a/apps/nodectrl2/internal/domain/do.go b/apps/nodectrl2/internal/domain/do.go deleted file mode 100644 index d0bea72b4..000000000 --- a/apps/nodectrl2/internal/domain/do.go +++ /dev/null @@ -1,106 +0,0 @@ -package domain - -import ( - "encoding/base64" - "encoding/json" - "errors" - "fmt" - - "gopkg.in/yaml.v3" - infraclient "kloudlite.io/pkg/infraClient" -) - -type doConfig struct { - Version string `yaml:"version"` - Action string `yaml:"action"` - Provider string `yaml:"provider"` - Spec struct { - Provider struct { - ApiToken string `yaml:"apiToken"` - AccountId string `yaml:"accountId"` - } `yaml:"provider"` - Node struct { - Region string `yaml:"region"` - Size string `yaml:"size"` - NodeId string `yaml:"nodeId"` - ImageId string `yaml:"imageId"` - } `yaml:"node"` - } `yaml:"spec"` -} - -func (d *domainI) doWithDO() error { - - out, err := base64.StdEncoding.DecodeString(d.env.Config) - if err != nil { - fmt.Println("here") - return err - } - - var doConf doConfig - e := yaml.Unmarshal(out, &doConf) - if e != nil { - return e - } - klConf, err := d.getKlConf() - if err != nil { - fmt.Println("here") - return err - } - - labels := map[string]string{} - if e := json.Unmarshal([]byte(d.env.Labels), &labels); e != nil { - fmt.Println(e) - } - - taints := []string{} - if e := json.Unmarshal([]byte(d.env.Taints), &taints); e != nil { - fmt.Println(e) - } - - doProvider := infraclient.NewDOProvider(infraclient.DoProvider{ - ApiToken: doConf.Spec.Provider.ApiToken, - AccountId: doConf.Spec.Provider.AccountId, - }, infraclient.DoProviderEnv{ - StorePath: klConf.Values.StorePath, - TfTemplates: klConf.Values.TfTemplates, - Secrets: klConf.Values.Secrets, - Labels: labels, - Taints: taints, - SSHPath: klConf.Values.SSHPath, - }) - - doNode := infraclient.DoNode{ - Region: doConf.Spec.Node.Region, - Size: doConf.Spec.Node.Size, - NodeId: doConf.Spec.Node.NodeId, - ImageId: doConf.Spec.Node.ImageId, - } - - // return nil - - switch doConf.Action { - case "create": - if err = doProvider.NewNode(doNode); err != nil { - return err - } - - if err = doProvider.AttachNode(doNode); err != nil { - return err - } - - case "delete": - - if err = doProvider.UnattachNode(doNode); err != nil { - return err - } - - if err = doProvider.DeleteNode(doNode); err != nil { - return err - } - - default: - return errors.New("wrong action") - } - - return nil -} diff --git a/apps/nodectrl2/internal/domain/main.go b/apps/nodectrl2/internal/domain/main.go deleted file mode 100644 index 2cb7dd50a..000000000 --- a/apps/nodectrl2/internal/domain/main.go +++ /dev/null @@ -1,91 +0,0 @@ -package domain - -import ( - "encoding/base64" - "errors" - "fmt" - - "gopkg.in/yaml.v3" - "kloudlite.io/pkg/config" - - "go.uber.org/fx" -) - -type domainI struct { - env *Env -} - -type KLConf struct { - Version string `yaml:"version"` - Values struct { - StorePath string `yaml:"storePath"` - TfTemplates string `yaml:"tfTemplatesPath"` - Secrets string `yaml:"secrets"` - SSHPath string `yaml:"sshPath"` - PubKey string `yaml:"pubkey"` - } `yaml:"spec"` -} - -func (d *domainI) getKlConf() (*KLConf, error) { - out, err := base64.StdEncoding.DecodeString(d.env.KLConfig) - if err != nil { - fmt.Println("here") - return nil, err - } - - var klConf KLConf - e := yaml.Unmarshal(out, &klConf) - if e != nil { - - return nil, e - } - - return &klConf, nil -} - -// startJob implements Domain -func (d *domainI) StartJob() error { - - switch d.env.Provider { - case "do": - if err := d.doWithDO(); err != nil { - return err - } - - case "aws": - if err := d.doWithAWS(); err != nil { - return err - } - - default: - return errors.New("this type of provider not suported") - } - return nil -} - -func fxDomain(env *Env) Domain { - return &domainI{ - env: env, - } -} - -type Env struct { - Config string `env:"NODE_CONFIG" required:"true"` - Provider string `env:"PROVIDER" required:"true"` - KLConfig string `env:"KL_CONFIG" required:"true"` - Labels string `env:"LABELS" required:"true"` - Taints string `env:"TAINTS" required:"true"` -} - -var Module = fx.Module( - "domain", - config.EnvFx[Env](), - fx.Provide(fxDomain), -) - -/* -main - -> framework () - -> app () - -> domain (main logic) -*/ diff --git a/apps/nodectrl2/internal/domain/port.go b/apps/nodectrl2/internal/domain/port.go deleted file mode 100644 index 184633284..000000000 --- a/apps/nodectrl2/internal/domain/port.go +++ /dev/null @@ -1,5 +0,0 @@ -package domain - -type Domain interface { - StartJob() error -} diff --git a/apps/nodectrl2/internal/framework/main.go b/apps/nodectrl2/internal/framework/main.go deleted file mode 100644 index 7dd720d14..000000000 --- a/apps/nodectrl2/internal/framework/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package framework - -import ( - "fmt" - "os" - - "go.uber.org/fx" - "kloudlite.io/apps/nodectrl/internal/app" - "kloudlite.io/apps/nodectrl/internal/domain" -) - -var Module = fx.Module( - "framework", - app.Module, - fx.Invoke(func(d domain.Domain, shutdowner fx.Shutdowner) { - err := d.StartJob() - if err != nil { - fmt.Println(err) - os.Exit(1) - } else { - shutdowner.Shutdown() - } - }), -) diff --git a/apps/nodectrl2/main.go b/apps/nodectrl2/main.go deleted file mode 100644 index 64ad2e0a2..000000000 --- a/apps/nodectrl2/main.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "flag" - - "go.uber.org/fx" - "kloudlite.io/apps/nodectrl/internal/framework" - "kloudlite.io/pkg/logging" -) - -func main() { - var isDev bool - flag.BoolVar(&isDev, "dev", false, "--dev") - flag.Parse() - fx.New( - framework.Module, - fx.Provide( - func() (logging.Logger, error) { - return logging.New(&logging.Options{Name: "auth", Dev: isDev}) - }, - ), - // fx.NopLogger, - ).Run() -} - diff --git a/apps/nodectrl2/task.md b/apps/nodectrl2/task.md deleted file mode 100644 index 9ab842f47..000000000 --- a/apps/nodectrl2/task.md +++ /dev/null @@ -1,11 +0,0 @@ -# Tasks - -[ ] lib to create node - [ ] digital ocean - [ ] aws - [ ] azure - [ ] gcp -[ ] read message from kakfa -[ ] execute the message -[ ] commit the message as processed ( according to condition ) -[ ] apply the status in resource annotation of the resource diff --git a/go.mod b/go.mod index def330fbd..a5446d6a0 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 github.com/bradleyfalzon/ghinstallation/v2 v2.0.4 github.com/codingconcepts/env v0.0.0-20200821220118-a8fbf8d84482 + github.com/containerd/continuity v0.4.1 github.com/go-redis/redis/v8 v8.11.5 github.com/gobuffalo/flect v1.0.2 github.com/gofiber/adaptor/v2 v2.1.23 @@ -35,6 +36,7 @@ require ( go.mongodb.org/mongo-driver v1.9.1 go.uber.org/fx v1.17.1 go.uber.org/zap v1.24.0 + golang.org/x/net v0.8.0 golang.org/x/oauth2 v0.3.0 golang.org/x/sync v0.1.0 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb @@ -64,6 +66,7 @@ require ( contrib.go.opencensus.io/exporter/prometheus v0.4.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/Microsoft/go-winio v0.6.0 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/andybalholm/brotli v1.0.4 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -131,6 +134,7 @@ require ( github.com/seancfoley/ipaddress-go v1.5.3 // indirect github.com/sendgrid/rest v2.6.9+incompatible // indirect github.com/shopspring/decimal v1.3.1 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/tektoncd/pipeline v0.43.2 // indirect @@ -152,7 +156,6 @@ require ( go.uber.org/multierr v1.8.0 // indirect golang.org/x/crypto v0.5.0 // indirect golang.org/x/mod v0.8.0 // indirect - golang.org/x/net v0.8.0 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/term v0.6.0 // indirect golang.org/x/text v0.8.0 // indirect diff --git a/go.sum b/go.sum index 7c002a11c..8e22c8913 100644 --- a/go.sum +++ b/go.sum @@ -49,6 +49,8 @@ github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7Y github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= @@ -93,6 +95,8 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/codingconcepts/env v0.0.0-20200821220118-a8fbf8d84482 h1:5/aEFreBh9hH/0G+33xtczJCvMaulqsm9nDuu2BZUEo= github.com/codingconcepts/env v0.0.0-20200821220118-a8fbf8d84482/go.mod h1:TM9ug+H/2cI3EjyIDr5xKCkFGyNE59URgH1wu5NyU8E= +github.com/containerd/continuity v0.4.1 h1:wQnVrjIyQ8vhU2sgOiL5T07jo+ouqc2bnKsv5/EqGhU= +github.com/containerd/continuity v0.4.1/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -434,6 +438,7 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/slack-go/slack v0.11.4 h1:ojSa7KlPm3PqY2AomX4VTxEsK5eci5JaxCjlzGV5zoM= github.com/slack-go/slack v0.11.4/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -695,6 +700,7 @@ golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/infraClient/.dockerignore b/pkg/infraClient/.dockerignore deleted file mode 100644 index 002bae2c9..000000000 --- a/pkg/infraClient/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -aws-test* -do-test* -consts.go diff --git a/pkg/infraClient/aws-test.go b/pkg/infraClient/aws-test.go deleted file mode 100644 index 548e8e327..000000000 --- a/pkg/infraClient/aws-test.go +++ /dev/null @@ -1,53 +0,0 @@ -package infraclient - -import ( - "fmt" -) - -func testAwsClient() { - env := GetEnvOrDie() - - awsp := NewAWSProvider(AWSProvider{ - AccessKey: "***REMOVED***", - AccessSecret: "***REMOVED***", - AccountId: "kl-core", - }, AWSProviderEnv{ - StorePath: "/home/vision/tf", - TfTemplates: "/home/vision/kloudlite/api-go/pkg/infraClient/terraform", - Secrets: env.Secret, - SSHPath: "/home/vision/.ssh", - }) - - var err error - - node := AWSNode{ - NodeId: "aws-worker-01", - Region: "ap-south-1", - InstanceType: "m5.large", - VPC: "", - } - - if false { - - if err = awsp.NewNode(node); err != nil { - fmt.Println(err) - return - } - - if err = awsp.AttachNode(node); err != nil { - fmt.Println(err) - return - } - - } else { - - if err = awsp.DeleteNode(node); err != nil { - fmt.Println(err) - return - } - - } - - // time.Sleep(time.Second * 10) - -} diff --git a/pkg/infraClient/aws.go b/pkg/infraClient/aws.go deleted file mode 100644 index 979ba2a2a..000000000 --- a/pkg/infraClient/aws.go +++ /dev/null @@ -1,305 +0,0 @@ -package infraclient - -import ( - "encoding/base64" - "fmt" - "os" - "os/exec" - "path" - "strings" - "time" - - "gopkg.in/yaml.v3" -) - -type awsProvider struct { - accessKey string - accessSecret string - - SSHPath string - accountId string - secrets string - providerDir string - storePath string - tfTemplates string - labels map[string]string - taints []string -} - -type AWSNode struct { - NodeId string - Region string - InstanceType string - VPC string - ImageId string -} - -type awsProviderClient interface { - NewNode(node AWSNode) error - DeleteNode(node AWSNode) error - - AttachNode(node AWSNode) error - UnattachNode(node AWSNode) error - - // mkdir(folder string) error - // rmdir(folder string) error - // getFolder(region, nodeId string) string - // initTFdir(region, nodeId string) error - // applyTF(region, nodeId string, values map[string]string) error - // destroyNode(folder string) error - // execCmd(cmd string) error -} - -// getFolder implements doProviderClient -func (a *awsProvider) getFolder(region string, nodeId string) string { - // eg -> /path/acc_id/do/blr1/node_id/do - - return path.Join(a.storePath, a.accountId, a.providerDir, region, nodeId) -} - -// initTFdir implements doProviderClient -func (d *awsProvider) initTFdir(node AWSNode) error { - - folder := d.getFolder(node.Region, node.NodeId) - - if err := execCmd(fmt.Sprintf("cp -r %s %s", fmt.Sprintf("%s/%s", d.tfTemplates, d.providerDir), folder), "initialize terraform"); err != nil { - return err - } - - cmd := exec.Command("terraform", "init") - cmd.Dir = path.Join(folder, d.providerDir) - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - return cmd.Run() -} - -type TalosAmi struct { - Cloud string - Version string - Region string - Arch string - Type string - Id string -} - -// NewNode implements awsProviderClient -func (a *awsProvider) NewNode(node AWSNode) error { - - values := map[string]string{} - - values["access_key"] = a.accessKey - values["secret_key"] = a.accessSecret - - values["region"] = node.Region - values["node_id"] = node.NodeId - values["instance_type"] = node.InstanceType - values["keys-path"] = a.SSHPath - values["ami"] = node.ImageId - - // making dir - if err := mkdir(a.getFolder(node.Region, node.NodeId)); err != nil { - return err - } - - // initialize directory - if err := a.initTFdir(node); err != nil { - return err - } - - // apply terraform - return applyTF(path.Join(a.getFolder(node.Region, node.NodeId), a.providerDir), values) - -} - -// AttachNode implements awsProviderClient -func (a *awsProvider) AttachNode(node AWSNode) error { - - var out, secretYaml []byte - var err error - - if out, err = getOutput(path.Join(a.getFolder(node.Region, node.NodeId), a.providerDir), "node-ip"); err != nil { - return err - } - - if secretYaml, err = base64.StdEncoding.DecodeString(a.secrets); err != nil { - return err - } - - var sec joinTokenSecret - - if err = yaml.Unmarshal(secretYaml, &sec); err != nil { - return err - } - - labels := func() []string { - l := []string{} - for k, v := range a.labels { - l = append(l, fmt.Sprintf("--node-label %s=%s", k, v)) - } - l = append(l, fmt.Sprintf("--node-label %s=%s", "kloudlite.io/public-ip", string(out))) - return l - }() - - count := 0 - - for { - if e := execCmd( - fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s ls", - fmt.Sprintf("%v/access", a.SSHPath), - string(out)), - "checking if node is ready "); e == nil { - break - } - - count++ - if count > 24 { - return fmt.Errorf("node is not ready even after 6 minutes") - } - time.Sleep(time.Second * 15) - } - - if err = execCmd(fmt.Sprintf("kubectl get node %s", node.NodeId), "checking if node attached"); err == nil { - fmt.Println("node already attached. clean exit") - return nil - } - - // // install k3s - // if e := execCmd( - // fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s sudo sh /tmp/k3s-install.sh", - // fmt.Sprintf("%v/access", d.SSHPath), string(out)), - // ""); e != nil { - // return e - // } - - // attach node - if e := execCmd( - fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s sudo sh /tmp/k3s-install.sh agent --server %s --token %s %s --node-name %s --node-external-ip %s --node-ip %s", - fmt.Sprintf("%v/access", a.SSHPath), string(out), sec.EndpointUrl, sec.JoinToken, - strings.Join(labels, " "), node.NodeId, string(out), string(out)), - "attaching to cluster"); e != nil { - return e - } - - count = 0 - for { - if err = execCmd(fmt.Sprintf("kubectl get node %s", node.NodeId), "checking if node attached"); err == nil { - fmt.Println("node attached successfully.") - break - } - - count++ - if count > 24 { - return fmt.Errorf("node not attached even after 6minutes") - } - time.Sleep(time.Second * 15) - } - - // "hostname": node.NodeId, - // "labels": strings.Join(labels, ","), - // TODO: needs to AttachNode here - return nil - -} - -// DeleteNode implements awsProviderClient -func (a *awsProvider) DeleteNode(node AWSNode) error { - var err error - - // time.Sleep(time.Minute * 2) - values := map[string]string{} - - //TODO: remove node from cluster after drain proceed following - - values["access_key"] = a.accessKey - values["secret_key"] = a.accessSecret - - values["region"] = node.Region - values["node_id"] = node.NodeId - values["instance_type"] = node.InstanceType - values["keys-path"] = a.SSHPath - values["ami"] = node.ImageId - - nodetfpath := path.Join(a.getFolder(node.Region, node.NodeId), a.providerDir) - - // check if dir present - if _, e := os.Stat(path.Join(nodetfpath, "init.sh")); e != nil && os.IsNotExist(e) { - fmt.Println("tf state not present nothing to do") - return nil - } - - // get node name - var out []byte - if out, err = getOutput(nodetfpath, "node-name"); err != nil { - return err - } else if strings.TrimSpace(string(out)) == "" { - fmt.Println("something went wrong, can't find node_name") - return nil - } - - // destroy node - return destroyNode(nodetfpath, values) - -} - -func (a *awsProvider) UnattachNode(node AWSNode) error { - var out []byte - var err error - - if out, err = getOutput(path.Join(a.getFolder(node.Region, node.NodeId), a.providerDir), "node-name"); err != nil { - return err - } else if strings.TrimSpace(string(out)) == "" { - fmt.Println("something went wrong, can't find node_name") - return nil - } - - if err = execCmd(fmt.Sprintf("kubectl get node %s", out), "checknode present"); err != nil { - fmt.Println("node not found may be already deleted") - return nil - } - - // drain node - if err = execCmd(fmt.Sprintf("kubectl taint nodes %s force=delete:NoExecute", node.NodeId), "drain node to delete"); err != nil { - return err - } - - fmt.Println("[#] waiting 10 seconds after drain") - time.Sleep(time.Second * 10) - - // delete node - return execCmd(fmt.Sprintf("kubectl delete node %s", out), - "delete node from cluster") -} - -type AWSProvider struct { - AccessKey string - AccessSecret string - AccountId string -} - -type AWSProviderEnv struct { - StorePath string - TfTemplates string - - Labels map[string]string - Taints []string - Secrets string - SSHPath string -} - -func NewAWSProvider(provider AWSProvider, p AWSProviderEnv) awsProviderClient { - return &awsProvider{ - accessKey: provider.AccessKey, - accessSecret: provider.AccessSecret, - accountId: provider.AccountId, - - providerDir: "aws", - secrets: p.Secrets, - storePath: p.StorePath, - tfTemplates: p.TfTemplates, - labels: p.Labels, - taints: p.Taints, - SSHPath: p.SSHPath, - } -} diff --git a/pkg/infraClient/azure.go b/pkg/infraClient/azure.go deleted file mode 100644 index da2ccd9bb..000000000 --- a/pkg/infraClient/azure.go +++ /dev/null @@ -1,11 +0,0 @@ -package infraclient - -type azureProvider struct { - provider string -} - -func NewAzureProvider() *azureProvider { - return &azureProvider{ - provider: "", - } -} diff --git a/pkg/infraClient/common.go b/pkg/infraClient/common.go deleted file mode 100644 index 5acbbd941..000000000 --- a/pkg/infraClient/common.go +++ /dev/null @@ -1,125 +0,0 @@ -package infraclient - -import ( - "encoding/csv" - "encoding/json" - "fmt" - "os" - "os/exec" - "strings" -) - -const ( - CLUSTER_ID = "kl" -) - -// rmTFdir implements doProviderClient -// func rmdir(folder string) error { -// return execCmd(fmt.Sprintf("rm -rf %q", folder), "") -// } - -// makeTFdir implements doProviderClient -func mkdir(folder string) error { - return execCmd(fmt.Sprintf("mkdir -p %q", folder), "mkdir ") -} - -// destroyNode implements doProviderClient -func destroyNode(folder string, values map[string]string) error { - vars := []string{"destroy", "-auto-approve"} - - for k, v := range values { - vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) - } - - cmd := exec.Command("terraform", vars...) - cmd.Dir = folder - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err := cmd.Run() - if err != nil { - fmt.Println(err) - return err - } - - return os.RemoveAll(folder) - - // return err -} - -func getOutput(folder, key string) ([]byte, error) { - vars := []string{"output", "-json"} - fmt.Printf("[#] terraform %s\n", strings.Join(vars, " ")) - cmd := exec.Command("terraform", vars...) - cmd.Dir = folder - - // cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - out, err := cmd.Output() - if err != nil { - return nil, err - - } - - // fmt.Println(string(out)) - - var resp map[string]struct { - Value string `json:"value"` - } - - err = json.Unmarshal(out, &resp) - if err != nil { - return nil, err - } - - return []byte(resp[key].Value), nil -} - -// applyTF implements doProviderClient -func applyTF(folder string, values map[string]string) error { - - vars := []string{"apply", "-auto-approve"} - - fmt.Printf("[#] terraform %s", strings.Join(vars, " ")) - - for k, v := range values { - vars = append(vars, fmt.Sprintf("-var=%s=%s", k, v)) - } - - cmd := exec.Command("terraform", vars...) - cmd.Dir = folder - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - cmd.Dir = folder - - return cmd.Run() -} - -func execCmd(cmdString string, logStr string) error { - r := csv.NewReader(strings.NewReader(cmdString)) - r.Comma = ' ' - cmdArr, err := r.Read() - if err != nil { - return err - } - - if logStr != "" { - fmt.Printf("[#] %s\n", logStr) - } else { - fmt.Printf("[#] %s\n", strings.Join(cmdArr, " ")) - } - - cmd := exec.Command(cmdArr[0], cmdArr[1:]...) - cmd.Stderr = os.Stderr - // cmd.Stdout = os.Stdout - - if err := cmd.Run(); err != nil { - fmt.Printf("err occurred: %s\n", err.Error()) - return err - } - return nil -} diff --git a/pkg/infraClient/do-test.go b/pkg/infraClient/do-test.go deleted file mode 100644 index 6505ce256..000000000 --- a/pkg/infraClient/do-test.go +++ /dev/null @@ -1,82 +0,0 @@ -package infraclient - -import ( - "fmt" - "time" - - "github.com/codingconcepts/env" -) - -type Env struct { - Secret string `env:"SECRETS" required:"true"` -} - -func GetEnvOrDie() *Env { - var ev Env - if err := env.Set(&ev); err != nil { - panic(err) - } - return &ev -} - -func testDoClient() { - env := GetEnvOrDie() - - dop := NewDOProvider(DoProvider{ - ApiToken: "***REMOVED***", - AccountId: "kl-core", - }, DoProviderEnv{ - StorePath: "/home/vision/tf", - TfTemplates: "/home/vision/kloudlite/api-go/pkg/infraClient/terraform", - Secrets: env.Secret, - Labels: map[string]string{ - // "kloudlite.io/region": "blr1", - - }, - - SSHPath: "/home/vision/.ssh", - }) - - var err error - - node := DoNode{ - Region: "blr1", - // Size: "s-4vcpu-8gb-amd", - // Size: "s-2vcpu-4gb-amd", - // Size: "s-1vcpu-1gb-amd", - Size: "c-2", - NodeId: "try-agent-01", - ImageId: "ubuntu-22-10-x64", - } - - // fmt.Println(node, err, dop) - - if false { - - if err = dop.NewNode(node); err != nil { - fmt.Println(err) - return - } - - for { - - if err = dop.AttachNode(node); err != nil { - fmt.Println(err) - - time.Sleep(time.Second * 5) - continue - } - - return - } - - } else { - - if err = dop.DeleteNode(node); err != nil { - fmt.Println(err) - return - } - - } - -} diff --git a/pkg/infraClient/do.go b/pkg/infraClient/do.go deleted file mode 100644 index 4e7768b17..000000000 --- a/pkg/infraClient/do.go +++ /dev/null @@ -1,300 +0,0 @@ -package infraclient - -import ( - "encoding/base64" - "fmt" - "os" - "os/exec" - "path" - "strings" - "time" - - "gopkg.in/yaml.v3" -) - -type doProviderClient interface { - NewNode(node DoNode) error - DeleteNode(node DoNode) error - - AttachNode(node DoNode) error - UnattachNode(node DoNode) error - - // mkdir(folder string) error - // rmdir(folder string) error - // getFolder(region, nodeId string) string - // initTFdir(region, nodeId string) error - // applyTF(region, nodeId string, values map[string]string) error - // destroyNode(folder string) error - // execCmd(cmd string) error -} - -type DoNode struct { - Region string - Size string - NodeId string - ImageId string -} - -type joinTokenSecret struct { - JoinToken string `json:"joinToken" yaml:"joinToken"` - EndpointUrl string `json:"endpointUrl" yaml:"endPointUrl"` -} - -type doProvider struct { - apiToken string - accountId string - providerDir string - storePath string - tfTemplates string - SSHPath string - PubKey string - labels map[string]string - taints []string - secrets string -} - -// getFolder implements doProviderClient -func (d *doProvider) getFolder(region string, nodeId string) string { - // eg -> /path/do/blr1/acc_id/node_id - return path.Join(d.storePath, d.accountId, d.providerDir, region, nodeId) -} - -// initTFdir implements doProviderClient -func (d *doProvider) initTFdir(node DoNode) error { - - folder := d.getFolder(node.Region, node.NodeId) - - if err := execCmd(fmt.Sprintf("cp -r %s %s", fmt.Sprintf("%s/%s", d.tfTemplates, d.providerDir), folder), "initialize terraform"); err != nil { - return err - } - - cmd := exec.Command("terraform", "init") - cmd.Dir = path.Join(folder, d.providerDir) - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - return cmd.Run() -} - -// NewNode implements doProviderClient -func (d *doProvider) NewNode(node DoNode) error { - values := map[string]string{} - - values["cluster-id"] = CLUSTER_ID - - // values["keys-path"] = d.sshKeyPath - values["do-token"] = d.apiToken - values["accountId"] = d.accountId - - values["do-image-id"] = "ubuntu-22-10-x64" - values["nodeId"] = node.NodeId - values["size"] = node.Size - values["keys-path"] = d.SSHPath - - // making dir - if err := mkdir(d.getFolder(node.Region, node.NodeId)); err != nil { - return err - } - - // initialize directory - if err := d.initTFdir(node); err != nil { - return err - } - - tfPath := path.Join(d.getFolder(node.Region, node.NodeId), d.providerDir) - - // apply terraform - return applyTF(tfPath, values) -} - -// DeleteNode implements ProviderClient -func (d *doProvider) DeleteNode(node DoNode) error { - // time.Sleep(time.Minute * 2) - values := map[string]string{} - - //TODO: remove node from cluster after drain proceed following - - values["cluster-id"] = CLUSTER_ID - - // values["keys-path"] = d.sshKeyPath - values["do-token"] = d.apiToken - values["accountId"] = d.accountId - - // values["do-image-id"] = node.ImageId - values["do-image-id"] = "ubuntu-22-10-x64" - values["nodeId"] = node.NodeId - values["keys-path"] = d.SSHPath - - nodetfpath := path.Join(d.getFolder(node.Region, node.NodeId), d.providerDir) - - // check if dir present - if _, err := os.Stat(path.Join(nodetfpath, "init.sh")); err != nil && os.IsNotExist(err) { - fmt.Println("tf state not present nothing to do") - return nil - } - - // get node name - var out []byte - var err error - if out, err = getOutput(nodetfpath, "node-name"); err != nil { - return err - } else if strings.TrimSpace(string(out)) == "" { - fmt.Println("something went wrong, can't find node_name") - return nil - } - - // destroy node - return destroyNode(nodetfpath, values) -} - -// AttachNode implements ProviderClient -func (d *doProvider) AttachNode(node DoNode) error { - - var out, secretYaml []byte - var err error - - if out, err = getOutput(path.Join(d.getFolder(node.Region, node.NodeId), d.providerDir), "node-ip"); err != nil { - return err - } - - if secretYaml, err = base64.StdEncoding.DecodeString(d.secrets); err != nil { - fmt.Println("here", d.secrets) - return err - } - - var sec joinTokenSecret - - if err = yaml.Unmarshal(secretYaml, &sec); err != nil { - return err - } - - labels := func() []string { - l := []string{} - for k, v := range d.labels { - l = append(l, fmt.Sprintf("--node-label %s=%s", k, v)) - } - l = append(l, fmt.Sprintf("--node-label %s=%s", "kloudlite.io/public-ip", string(out))) - return l - }() - - // fmt.Println(labels) - - // "hostname": node.NodeId, - // "labels": strings.Join(labels, ","), - //check is node ready - count := 0 - - for { - if e := execCmd( - fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s ls", - fmt.Sprintf("%v/access", d.SSHPath), - string(out)), - "checking if node is ready"); e == nil { - break - } - - count++ - if count > 24 { - return fmt.Errorf("node is not ready even after 6 minutes") - } - time.Sleep(time.Second * 15) - } - - if err = execCmd(fmt.Sprintf("kubectl get node %s", node.NodeId), "checking if node attached"); err == nil { - fmt.Println("node already attached. clean exit") - return nil - } - - // // install k3s - // if e := execCmd( - // fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s sudo sh /tmp/k3s-install.sh", - // fmt.Sprintf("%v/access", d.SSHPath), string(out)), - // ""); e != nil { - // return e - // } - - // attach node - if e := execCmd( - fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i %s root@%s sudo sh /tmp/k3s-install.sh agent --server %s --token %s %s --node-name %s --node-external-ip %s", - fmt.Sprintf("%v/access", d.SSHPath), string(out), sec.EndpointUrl, sec.JoinToken, - strings.Join(labels, " "), node.NodeId, string(out)), - "attaching to cluster"); e != nil { - return e - } - - count = 0 - for { - if err = execCmd(fmt.Sprintf("kubectl get node %s", node.NodeId), "checking if node attached"); err == nil { - fmt.Println("node attached successfully.") - break - } - - count++ - if count > 24 { - return fmt.Errorf("node not attached even after 6minutes") - } - time.Sleep(time.Second * 15) - } - - return nil -} - -// UnattachNode implements doProviderClient -func (d *doProvider) UnattachNode(node DoNode) error { - var out []byte - var err error - - if out, err = getOutput(path.Join(d.getFolder(node.Region, node.NodeId), d.providerDir), "node-name"); err != nil { - return err - } else if strings.TrimSpace(string(out)) == "" { - fmt.Println("something went wrong, can't find node_name") - return nil - } - - if err = execCmd(fmt.Sprintf("kubectl get node %s", out), "checking if node attached"); err != nil { - fmt.Println("node not found may be already deleted") - return nil - } - - // drain node - if err = execCmd(fmt.Sprintf("kubectl taint nodes %s force=delete:NoExecute", node.NodeId), "drain node to delete"); err != nil { - return err - } - - fmt.Println("[#] waiting 10 seconds after drain") - time.Sleep(time.Second * 10) - - // delete node - return execCmd(fmt.Sprintf("kubectl delete node %s", out), - "delete node from cluster") -} - -type DoProvider struct { - ApiToken string - AccountId string -} - -type DoProviderEnv struct { - StorePath string - TfTemplates string - - SSHPath string - Secrets string - Labels map[string]string - Taints []string -} - -func NewDOProvider(provider DoProvider, p DoProviderEnv) doProviderClient { - return &doProvider{ - secrets: p.Secrets, - apiToken: provider.ApiToken, - accountId: provider.AccountId, - providerDir: "do", - storePath: p.StorePath, - tfTemplates: p.TfTemplates, - labels: p.Labels, - taints: p.Taints, - SSHPath: p.SSHPath, - } -} diff --git a/pkg/infraClient/interface.go b/pkg/infraClient/interface.go deleted file mode 100644 index 98b6eb076..000000000 --- a/pkg/infraClient/interface.go +++ /dev/null @@ -1,8 +0,0 @@ -package infraclient - -type ProviderClient interface { - NewNode() error - DeleteNode() error - UpdateNode() error - AttachNode() error -} diff --git a/pkg/infraClient/main.go b/pkg/infraClient/main.go deleted file mode 100644 index 96dc63cfc..000000000 --- a/pkg/infraClient/main.go +++ /dev/null @@ -1,6 +0,0 @@ -package infraclient - -func InfraClientTest() { - testAwsClient() - // testDoClient() -} diff --git a/pkg/infraClient/terraform/aws/init.sh b/pkg/infraClient/terraform/aws/init.sh deleted file mode 100644 index fa313848b..000000000 --- a/pkg/infraClient/terraform/aws/init.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -cat >> /root/.ssh/authorized_keys << EOF -${pubkey} -EOF - -echo "curl -sfL https://get.k3s.io | sh -s - \$@" > /tmp/k3s-install.sh && chomod +x /tmp/k3s-install.sh diff --git a/pkg/infraClient/terraform/aws/resource.tf b/pkg/infraClient/terraform/aws/resource.tf deleted file mode 100644 index e1ec3ae56..000000000 --- a/pkg/infraClient/terraform/aws/resource.tf +++ /dev/null @@ -1,161 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 4.16" - } - } - - required_version = ">= 1.2.0" -} - -provider "aws" { - region = var.region - access_key = var.access_key - secret_key = var.secret_key -} - - -data "aws_ami" "latest-ubuntu" { - most_recent = true - owners = ["099720109477"] # Canonical - - filter { - name = "name" - values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] - } - - filter { - name = "virtualization-type" - values = ["hvm"] - } -} - - -resource "aws_security_group" "sg" { - # name = "${var.node_id}-sg" - - ingress { - from_port = 22 - protocol = "tcp" - to_port = 22 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 6443 - protocol = "tcp" - to_port = 6443 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 8472 - protocol = "udp" - to_port = 8472 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 9100 - protocol = "tcp" - to_port = 9100 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 51820 - protocol = "udp" - to_port = 51820 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 51821 - protocol = "udp" - to_port = 51821 - cidr_blocks = ["0.0.0.0/0"] - } - - - ingress { - from_port = 10250 - protocol = "tcp" - to_port = 10250 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 80 - protocol = "tcp" - to_port = 80 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 443 - protocol = "tcp" - to_port = 443 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 30000 - protocol = "tcp" - to_port = 32768 - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - from_port = 30000 - protocol = "udp" - to_port = 32768 - cidr_blocks = ["0.0.0.0/0"] - } - - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - - # lifecycle { - # create_before_destroy = true - # } -} - - - -resource "aws_instance" "byoc-node" { - ami = var.ami == "" ? "${data.aws_ami.latest-ubuntu.id}" : var.ami - instance_type = var.instance_type - security_groups = [aws_security_group.sg.name] - - user_data = templatefile("./init.sh", { - pubkey = file("${var.keys-path}/access.pub") - # hostname = var.node_id - }) - - tags = { - Name = var.node_id - } - - root_block_device { - volume_size = 100 # in GB <<----- I increased this! - volume_type = "standard" - encrypted = false - # kms_key_id = data.aws_kms_key.customer_master_key.arn - } - -} - - -output "node-ip" { - value = aws_instance.byoc-node.public_ip -} - -output "node-name" { - value = var.node_id -} diff --git a/pkg/infraClient/terraform/aws/variables.tf b/pkg/infraClient/terraform/aws/variables.tf deleted file mode 100644 index 456101f16..000000000 --- a/pkg/infraClient/terraform/aws/variables.tf +++ /dev/null @@ -1,31 +0,0 @@ -variable "access_key" { - default = "" -} - -variable "secret_key" { - default = "" -} - -variable "region" { - default = "" -} - -variable "node_id" { - default = "" -} - -variable "instance_type" { - default = "" -} - -variable "pubkey" { - default = "" -} - -variable "keys-path" { - default = "" -} - -variable "ami" { - default = "" -} diff --git a/pkg/infraClient/terraform/do/init.sh b/pkg/infraClient/terraform/do/init.sh deleted file mode 100644 index e0a23d369..000000000 --- a/pkg/infraClient/terraform/do/init.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -cat > /root/.ssh/authorized_keys << EOF -${pubkey} -EOF - -echo "curl -sfL https://get.k3s.io | sh -s - \$@" > /tmp/k3s-install.sh && chomod +x /tmp/k3s-install.sh diff --git a/pkg/infraClient/terraform/do/resource.tf b/pkg/infraClient/terraform/do/resource.tf deleted file mode 100644 index 792dfdea0..000000000 --- a/pkg/infraClient/terraform/do/resource.tf +++ /dev/null @@ -1,36 +0,0 @@ -terraform { - required_providers { - digitalocean = { - source = "digitalocean/digitalocean" - version = "2.22.3" - } - tls = { - source = "hashicorp/tls" - version = "4.0.3" - } - } -} - -provider "digitalocean" { - token = var.do-token -} - -resource "digitalocean_droplet" "byoc-node" { - image = var.do-image-id - name = var.nodeId - region = var.region - size = var.size - ssh_keys = var.ssh_keys - user_data = templatefile("./init.sh", { - pubkey = file("${var.keys-path}/access.pub") - }) - -} - -output "node-ip" { - value = digitalocean_droplet.byoc-node.ipv4_address -} - -output "node-name" { - value = digitalocean_droplet.byoc-node.name -} diff --git a/pkg/infraClient/terraform/do/variables.tf b/pkg/infraClient/terraform/do/variables.tf deleted file mode 100644 index f78fac56b..000000000 --- a/pkg/infraClient/terraform/do/variables.tf +++ /dev/null @@ -1,40 +0,0 @@ -variable "cluster-id" { - default = "kl" -} - -variable "do-token" { - default = "" -} - -variable "accountId" { - default = "" -} - -variable "nodeId" { - default = "" -} - -variable "size" { - default = "s-4vcpu-8gb" -} - -variable "region" { - default = "blr1" -} - -variable "keys-path" { - # default = "" -} - -# variable "pubkey" { -# # default = "" -# } - -variable "do-image-id" { - default = "ubuntu-22-10-x64" - # default = "105910703" -} - -variable "ssh_keys" { - default = ["25:d8:56:2b:70:15:43:c5:dd:e2:ff:d7:47:1b:68:22"] -} diff --git a/pkg/infraClient/terraform/e b/pkg/infraClient/terraform/e deleted file mode 100644 index fda47e4f0..000000000 --- a/pkg/infraClient/terraform/e +++ /dev/null @@ -1 +0,0 @@ -export SECRETS=***REMOVED*** diff --git a/pkg/infraClient/terraform/secrets.yml b/pkg/infraClient/terraform/secrets.yml deleted file mode 100644 index 8c5f7afb0..000000000 --- a/pkg/infraClient/terraform/secrets.yml +++ /dev/null @@ -1,2 +0,0 @@ -joinToken: ***REMOVED*** -endPointUrl: ***REMOVED*** diff --git a/pkg/mongo-gridfs/gridfs.go b/pkg/mongo-gridfs/gridfs.go index 2789fb216..4d8ff2a00 100644 --- a/pkg/mongo-gridfs/gridfs.go +++ b/pkg/mongo-gridfs/gridfs.go @@ -1,40 +1,144 @@ package mongogridfs -import "go.mongodb.org/mongo-driver/mongo/gridfs" +import ( + "bytes" + "context" + "fmt" + "io" + "os" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/gridfs" + "go.mongodb.org/mongo-driver/mongo/options" +) type gfs struct { bucket *gridfs.Bucket } type GridFs interface { - Upload() error - Download() error - Delete() error - Fetch() error - Search() error + Upload(ctx context.Context, filename, source string) error + Download(ctx context.Context, filename, destination string) error + Upsert(ctx context.Context, filename, source string) error + DeleteById(id string) error + GetAllFiles() ([]GridfsFile, error) + FetchFileRef(ctx context.Context, filename string) (*GridfsFile, error) + DeleteAllWithFilename(filename string) error +} + +// Delete implements GridFs +func (g *gfs) DeleteById(id string) error { + _id, err := primitive.ObjectIDFromHex(id) + if err != nil { + return err + } + return g.bucket.Delete(_id) +} + +func (g *gfs) DeleteAllWithFilename(filename string) error { + gf, err := g.GetAllFiles() + if err != nil { + return err + } + for _, gf2 := range gf { + if gf2.Name == filename { + if err := g.DeleteById(gf2.Id); err != nil { + return err + } + } + } + return nil } // Delete implements GridFs -func (*gfs) Delete() error { - panic("unimplemented") +func (g *gfs) Upsert(ctx context.Context, filename, source string) error { + if err := g.DeleteAllWithFilename(filename); err != nil { + return err + } + + return g.Upload(ctx, filename, source) } // Download implements GridFs -func (*gfs) Download() error { - panic("unimplemented") +func (g *gfs) Download(ctx context.Context, filename, destination string) error { + gf, err := g.FetchFileRef(ctx, filename) + if err != nil { + return err + } + + id, err := primitive.ObjectIDFromHex(gf.Id) + if err != nil { + return err + } + fileBuffer := bytes.NewBuffer(nil) + if _, err := g.bucket.DownloadToStream(id, fileBuffer); err != nil { + panic(err) + } + + if err := os.WriteFile(destination, fileBuffer.Bytes(), os.ModePerm); err != nil { + return err + } + return nil +} + +type Filter map[string]interface{} + +type GridfsFile struct { + Id string `bson:"_id"` + Name string `bson:"filename"` + Length int64 `bson:"length"` } -// Fetch implements GridFs -func (*gfs) Fetch() error { - panic("unimplemented") +func (g *gfs) GetAllFiles() ([]GridfsFile, error) { + filter := bson.D{{}} + // filter := bson.D{{"length", bson.D{{"$lt", 1500}}}} + cursor, err := g.bucket.Find(filter) + if err != nil { + return nil, err + } + var foundFiles []GridfsFile + if err = cursor.All(context.TODO(), &foundFiles); err != nil { + return nil, err + } + + return foundFiles, nil } // Search implements GridFs -func (*gfs) Search() error { - panic("unimplemented") +func (g *gfs) FetchFileRef(ctx context.Context, filename string) (*GridfsFile, error) { + + cursor, err := g.bucket.Find(Filter{"filename": filename}) + if err != nil { + return nil, err + } + + var foundFiles []GridfsFile + + if err = cursor.All(context.TODO(), &foundFiles); err != nil { + return nil, err + } + + if len(foundFiles) == 0 { + return nil, nil + } + + return &foundFiles[0], nil + } // Upload implements GridFs -func (*gfs) Upload() error { - panic("unimplemented") +func (g *gfs) Upload(ctx context.Context, filename, source string) error { + file, err := os.Open(source) + if err != nil { + return err + } + + uploadOpts := options.GridFSUpload() + objectID, err := g.bucket.UploadFromStream(filename, io.Reader(file), uploadOpts) + if err != nil { + return err + } + fmt.Printf("file %s uploaded with ID %s", filename, objectID) + return nil }