Skip to content
This repository was archived by the owner on Feb 5, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 40 additions & 8 deletions installer/cmd/tectonic/main.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,61 @@
package main

import (
"log"

"github.com/coreos/tectonic-installer/installer/pkg/workflow"
"gopkg.in/alecthomas/kingpin.v2"
)

var (
dryRunFlag = kingpin.Flag("dry-run", "Just pretend, but don't do anything").Bool()
clusterInstallCommand = kingpin.Command("install", "Create a new Tectonic cluster")
clusterDeleteCommand = kingpin.Command("delete", "Delete an existing Tectonic cluster")
deleteClusterDir = clusterDeleteCommand.Arg("dir", "The name of the cluster to delete").String()
clusterConfigFlag = clusterInstallCommand.Flag("config", "Cluster specification file").Required().ExistingFile()
dryRunFlag = kingpin.Flag("dry-run", "Just pretend, but don't do anything").Bool()
clusterInstallCommand = kingpin.Command("install", "Create a new Tectonic cluster")
clusterFullInstallCommand = clusterInstallCommand.Command("full", "Create a new Tectonic cluster").Default()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be the default path. No need to explicitly have a sub-command.
#lessismore #minimalism

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By adding full as default the user can just run cli install --config otherwise the same command complains about it expecting a subcommand. Not sure yet how to tell kingpin that the subcommand is not mandatory

clusterAssetsCommand = clusterInstallCommand.Command("assets", "Generate Tectonic assets.")
clusterBootstrapCommand = clusterInstallCommand.Command("bootstrap", "Create a single bootstrap node Tectonic cluster.")
clusterJoinCommand = clusterInstallCommand.Command("join", "Create master and worker nodes to join an exisiting Tectonic cluster.")
clusterDeleteCommand = kingpin.Command("delete", "Delete an existing Tectonic cluster")
deleteClusterDir = clusterDeleteCommand.Arg("dir", "The name of the cluster to delete").String()
clusterConfigFlag = clusterInstallCommand.Flag("config", "Cluster specification file").Required().ExistingFile()
)

func main() {
// TODO: actually do proper error handling
switch kingpin.Parse() {
case clusterInstallCommand.FullCommand():
case clusterFullInstallCommand.FullCommand():
{
w := workflow.NewInstallWorkflow(*clusterConfigFlag)
w.Execute()
if err := w.Execute(); err != nil {
log.Fatal(err)
}
}
case clusterAssetsCommand.FullCommand():
{
w := workflow.NewAssetsWorkflow(*clusterConfigFlag)
if err := w.Execute(); err != nil {
log.Fatal(err)
}
}
case clusterBootstrapCommand.FullCommand():
{
w := workflow.NewBootstrapWorkflow(*clusterConfigFlag)
if err := w.Execute(); err != nil {
log.Fatal(err)
}
}
case clusterJoinCommand.FullCommand():
{
w := workflow.NewJoinWorkflow(*clusterConfigFlag)
if err := w.Execute(); err != nil {
log.Fatal(err)
}
}
case clusterDeleteCommand.FullCommand():
{
w := workflow.NewDestroyWorkflow(*deleteClusterDir)
w.Execute()
if err := w.Execute(); err != nil {
log.Fatal(err)
}
}
}
}
8 changes: 8 additions & 0 deletions installer/pkg/tectonic/buildstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,11 @@ func writeFile(path, content string) error {

return nil
}

// FindTemplatesForStep determines the location of top-level
// Terraform templates for a given step of build.
func FindTemplatesForStep(step ...string) string {
pwd, _ := os.Getwd()
step = append([]string{pwd, "steps"}, step...)
return filepath.Join(step...)
}
36 changes: 34 additions & 2 deletions installer/pkg/workflow/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package workflow
import (
"log"
"os"

"github.com/coreos/tectonic-installer/installer/pkg/tectonic"
)

// NewDestroyWorkflow creates new instances of the 'destroy' workflow,
Expand All @@ -16,12 +18,29 @@ func NewDestroyWorkflow(buildPath string) Workflow {
} else if err != nil {
log.Fatalf("%v encountered while validating build location.", err)
}

// TODO: get this dynamically once we move to cluster config
platform := "aws"

