Skip to content

Commit

Permalink
cp: rewrite input file handling
Browse files Browse the repository at this point in the history
* copy kubeconfig to working dir temporarily (because /tmp is out of DfM default
shared paths)
* don't offer editing of values file during `cp down`
* fix input values editing during `cp up`
  • Loading branch information
Mate Ory authored and kozmagabor committed May 31, 2019
1 parent 86e6aa0 commit abc398a
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 115 deletions.
6 changes: 3 additions & 3 deletions cmd/banzai/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import "github.com/banzaicloud/banzai-cli/cmd"
// Provisioned by ldflags
// nolint: gochecknoglobals
var (
version string
commitHash string
buildDate string
version string
commitHash string
buildDate string
pipelineVersion string
)

Expand Down
3 changes: 2 additions & 1 deletion internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
package cli

import (
"github.com/banzaicloud/banzai-cli/.gen/pipeline"
"io"
"os"
"path"
"path/filepath"
"sync"

"github.com/banzaicloud/banzai-cli/.gen/pipeline"

"github.com/goph/emperror"
"github.com/mattn/go-isatty"
"github.com/mitchellh/go-homedir"
Expand Down
105 changes: 30 additions & 75 deletions internal/cli/command/controlplane/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,85 +15,35 @@
package controlplane

import (
"encoding/json"
"fmt"
"errors"
"io/ioutil"
"os"
"path/filepath"

"github.com/goph/emperror"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"gopkg.in/AlecAivazis/survey.v1"
)

// NewDownCommand creates a new cobra.Command for `banzai clontrolplane down`.
func NewDownCommand() *cobra.Command {
options := createOptions{}

cmd := &cobra.Command{
Use: "down",
Short: "Destroy the controlplane",
Long: "Destroy a controlplane based on json stdin or interactive session",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
runDestroy(options)
RunE: func(cmd *cobra.Command, args []string) error {
return runDestroy()
},
}

flags := cmd.Flags()

flags.StringVarP(&options.file, "file", "f", "values.yaml", "Control Plane descriptor file")

return cmd
}

func runDestroy(options createOptions) {
var out map[string]interface{}

filename := options.file
func runDestroy() error {

if isInteractive() {
var content string

for {
if filename == "" {
_ = survey.AskOne(
&survey.Input{
Message: "Load a JSON or YAML file:",
Default: "values.yaml",
Help: "Give either a relative or an absolute path to a file containing a JSON or YAML Control Plane creation descriptor. Leave empty to cancel.",
},
&filename,
nil,
)
if filename == "skip" || filename == "" {
break
}
}

if raw, err := ioutil.ReadFile(filename); err != nil {

log.Errorf("failed to read file %q: %v", filename, err)

filename = "" // reset fileName so that we can ask for one

continue
} else {
if err := unmarshal(raw, &out); err != nil {
log.Fatalf("failed to parse control plane descriptor: %v", err)
}

break
}
}

if bytes, err := json.MarshalIndent(out, "", " "); err != nil {
log.Debugf("descriptor: %#v", out)
log.Fatalf("failed to marshal descriptor: %v", err)
} else {
content = string(bytes)
_, _ = fmt.Fprintf(os.Stderr, "The current state of the descriptor:\n\n%s\n", content)
}

var destroy bool
_ = survey.AskOne(
&survey.Confirm{
Expand All @@ -105,31 +55,36 @@ func runDestroy(options createOptions) {
)

if !destroy {
log.Fatal("controlplane destroy cancelled")
return errors.New("controlplane destroy cancelled")
}
} else { // non-interactive
var raw []byte
var err error
}

if filename != "" {
raw, err = ioutil.ReadFile(filename)
} else {
raw, err = ioutil.ReadAll(os.Stdin)
filename = "stdin"
}
// create temp dir for the files to attach
dir, err := ioutil.TempDir(".", "tmp")
if err != nil {
return emperror.Wrapf(err, "failed to create temporary directory")
}
defer os.RemoveAll(dir)

if err != nil {
log.Fatalf("failed to read %s: %v", filename, err)
}
kubeconfigName, err := filepath.Abs(filepath.Join(dir, "kubeconfig"))
if err != nil {
return emperror.Wrap(err, "failed to construct kubeconfig file name")
}

if err := unmarshal(raw, &out); err != nil {
log.Fatalf("failed to parse controlplane descriptor: %v", err)
}
if err := copyKubeconfig(kubeconfigName); err != nil {
return emperror.Wrap(err, "failed to copy Kubeconfig")
}

log.Info("controlplane is being destroy")
tfdir, err := filepath.Abs("./.tfstate")
if err != nil {
return emperror.Wrap(err, "failed to construct tfstate directory path")
}

if err := runInternal("destroy", filename); err != nil {
log.Fatalf("controlplane destroy failed: %v", err)
valuesName, err := filepath.Abs(valuesDefault)
if err != nil {
return emperror.Wrap(err, "failed to construct values file name")
}

log.Info("controlplane is being destroyed")
return emperror.Wrap(runInternal("destroy", valuesName, kubeconfigName, tfdir), "controlplane destroy failed")
}
98 changes: 64 additions & 34 deletions internal/cli/command/controlplane/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ package controlplane

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/goph/emperror"
"github.com/mattn/go-isatty"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand All @@ -42,21 +44,21 @@ func NewUpCommand() *cobra.Command {
Use: "up",
Aliases: []string{"c"},
Short: "Create a controlplane",
Long: "Create controlplane based on json stdin or interactive session",
Long: "Create controlplane based on json stdin or interactive session in the current Kubernetes context. The current working directory will be used for storing the applied configuration and deployment status.",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
runUp(options)
RunE: func(cmd *cobra.Command, args []string) error {
return runUp(options)
},
}

flags := cmd.Flags()

flags.StringVarP(&options.file, "file", "f", "values.yaml", "Control Plane descriptor file")
flags.StringVarP(&options.file, "file", "f", valuesDefault, "Input control plane descriptor file")

return cmd
}

func runUp(options createOptions) {
func runUp(options createOptions) error {
var out map[string]interface{}

filename := options.file
Expand All @@ -69,8 +71,8 @@ func runUp(options createOptions) {
_ = survey.AskOne(
&survey.Input{
Message: "Load a JSON or YAML file:",
Default: "values.yaml",
Help: "Give either a relative or an absolute path to a file containing a JSON or YAML Control Plane creation descriptor. Leave empty to cancel.",
Default: valuesDefault,
Help: "Give either a relative or an absolute path to a file containing a JSON or YAML control plane descriptor. Leave empty to cancel.",
},
&filename,
nil,
Expand All @@ -89,7 +91,7 @@ func runUp(options createOptions) {
continue
} else {
if err := unmarshal(raw, &out); err != nil {
log.Fatalf("failed to parse control plane descriptor: %v", err)
return emperror.Wrap(err, "failed to parse control plane descriptor")
}

break
Expand All @@ -112,7 +114,8 @@ func runUp(options createOptions) {
}

_ = survey.AskOne(&survey.Editor{Message: "controlplane descriptor:", Default: content, HideDefault: true, AppendDefault: true}, &content, nil)
if err := json.Unmarshal([]byte(content), &out); err != nil {
out = make(map[string]interface{})
if err := unmarshal([]byte(content), &out); err != nil {
log.Errorf("can't parse descriptor: %v", err)
}
}
Expand All @@ -128,7 +131,7 @@ func runUp(options createOptions) {
)

if !create {
log.Fatal("controlplane creation cancelled")
return errors.New("controlplane creation cancelled")
}
} else { // non-interactive
var raw []byte
Expand All @@ -142,45 +145,67 @@ func runUp(options createOptions) {
}

if err != nil {
log.Fatalf("failed to read %s: %v", filename, err)
return emperror.Wrapf(err, "failed to read %q", filename)
}

if err := unmarshal(raw, &out); err != nil {
log.Fatalf("failed to parse controlplane descriptor: %v", err)
return emperror.Wrap(err, "failed to parse controlplane descriptor")
}
}

log.Info("controlplane is being created")
// create temp dir for the files to attach
dir, err := ioutil.TempDir(".", "tmp")
if err != nil {
return emperror.Wrap(err, "failed to create temporary directory")
}
defer os.RemoveAll(dir)

if err := runInternal("apply", filename); err != nil {
log.Fatalf("controlplane creation failed: %v", err)
// write values to temp file
values, err := json.MarshalIndent(out, "", " ")
if err != nil {
return emperror.Wrap(err, "failed to masrshal values file")
}
}

func isInteractive() bool {
if isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stdin.Fd()) {
return !viper.GetBool("formatting.no-interactive")
valuesName, err := filepath.Abs(filepath.Join(dir, "values"))
if err != nil {
return emperror.Wrap(err, "failed to construct values file name")
}
return viper.GetBool("formatting.force-interactive")
}

func runInternal(command, valuesFile string) error {
if err := ioutil.WriteFile(valuesName, values, 0600); err != nil {
return emperror.Wrapf(err, "failed to write temporary file %q", valuesName)
}

kubeconfig := os.Getenv("KUBECONFIG")
if kubeconfig == "" {
kubeconfig = os.Getenv("HOME") + "/.kube/config"
if err := ioutil.WriteFile(filename, values, 0600); err != nil {
return emperror.Wrapf(err, "failed to write values.yaml file %q", filename)
}

pwd, err := os.Getwd()
kubeconfigName, err := filepath.Abs(filepath.Join(dir, "kubeconfig"))
if err != nil {
return err
return emperror.Wrap(err, "failed to construct kubeconfig file name")
}

if err := copyKubeconfig(kubeconfigName); err != nil {
return emperror.Wrap(err, "failed to copy Kubeconfig")
}

valuesFile, err = filepath.Abs(valuesFile)
tfdir, err := filepath.Abs("./.tfstate")
if err != nil {
return err
return emperror.Wrap(err, "failed to construct tfstate directory path")
}

log.Info("controlplane is being created")
return emperror.Wrap(runInternal("apply", valuesName, kubeconfigName, tfdir), "controlplane creation failed")
}

func isInteractive() bool {
if isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stdin.Fd()) {
return !viper.GetBool("formatting.no-interactive")
}
return viper.GetBool("formatting.force-interactive")
}

func runInternal(command, valuesFile, kubeconfigFile, tfdir string) error {

infoCmd := exec.Command("docker", "info", "-f", "{{eq .OperatingSystem \"Docker Desktop\"}}")

infoOuput, err := infoCmd.Output()
Expand All @@ -192,16 +217,21 @@ func runInternal(command, valuesFile string) error {

args := []string{
"run", "-it", "--rm",
"-v", fmt.Sprintf("%s:/root/.kube/config", kubeconfig),
"-v", fmt.Sprintf("%s/.tfstate:/tfstate", pwd),
"-v", fmt.Sprintf("%s:/terraform/values.yaml", valuesFile),
"-v", fmt.Sprintf("%s:/root/.kube/config", kubeconfigFile),
"-v", fmt.Sprintf("%s:/tfstate", tfdir),
"-e", fmt.Sprintf("IS_DOCKER_FOR_MAC=%s", isDockerForMac),
"--entrypoint", "/terraform/entrypoint.sh",
}

if valuesFile != "" {
args = append(args, "-v", fmt.Sprintf("%s:/terraform/values.yaml", valuesFile))
}

args = append(args,
"banzaicloud/cp-installer:latest",
command,
"-state=/tfstate/terraform.tfstate", // workaround for https://github.com/terraform-providers/terraform-provider-helm/issues/271
"-parallelism=1",
}
"-parallelism=1")

log.Infof("docker %v", args)

Expand Down
Loading

0 comments on commit abc398a

Please sign in to comment.