From dbb53c3c37334f5f9d2c5375a900735eda0df83f Mon Sep 17 00:00:00 2001 From: jianli-97 Date: Mon, 21 Aug 2023 17:39:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0deploy,=20destroy=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E8=A1=8C=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/cmd/cmd.go | 4 +- app/cmd/deploy.go | 27 +++ .../terraform/init.go => cmd/destroy.go} | 38 ++-- app/phases/infra/assets/assets.go | 22 -- app/phases/infra/assets/cluster/cluster.go | 151 -------------- app/phases/infra/assets/cluster/tfvars.go | 84 -------- app/phases/infra/infra.go | 188 ++++++++++++++++++ .../infra/terraform/providers/providers.go | 35 ---- app/phases/infra/terraform/stage.go | 26 --- .../terraform/stages/openstack/stages.go | 19 -- .../infra/terraform/stages/platform/stages.go | 19 -- app/phases/infra/terraform/stages/split.go | 43 ---- app/phases/infra/terraform/state.go | 8 +- app/phases/infra/terraform/terraform.go | 42 +++- app/phases/infra/tfvars/openstack/tfvars.go | 44 ---- 15 files changed, 272 insertions(+), 478 deletions(-) rename app/{phases/infra/terraform/init.go => cmd/destroy.go} (46%) delete mode 100755 app/phases/infra/assets/assets.go delete mode 100644 app/phases/infra/assets/cluster/cluster.go delete mode 100644 app/phases/infra/assets/cluster/tfvars.go create mode 100644 app/phases/infra/infra.go delete mode 100644 app/phases/infra/terraform/providers/providers.go delete mode 100644 app/phases/infra/terraform/stage.go delete mode 100644 app/phases/infra/terraform/stages/openstack/stages.go delete mode 100644 app/phases/infra/terraform/stages/platform/stages.go delete mode 100644 app/phases/infra/terraform/stages/split.go delete mode 100644 app/phases/infra/tfvars/openstack/tfvars.go diff --git a/app/cmd/cmd.go b/app/cmd/cmd.go index 8afb5a5..db84be2 100755 --- a/app/cmd/cmd.go +++ b/app/cmd/cmd.go @@ -29,9 +29,11 @@ func NewNkdCommand(in io.Reader, out, err io.Writer) *cobra.Command { cmds.ResetFlags() // TODO: 修改名称 - // TODO: 添加DeployCommand, DestroyCommand, ExtendCommand + // TODO: 添加ExtendCommand cmds.AddCommand(NewConfigCommand()) cmds.AddCommand(NewInitCommand()) + cmds.AddCommand(NewDeployCommand()) + cmds.AddCommand(NewDestroyCommand()) return cmds } diff --git a/app/cmd/deploy.go b/app/cmd/deploy.go index 3814ce6..1456551 100755 --- a/app/cmd/deploy.go +++ b/app/cmd/deploy.go @@ -14,3 +14,30 @@ See the License for the specific language governing permissions and limitations under the License. */ package cmd + +import ( + "nestos-kubernetes-deployer/app/phases/infra" + + "github.com/spf13/cobra" +) + +func NewDeployCommand() *cobra.Command { + var dir, role string + + cmd := &cobra.Command{ + Use: "deploy", + Short: "Use this command to deploy kubernetes node", + RunE: func(cmd *cobra.Command, args []string) error { + cluster := &infra.Cluster{ + Dir: dir, + Role: role, + } + return cluster.Create() + }, + } + + cmd.PersistentFlags().StringVarP(&dir, "dir", "d", "", "directory for deployment") + cmd.PersistentFlags().StringVarP(&role, "role", "r", "", "node role for deployment") + + return cmd +} diff --git a/app/phases/infra/terraform/init.go b/app/cmd/destroy.go similarity index 46% rename from app/phases/infra/terraform/init.go rename to app/cmd/destroy.go index cbd3ad3..56124d3 100644 --- a/app/phases/infra/terraform/init.go +++ b/app/cmd/destroy.go @@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -13,29 +13,31 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ - -package terraform +package cmd import ( - "context" - "path/filepath" + "nestos-kubernetes-deployer/app/phases/infra" - "github.com/hashicorp/terraform-exec/tfexec" - "github.com/pkg/errors" + "github.com/spf13/cobra" ) -// terraform init -func TFInit(dir string, terraformDir string) error { - tf, err := newTFExec(dir, terraformDir) - if err != nil { - return errors.Wrap(err, "failed to create a new tfexec") +func NewDestroyCommand() *cobra.Command { + var dir, role string + + cmd := &cobra.Command{ + Use: "destroy", + Short: "Use this command to destroy kubernetes node", + RunE: func(cmd *cobra.Command, args []string) error { + cluster := &infra.Cluster{ + Dir: dir, + Role: role, + } + return cluster.Destroy() + }, } - // 使用本地terraform插件 - err = tf.Init(context.Background(), tfexec.PluginDir(filepath.Join(terraformDir, "plugins"))) - if err != nil { - return errors.Wrap(err, "failed to init terraform") - } + cmd.PersistentFlags().StringVarP(&dir, "dir", "d", "", "directory for deployment") + cmd.PersistentFlags().StringVarP(&role, "role", "r", "", "node role for deployment") - return nil + return cmd } diff --git a/app/phases/infra/assets/assets.go b/app/phases/infra/assets/assets.go deleted file mode 100755 index 81fa8cd..0000000 --- a/app/phases/infra/assets/assets.go +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright 2023 KylinSoft Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package assets - -type File struct { - Filename string - Data []byte -} diff --git a/app/phases/infra/assets/cluster/cluster.go b/app/phases/infra/assets/cluster/cluster.go deleted file mode 100644 index 9d5dda2..0000000 --- a/app/phases/infra/assets/cluster/cluster.go +++ /dev/null @@ -1,151 +0,0 @@ -/* -Copyright 2023 KylinSoft Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package cluster - -import ( - "os" - "path/filepath" - - "nestos-kubernetes-deployer/pkg/infra/assets" - "nestos-kubernetes-deployer/pkg/infra/terraform" - - "github.com/hashicorp/terraform-exec/tfexec" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -/* - installDir:自定义工作目录,在该路径下自动创建terraform文件夹 - dir:该路径存放tf配置文件 - terraformDir:该路径包含bin、plugins目录,存放terraform执行文件以及所需plugins -*/ - -type InfraProvider interface { - Create() - Destroy() -} - -type Cluster struct { - InstallDir string - Platform string - Name string -} - -// TODO: 配置文件准备阶段 - -func (c *Cluster) Create() error { - terraformDir := filepath.Join(c.InstallDir, "terraform") - dir := filepath.Join(terraformDir, c.Platform, c.Name) - - terraformVariables := &TerraformVariables{} - tfvarsFiles := make([]*assets.File, 0, len(terraformVariables.Files())+len(c.Platform)+len(c.Name)) - tfvarsFiles = append(tfvarsFiles, terraformVariables.Files()...) - - logrus.Infof("start to create %s in %s", c.Name, c.Platform) - - outputs, err := c.applyStage(dir, terraformDir, tfvarsFiles) - if err != nil { - return errors.Wrapf(err, "failed to create %s in %s", c.Name, c.Platform) - } - - logrus.Info(string(outputs.Data)) - logrus.Infof("succeed in creating %s in %s", c.Name, c.Platform) - - return nil -} - -func (c *Cluster) applyStage(dir string, terraformDir string, tfvarsFiles []*assets.File) (*assets.File, error) { - var applyOpts []tfexec.ApplyOption - for _, file := range tfvarsFiles { - if err := os.WriteFile(filepath.Join(dir, file.Filename), file.Data, 0o600); err != nil { - return nil, err - } - applyOpts = append(applyOpts, tfexec.VarFile(filepath.Join(dir, file.Filename))) - } - - return c.applyTerraform(dir, terraformDir, applyOpts...) -} - -func (c *Cluster) applyTerraform(dir string, terraformDir string, applyOpts ...tfexec.ApplyOption) (*assets.File, error) { - applyErr := terraform.TFApply(dir, terraformDir, applyOpts...) - - _, err := os.Stat(filepath.Join(dir, "terraform.tfstate")) - if os.IsNotExist(err) { - logrus.Errorf("Failed to read tfstate: %v", err) - return nil, errors.Wrap(err, "failed to read tfstate") - } - - if applyErr != nil { - return nil, errors.WithMessage(applyErr, "failed to apply Terraform") - } - - outputs, err := terraform.Outputs(dir, terraformDir) - if err != nil { - return nil, errors.Wrap(err, "could not get outputs file") - } - - outputsFile := &assets.File{ - Filename: "outputs", - Data: outputs, - } - - return outputsFile, nil -} - -func (c *Cluster) Destroy() error { - terraformDir := filepath.Join(c.InstallDir, "terraform") - dir := filepath.Join(terraformDir, c.Platform, c.Name) - - logrus.Infof("start to destroy %s in %s", c.Name, c.Platform) - - // Question: Destroy的tfvarsFiles的获取 - - terraformVariables := &TerraformVariables{} - tfvarsFiles := make([]*assets.File, 0, len(terraformVariables.Files())+len(c.Platform)+len(c.Name)) - tfvarsFiles = append(tfvarsFiles, terraformVariables.Files()...) - - err := c.destroyStage(dir, terraformDir, tfvarsFiles) - if err != nil { - return errors.Wrapf(err, "failed to destroy %s in %s", c.Name, c.Platform) - } - os.Remove(dir) - - logrus.Infof("succeed in destroying %s in %s", c.Name, c.Platform) - - return nil -} - -func (c *Cluster) destroyStage(dir string, terraformDir string, tfvarsFiles []*assets.File) error { - var destroyOpts []tfexec.DestroyOption - for _, file := range tfvarsFiles { - if err := os.WriteFile(filepath.Join(dir, file.Filename), file.Data, 0o600); err != nil { - return err - } - destroyOpts = append(destroyOpts, tfexec.VarFile(filepath.Join(dir, file.Filename))) - } - - return destroyTerraform(dir, terraformDir, destroyOpts...) -} - -func destroyTerraform(dir string, terraformDir string, destroyOpts ...tfexec.DestroyOption) error { - destroyErr := terraform.TFDestroy(dir, terraformDir, destroyOpts...) - if destroyErr != nil { - return errors.WithMessage(destroyErr, "failed to destroy Terraform") - } - - return nil -} diff --git a/app/phases/infra/assets/cluster/tfvars.go b/app/phases/infra/assets/cluster/tfvars.go deleted file mode 100644 index 0f850f6..0000000 --- a/app/phases/infra/assets/cluster/tfvars.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright 2023 KylinSoft Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package cluster - -import ( - "encoding/json" - "html/template" - "os" - "path/filepath" - - "nestos-kubernetes-deployer/pkg/infra/assets" - "nestos-kubernetes-deployer/pkg/infra/tfvars/openstack" - - "github.com/pkg/errors" -) - -type TerraformVariables struct { - FileList []*assets.File -} - -func (t *TerraformVariables) Files() []*assets.File { - return t.FileList -} - -func Generate() error { - // 从文件中读取 jsonData - jsonData, err := os.ReadFile("data/json/openstack/main.tf.json") - if err != nil { - return errors.Wrap(err, "error reading json data") - } - - // 解析 JSON 数据 - var terraformData openstack.TerraformData - err = json.Unmarshal(jsonData, &terraformData) - if err != nil { - return errors.Wrap(err, "error parsing json data") - } - - // 从文件中读取 terraformConfig - terraformConfig, err := os.ReadFile("data/templates/openstack/main.tf.template") - if err != nil { - return errors.Wrap(err, "error reading terraform config template") - } - - // 使用模板填充数据 - tmpl, err := template.New("terraform").Parse(string(terraformConfig)) - if err != nil { - return errors.Wrap(err, "error creating terraform config template") - } - - // 创建一个新的文件用于写入填充后的数据 - tfDir := filepath.Join("/root", "terraform") - if err := os.MkdirAll(tfDir, os.ModePerm); err != nil { - return errors.Wrap(err, "could not create the terraform directory") - } - - outputFile, err := os.Create(filepath.Join(tfDir, "main.tf")) - if err != nil { - return errors.Wrap(err, "error creating terraform config") - } - defer outputFile.Close() - - // 将填充后的数据写入文件 - err = tmpl.Execute(outputFile, terraformData) - if err != nil { - return errors.Wrap(err, "error executing terraform config") - } - - return nil -} diff --git a/app/phases/infra/infra.go b/app/phases/infra/infra.go new file mode 100644 index 0000000..bac37e5 --- /dev/null +++ b/app/phases/infra/infra.go @@ -0,0 +1,188 @@ +/* +Copyright 2023 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package infra + +import ( + "nestos-kubernetes-deployer/app/apis/nkd" + "nestos-kubernetes-deployer/app/phases/infra/terraform" + "os" + "path/filepath" + + "github.com/hashicorp/terraform-exec/tfexec" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v2" +) + +type Cluster struct { + Dir string // 由用户自定义,会在该路径下创建工作目录 /nkd + Role string // 节点类别 +} + +type File struct { + Filename string + Data []byte +} + +func (c *Cluster) Create() error { + // 若未指定c.Dir,则默认为当前路径 + if c.Dir == "" { + c.Dir = "./" + } + + if c.Role == "" { + return errors.Errorf("please provide the role of the node, master or worker") + } + + // 工作目录,包含terraform执行文件以及所需plugins + workDir := filepath.Join(c.Dir, "nkd") + // 从文件中读取 infraData + infraData, err := os.ReadFile(filepath.Join(workDir, "config.yaml")) + if err != nil { + return errors.WithMessage(err, "failed to read yaml file") + } + + // 解析 yaml 数据 + var configData nkd.Nkd + err = yaml.Unmarshal(infraData, &configData) + if err != nil { + return errors.WithMessage(err, "failed to parse yaml data") + } + + // 获取当前选择的平台 + platform := configData.Infra.Platform + + // tf配置文件所在目录 + tfDir := filepath.Join(workDir, platform, c.Role) + + // ToDo: 支持terraform apply参数 + applyOptsFile := &File{ + Filename: ".applyOptsFile", + Data: []byte{}, + } + + logrus.Infof("start to create %s in %s", c.Role, platform) + + outputs, err := executeApplyTerraform(tfDir, workDir, applyOptsFile) + if err != nil { + return errors.Wrapf(err, "failed to create %s in %s", c.Role, platform) + } + + logrus.Info(string(outputs.Data)) + logrus.Infof("succeed in creating %s in %s", c.Role, platform) + + return nil +} + +func executeApplyTerraform(tfDir string, terraformDir string, tfvarsFile *File) (*File, error) { + var applyOpts []tfexec.ApplyOption + + if err := os.WriteFile(filepath.Join(tfDir, tfvarsFile.Filename), tfvarsFile.Data, 0o600); err != nil { + return nil, err + } + applyOpts = append(applyOpts, tfexec.VarFile(filepath.Join(tfDir, tfvarsFile.Filename))) + + return applyTerraform(tfDir, terraformDir, applyOpts...) +} + +func applyTerraform(tfDir string, terraformDir string, applyOpts ...tfexec.ApplyOption) (*File, error) { + applyErr := terraform.TFApply(tfDir, terraformDir, applyOpts...) + + _, err := os.Stat(filepath.Join(tfDir, "terraform.tfstate")) + if os.IsNotExist(err) { + return nil, errors.Wrap(err, "failed to read tfstate") + } + + if applyErr != nil { + return nil, errors.Wrap(applyErr, "failed to apply Terraform") + } + + outputs, err := terraform.Outputs(tfDir, terraformDir) + if err != nil { + return nil, errors.Wrap(err, "failed to get outputs file") + } + + outputsFile := &File{ + Filename: "tfOutputs", + Data: outputs, + } + + return outputsFile, nil +} + +func (c *Cluster) Destroy() error { + // 若未指定c.Dir,则默认为当前路径 + if c.Dir == "" { + c.Dir = "./" + } + + if c.Role == "" { + return errors.Errorf("please provide the role of the node, master or worker") + } + + // 从文件中读取 infraData + workDir := filepath.Join(c.Dir, "nkd") + infraData, err := os.ReadFile(filepath.Join(workDir, "config.yaml")) + if err != nil { + return errors.Wrap(err, "failed to read yaml data") + } + + // 解析 yaml 数据 + var configData nkd.Nkd + err = yaml.Unmarshal(infraData, &configData) + if err != nil { + return errors.Wrap(err, "failed to parse yaml data") + } + + // 获取当前选择的平台 + platform := configData.Infra.Platform + tfDir := filepath.Join(workDir, platform, c.Role) + + logrus.Infof("start to destroy %s in %s", c.Role, platform) + + // ToDo: 支持terraform destroy参数 + destroyOptsFile := &File{ + Filename: ".destroyOptsFile", + Data: []byte{}, + } + + err = executeDestroyTerraform(tfDir, workDir, destroyOptsFile) + if err != nil { + return errors.Wrapf(err, "failed to destroy %s in %s", c.Role, platform) + } + os.RemoveAll(tfDir) + + logrus.Infof("succeed in destroying %s in %s", c.Role, platform) + + return nil +} + +func executeDestroyTerraform(tfDir string, terraformDir string, tfvarsFile *File) error { + var destroyOpts []tfexec.DestroyOption + destroyOpts = append(destroyOpts, tfexec.VarFile(filepath.Join(tfDir, tfvarsFile.Filename))) + + return destroyTerraform(tfDir, terraformDir, destroyOpts...) +} + +func destroyTerraform(tfDir string, terraformDir string, destroyOpts ...tfexec.DestroyOption) error { + destroyErr := terraform.TFDestroy(tfDir, terraformDir, destroyOpts...) + if destroyErr != nil { + return errors.Wrap(destroyErr, "failed to destroy Terraform") + } + + return nil +} diff --git a/app/phases/infra/terraform/providers/providers.go b/app/phases/infra/terraform/providers/providers.go deleted file mode 100644 index 986ee8a..0000000 --- a/app/phases/infra/terraform/providers/providers.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2023 KylinSoft Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package providers - -import "fmt" - -var ( - OpenStack = provider("openstack") -) - -type Provider struct { - Name string - Source string -} - -func provider(name string) Provider { - return Provider{ - Name: name, - Source: fmt.Sprintf("nkd/local/%s", name), - } -} diff --git a/app/phases/infra/terraform/stage.go b/app/phases/infra/terraform/stage.go deleted file mode 100644 index 3cd5a1d..0000000 --- a/app/phases/infra/terraform/stage.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2023 KylinSoft Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package terraform - -import "nestos-kubernetes-deployer/pkg/infra/terraform/providers" - -type Stage interface { - Name() string - - // the list of providers that are used for the stage - Providers() []providers.Provider -} diff --git a/app/phases/infra/terraform/stages/openstack/stages.go b/app/phases/infra/terraform/stages/openstack/stages.go deleted file mode 100644 index c2ab1ed..0000000 --- a/app/phases/infra/terraform/stages/openstack/stages.go +++ /dev/null @@ -1,19 +0,0 @@ -package openstack - -import ( - "nestos-kubernetes-deployer/pkg/infra/terraform" - "nestos-kubernetes-deployer/pkg/infra/terraform/providers" - "nestos-kubernetes-deployer/pkg/infra/terraform/stages" -) - -var PlatformStages = []terraform.Stage{} - -func AddPlatformStage(name string) { - newStage := stages.NewStage( - "openstack", - name, - []providers.Provider{providers.OpenStack}, - ) - - PlatformStages = append(PlatformStages, newStage) -} diff --git a/app/phases/infra/terraform/stages/platform/stages.go b/app/phases/infra/terraform/stages/platform/stages.go deleted file mode 100644 index 5f26212..0000000 --- a/app/phases/infra/terraform/stages/platform/stages.go +++ /dev/null @@ -1,19 +0,0 @@ -package platform - -import ( - "errors" - "fmt" - - "nestos-kubernetes-deployer/pkg/infra/terraform" - "nestos-kubernetes-deployer/pkg/infra/terraform/stages/openstack" -) - -func StagesForPlatform(platform string, stage string) ([]terraform.Stage, error) { - switch platform { - case "openstack": - openstack.AddPlatformStage(stage) - return openstack.PlatformStages, nil - default: - return nil, errors.New(fmt.Sprintf("unsupported platform %q", platform)) - } -} diff --git a/app/phases/infra/terraform/stages/split.go b/app/phases/infra/terraform/stages/split.go deleted file mode 100644 index 556d734..0000000 --- a/app/phases/infra/terraform/stages/split.go +++ /dev/null @@ -1,43 +0,0 @@ -package stages - -import ( - "fmt" - - "nestos-kubernetes-deployer/pkg/infra/terraform/providers" -) - -type StageOption func(*SplitStage) - -func NewStage(platform, name string, providers []providers.Provider, opts ...StageOption) SplitStage { - s := SplitStage{ - platform: platform, - name: name, - providers: providers, - } - for _, opt := range opts { - opt(&s) - } - return s -} - -type SplitStage struct { - platform string - name string - providers []providers.Provider -} - -func (s SplitStage) Name() string { - return s.name -} - -func (s SplitStage) Providers() []providers.Provider { - return s.providers -} - -func (s SplitStage) StateFilename() string { - return fmt.Sprintf("terraform.%s.tfstate", s.name) -} - -func (s SplitStage) OutputsFilename() string { - return fmt.Sprintf("%s.tfvars.json", s.name) -} diff --git a/app/phases/infra/terraform/state.go b/app/phases/infra/terraform/state.go index 9509abc..446ddbd 100644 --- a/app/phases/infra/terraform/state.go +++ b/app/phases/infra/terraform/state.go @@ -23,11 +23,9 @@ import ( "github.com/pkg/errors" ) -const StateFilename = "terraform.tfstate" - -// Reads the terraform state file. -func Outputs(workingDir string, terraformBinary string) ([]byte, error) { - tf, err := newTFExec(workingDir, terraformBinary) +// Outputs reads the terraform state file and returns the outputs of the stage as json. +func Outputs(dir string, terraformDir string) ([]byte, error) { + tf, err := newTFExec(dir, terraformDir) if err != nil { return nil, err } diff --git a/app/phases/infra/terraform/terraform.go b/app/phases/infra/terraform/terraform.go index 6bb6a9c..b6fb898 100644 --- a/app/phases/infra/terraform/terraform.go +++ b/app/phases/infra/terraform/terraform.go @@ -20,7 +20,6 @@ import ( "context" "os" "path/filepath" - "runtime" "github.com/hashicorp/terraform-exec/tfexec" "github.com/openshift/installer/pkg/lineprinter" @@ -28,9 +27,14 @@ import ( "github.com/sirupsen/logrus" ) -func newTFExec(dir string, terraformDir string) (*tfexec.Terraform, error) { - tfPath := filepath.Join(terraformDir, "bin", runtime.GOOS+"_"+runtime.GOARCH, "terraform") - tf, err := tfexec.NewTerraform(dir, tfPath) +/* + tfDir: tf配置文件所在目录 + terraformDir: terraform执行文件所在目录 +*/ + +func newTFExec(tfDir string, terraformDir string) (*tfexec.Terraform, error) { + tfPath := filepath.Join(terraformDir, "terraform") + tf, err := tfexec.NewTerraform(tfDir, tfPath) if err != nil { return nil, err } @@ -59,13 +63,29 @@ func newTFExec(dir string, terraformDir string) (*tfexec.Terraform, error) { return tf, nil } +// terraform init +func TFInit(tfDir string, terraformDir string) (err error) { + tf, err := newTFExec(tfDir, terraformDir) + if err != nil { + return errors.Wrap(err, "failed to create a new tfexec") + } + + // 使用本地terraform插件 + err = tf.Init(context.Background(), tfexec.PluginDir(filepath.Join(terraformDir, "plugins"))) + if err != nil { + return errors.Wrap(err, "failed to init terraform") + } + + return nil +} + // terraform apply -func TFApply(dir string, terraformDir string, applyOpts ...tfexec.ApplyOption) error { - if err := TFInit(dir, terraformDir); err != nil { - return err +func TFApply(tfDir string, terraformDir string, applyOpts ...tfexec.ApplyOption) error { + if err := TFInit(tfDir, terraformDir); err != nil { + return errors.Wrap(err, "failed to init terraform") } - tf, err := newTFExec(dir, terraformDir) + tf, err := newTFExec(tfDir, terraformDir) if err != nil { return errors.Wrap(err, "failed to create a new tfexec") } @@ -79,12 +99,12 @@ func TFApply(dir string, terraformDir string, applyOpts ...tfexec.ApplyOption) e } // terraform destroy -func TFDestroy(dir string, terraformDir string, destroyOpts ...tfexec.DestroyOption) error { - if err := TFInit(dir, terraformDir); err != nil { +func TFDestroy(tfDir string, terraformDir string, destroyOpts ...tfexec.DestroyOption) error { + if err := TFInit(tfDir, terraformDir); err != nil { return err } - tf, err := newTFExec(dir, terraformDir) + tf, err := newTFExec(tfDir, terraformDir) if err != nil { return errors.Wrap(err, "failed to destroy a new tfexec") } diff --git a/app/phases/infra/tfvars/openstack/tfvars.go b/app/phases/infra/tfvars/openstack/tfvars.go deleted file mode 100644 index 693bee2..0000000 --- a/app/phases/infra/tfvars/openstack/tfvars.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2023 KylinSoft Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package openstack - -// 定义 JSON 数据的结构 -type TerraformData struct { - Openstack struct { - User_name string `json:"user_name"` - Password string `json:"password"` - Tenant_name string `json:"tenant_name"` - Auth_url string `json:"auth_url"` - Region string `json:"region"` - } `json:"openstack"` - Flavor struct { - Name string `json:"name"` - Ram string `json:"ram"` - Vcpus string `json:"vcpus"` - Disk string `json:"disk"` - Is_public string `json:"is_public"` - } `json:"flavor"` - Instance struct { - Count string `json:"count"` - Name string `json:"name"` - Image_name string `json:"image_name"` - Key_pair string `json:"key_pair"` - } `json:"instance"` - Floatip struct { - Pool string `json:"pool"` - } `json:"floatip"` -} -- Gitee