if platform == "aws" {
return simpleWorkflow{
metadata: metadata{
statePath: buildPath,
},
steps: []Step{
terraformPrepareStep,
joiningDestroyStep,
bootstrapDestroyStep,
assetsDestroyStep,
},
}
}
return simpleWorkflow{
metadata: metadata{
statePath: buildPath,
},
steps: []Step{
tectonicPrepareStep,
terraformPrepareStep,
terraformInitStep,
terraformDestroyStep,
},
Expand All @@ -32,8 +51,21 @@ func terraformDestroyStep(m *metadata) error {
if m.statePath == "" {
log.Fatalf("Invalid build location - cannot destroy.")
}
log.Printf("Destroying cluster from %s...", m.statePath)
return tfDestroy(m.statePath, "state", tectonic.FindTemplatesForType(m.platform))
}

func joiningDestroyStep(m *metadata) error {
log.Printf("Destroying cluster from %s...", m.statePath)
return tfDestroy(m.statePath, "joining", tectonic.FindTemplatesForStep("joining"))
}

return terraformExec(m, "destroy", "-force")
func bootstrapDestroyStep(m *metadata) error {
log.Printf("Destroying cluster from %s...", m.statePath)
return tfDestroy(m.statePath, "bootstrap", tectonic.FindTemplatesForStep("bootstrap"))
}

func assetsDestroyStep(m *metadata) error {
log.Printf("Destroying cluster from %s...", m.statePath)
return tfDestroy(m.statePath, "assets", tectonic.FindTemplatesForStep("assets"))
}
150 changes: 127 additions & 23 deletions installer/pkg/workflow/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,117 @@ import (
"os"
"path/filepath"

"github.com/coreos/tectonic-installer/installer/pkg/config"
"github.com/coreos/tectonic-installer/installer/pkg/tectonic"
)

const (
configFileName = "config.yaml"
terraformVariablesFileName = "terraform.tfvars"
kubeConfig = "/generated/auth/kubeconfig"
)

// NewInstallWorkflow creates new instances of the 'install' workflow,
// responsible for running the actions necessary to install a new cluster.
func NewInstallWorkflow(configFile string) Workflow {
config, err := config.ParseFile(configFile)
if err != nil {
log.Fatalf("%s is not a valid config file: %s", configFile, err)
}
cluster := config.Clusters[0]

// TODO: move to tectonicGenerateClusterConfig/tectonicGenerateTerraformVariables and get this dynamically
clusterName := "cluster-aws"
platform := "aws"

if platform == "aws" {
return simpleWorkflow{
metadata: metadata{
clusterName: clusterName,
configFile: configFile,
},
steps: []Step{
terraformPrepareStep,
assetsStep,
bootstrapStep,
joiningStep,
},
}
}
return simpleWorkflow{
metadata: metadata{
Cluster: cluster,
configFile: configFile,
clusterName: clusterName,
configFile: configFile,
platform: platform,
},
steps: []Step{
tectonicPrepareStep,
tectonicGenerateClusterConfig,
tectonicGenerateTerraformVariables,
terraformPrepareStep,
terraformInitStep,
terraformApplyStep,
},
}
}

func tectonicGenerateClusterConfig(m *metadata) error {
return tectonic.GenerateClusterConfig(m.Cluster, m.statePath)
//func tectonicGenerateClusterConfig(m *metadata) error {
// return tectonic.GenerateClusterConfig(m.Cluster, m.statePath)
//}
//
//func tectonicGenerateTerraformVariables(m *metadata) error {
// configFilePath := filepath.Join(m.statePath, terraformVariablesFileName)
//
// return tectonic.GenerateTerraformVars(m.Cluster, configFilePath)
//}

// NewAssetsWorkflow creates new instances of the 'assets' workflow,
// responsible for running the actions necessary to generate cluster assets.
func NewAssetsWorkflow(configFile string) Workflow {
// TODO: move to tectonicGenerateClusterConfig/tectonicGenerateTerraformVariables and get this dynamically
clusterName := "cluster-aws"
return simpleWorkflow{
metadata: metadata{
clusterName: clusterName,
configFile: configFile,
},
steps: []Step{
terraformPrepareStep,
assetsStep,
},
}
}

func tectonicGenerateTerraformVariables(m *metadata) error {
configFilePath := filepath.Join(m.statePath, terraformVariablesFileName)
// NewBootstrapWorkflow creates new instances of the 'bootstrap' workflow,
// responsible for running the actions necessary to generate a single bootstrap machine cluster.
func NewBootstrapWorkflow(configFile string) Workflow {
// TODO: move to tectonicGenerateClusterConfig/tectonicGenerateTerraformVariables and get this dynamically
clusterName := "cluster-aws"
return simpleWorkflow{
metadata: metadata{
clusterName: clusterName,
configFile: configFile,
},
steps: []Step{
terraformPrepareStep,
bootstrapStep,
},
}
}

return tectonic.GenerateTerraformVars(m.Cluster, configFilePath)
// NewJoinWorkflow creates new instances of the 'join' workflow,
// responsible for running the actions necessary to scale the machines of the cluster.
func NewJoinWorkflow(configFile string) Workflow {
// TODO: move to tectonicGenerateClusterConfig/tectonicGenerateTerraformVariables and get this dynamically
clusterName := "cluster-aws"
return simpleWorkflow{
metadata: metadata{
clusterName: clusterName,
configFile: configFile,
},
steps: []Step{
terraformPrepareStep,
joiningStep,
},
}
}

func tectonicPrepareStep(m *metadata) error {
func terraformPrepareStep(m *metadata) error {
if m.statePath == "" {
m.statePath = tectonic.NewBuildLocation(m.Cluster.Name)
m.statePath = tectonic.NewBuildLocation(m.clusterName)
}
varfile := filepath.Join(m.statePath, configFileName)
varfile := filepath.Join(m.statePath, m.configFile)
if _, err := os.Stat(varfile); os.IsNotExist(err) {
from, err := os.Open(m.configFile)
if err != nil {
Expand All @@ -73,14 +136,55 @@ func tectonicPrepareStep(m *metadata) error {
return nil
}

func terraformInitStep(m *metadata) error {
log.Printf("Initializing cluster ...")
return tfInit(m.statePath, tectonic.FindTemplatesForType(m.platform))
}

func terraformApplyStep(m *metadata) error {
log.Printf("Installation is running...")
return tfApply(m.statePath, "state", tectonic.FindTemplatesForType(m.platform))
}

return terraformExec(m, "apply")
func assetsStep(m *metadata) error {
log.Printf("Installation is running...")
return runStep(m.statePath, "assets")
}

func terraformInitStep(m *metadata) error {
log.Printf("Initializing cluster ...")
func bootstrapStep(m *metadata) error {
log.Printf("Installation is running...")
err := runStep(m.statePath, "bootstrap")
if err != nil {
return err
}
err = waitForNcg(m)
if err != nil {
return err
}
err = destroyCname(m)
if err != nil {
return err
}
return nil
}

func joiningStep(m *metadata) error {
// TODO: import will fail after a first run, error is ignored for now
importAutoScalingGroup(m)
log.Printf("Installation is running...")
return runStep(m.statePath, "joining")
}

return terraformExec(m, "init")
func runStep(buildPath string, step string) error {
codePath := tectonic.FindTemplatesForStep(step)
err := tfInit(buildPath, codePath)
if err != nil {
return err
}

err = tfApply(buildPath, step, codePath)
if err != nil {
return err
}
return nil
}
31 changes: 31 additions & 0 deletions installer/pkg/workflow/terraform.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package workflow

import (
"os"
"os/exec"
)

func runTfCommand(buildPath string, args ...string) error {
tfCommand := exec.Command("terraform", args...)
tfCommand.Dir = buildPath
tfCommand.Stdin = os.Stdin
tfCommand.Stdout = os.Stdout
tfCommand.Stderr = os.Stderr
err := tfCommand.Run()
if err != nil {
return err
}
return nil
}

func tfInit(buildPath string, codePath string) error {
return runTfCommand(buildPath, "init", codePath)
}

func tfDestroy(buildPath string, state string, codePath string) error {
return runTfCommand(buildPath, "destroy", "-force", "-state="+state+".tfstate", codePath)
}

func tfApply(buildPath string, state string, codePath string) error {
return runTfCommand(buildPath, "apply", "-state="+state+".tfstate", codePath)
}
Loading