diff --git a/.gitignore b/.gitignore index b678cfc7e46..1a2081502f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /bin/ .openshift-install.log +/data/data/bin/ diff --git a/cmd/openshift-install/version.go b/cmd/openshift-install/version.go index 21ba44995fe..5d7834202bb 100644 --- a/cmd/openshift-install/version.go +++ b/cmd/openshift-install/version.go @@ -3,14 +3,8 @@ package main import ( "fmt" "os" - "os/exec" - "strings" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" - - "github.com/openshift/installer/pkg/terraform" ) var ( @@ -28,14 +22,5 @@ func newVersionCmd() *cobra.Command { func runVersionCmd(cmd *cobra.Command, args []string) error { fmt.Printf("%s %s\n", os.Args[0], version) - terraformVersion, err := terraform.Version() - if err != nil { - exitError, ok := err.(*exec.ExitError) - if ok && len(exitError.Stderr) > 0 { - logrus.Error(strings.Trim(string(exitError.Stderr), "\n")) - } - return errors.Wrap(err, "Failed to calculate Terraform version") - } - fmt.Println(terraformVersion) return nil } diff --git a/data/unpack.go b/data/unpack.go index 9d925a5d924..1d2e225ffe0 100644 --- a/data/unpack.go +++ b/data/unpack.go @@ -38,6 +38,10 @@ func Unpack(base string, uri string) (err error) { return nil } + if err := os.MkdirAll(filepath.Dir(base), 0777); err != nil { + return err + } + out, err := os.OpenFile(base, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) if err != nil { return err diff --git a/hack/build.sh b/hack/build.sh index 604c620e463..31c90ebc9dc 100755 --- a/hack/build.sh +++ b/hack/build.sh @@ -25,6 +25,8 @@ TAGS="${TAGS:-}" OUTPUT="${OUTPUT:-bin/openshift-install}" export CGO_ENABLED=0 +(hack/get-terraform.sh) + case "${MODE}" in release) TAGS="${TAGS} release" diff --git a/hack/get-terraform.sh b/hack/get-terraform.sh index 0a9a47b046b..aae1b69cd08 100755 --- a/hack/get-terraform.sh +++ b/hack/get-terraform.sh @@ -24,8 +24,15 @@ then fi && TERRAFORM_VERSION="0.11.8" && TERRAFORM_URL="https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_${OS}_${ARCH}.zip" && -echo "pulling ${TERRAFORM_URL}" >&2 && +TERRAFORM_BINARY=".cache/terraform_${TERRAFORM_VERSION}_${OS}_${ARCH}" cd "$(dirname "$0")/.." && -mkdir -p bin && -curl -L "${TERRAFORM_URL}" | "${FUNZIP}" >bin/terraform && -chmod +x bin/terraform +if [ ! -x "${TERRAFORM_BINARY}" ] +then + echo "pulling ${TERRAFORM_URL}" >&2 && + mkdir -p "$(dirname "${TERRAFORM_BINARY}")" && + curl -L "${TERRAFORM_URL}" | "${FUNZIP}" >"${TERRAFORM_BINARY}" && + chmod +x "${TERRAFORM_BINARY}" +fi && +mkdir -p data/data/bin && +rm -f data/data/bin/terraform && +ln -s "../../../${TERRAFORM_BINARY}" data/data/bin/terraform diff --git a/pkg/asset/cluster/cluster.go b/pkg/asset/cluster/cluster.go index c88d166979d..92c5ac1ff1e 100644 --- a/pkg/asset/cluster/cluster.go +++ b/pkg/asset/cluster/cluster.go @@ -111,10 +111,10 @@ func (c *Cluster) Generate(parents asset.Parents) (err error) { return fmt.Errorf("no known platform") } - logrus.Infof("Using Terraform to create cluster...") + logrus.Infof("Creating cluster...") stateFile, err := terraform.Apply(tmpDir, installConfig.Config.Platform.Name()) if err != nil { - err = errors.Wrap(err, "failed to run terraform") + err = errors.Wrap(err, "failed to create cluster") } data, err2 := ioutil.ReadFile(stateFile) @@ -131,7 +131,6 @@ func (c *Cluster) Generate(parents asset.Parents) (err error) { } } - // TODO(yifan): Use the kubeconfig to verify the cluster is up. return err } diff --git a/pkg/terraform/executor.go b/pkg/terraform/executor.go index abd46ab1643..d00b666357e 100644 --- a/pkg/terraform/executor.go +++ b/pkg/terraform/executor.go @@ -2,59 +2,20 @@ package terraform import ( "bytes" - "os" "os/exec" "path/filepath" - "runtime" - "strings" "github.com/openshift/installer/pkg/lineprinter" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -// executor enables calling Terraform from Go, across platforms, with any -// additional providers/provisioners that the currently executing binary -// exposes. -// -// The Terraform binary is expected to be in the executing binary's folder, in -// the current working directory or in the PATH. -type executor struct { - binaryPath string -} - -// Set the binary names for different platforms -const ( - tfBinUnix = "terraform" - tfBinWindows = "terraform.exe" -) - -// errBinaryNotFound denotes the fact that the Terraform binary could not be -// found on disk. -var errBinaryNotFound = errors.New( - "terraform not in executable's folder, cwd nor PATH", -) - -// newExecutor initializes a new Executor. -func newExecutor() (*executor, error) { - ex := new(executor) - - // Find the Terraform binary. - binPath, err := tfBinaryPath() - if err != nil { - return nil, errors.Wrap(err, "failed to get Terraform binary's path") - } - - ex.binaryPath = binPath - return ex, nil -} - // Execute runs the given command and arguments against Terraform. // // An error is returned if the Terraform binary could not be found, or if the // Terraform call itself failed, in which case, details can be found in the // output. -func (ex *executor) execute(clusterDir string, args ...string) error { +func Execute(clusterDir string, args ...string) error { // Prepare Terraform command by setting up the command, configuration, // and the working directory if clusterDir == "" { @@ -67,7 +28,7 @@ func (ex *executor) execute(clusterDir string, args ...string) error { stderr := &bytes.Buffer{} - cmd := exec.Command(ex.binaryPath, args...) + cmd := exec.Command(filepath.Join(clusterDir, executablePath), args...) cmd.Dir = clusterDir cmd.Stdout = linePrinter cmd.Stderr = stderr @@ -80,67 +41,3 @@ func (ex *executor) execute(clusterDir string, args ...string) error { } return err } - -// Version gets the output of 'terrraform version'. -func Version() (version string, err error) { - // Find the Terraform binary. - binPath, err := tfBinaryPath() - if err != nil { - return "", err - } - - output, err := exec.Command(binPath, "version").Output() - if err != nil { - exitError := err.(*exec.ExitError) - if len(exitError.Stderr) == 0 { - exitError.Stderr = output - } - } - return strings.TrimRight(string(output), "\n"), err -} - -// tfBinaryPath searches for a Terraform binary on disk: -// - in the executing binary's folder, -// - in the current working directory, -// - in the PATH. -// The first to be found is the one returned. -func tfBinaryPath() (string, error) { - // Depending on the platform, the expected binary name is different. - binaryFileName := tfBinUnix - if runtime.GOOS == "windows" { - binaryFileName = tfBinWindows - } - - // Find the current executable's path, gets an absolute path or error - execPath, err := os.Executable() - if err == nil { - // execPath could be a symlink - if stat, err := os.Stat(execPath); err == nil && (stat.Mode()&os.ModeSymlink) == os.ModeSymlink { - if evalExecPath, err := filepath.EvalSymlinks(execPath); err != nil { - execPath = evalExecPath - } - } - - // Look into the executable's folder. - path := filepath.Join(filepath.Dir(execPath), binaryFileName) - if stat, err := os.Stat(path); err == nil && !stat.IsDir() { - return path, nil - } - } - - // Look into cwd. - if workingDirectory, err := os.Getwd(); err == nil { - path := filepath.Join(workingDirectory, binaryFileName) - if stat, err := os.Stat(path); err == nil && !stat.IsDir() { - return path, nil - } - } - - // If we still haven't found the executable, look for it - // in the PATH. - if path, err := exec.LookPath(binaryFileName); err == nil { - return filepath.Abs(path) - } - - return "", errBinaryNotFound -} diff --git a/pkg/terraform/terraform.go b/pkg/terraform/terraform.go index 69d0ed797ff..7199a8b6e7e 100644 --- a/pkg/terraform/terraform.go +++ b/pkg/terraform/terraform.go @@ -2,6 +2,7 @@ package terraform import ( "fmt" + "os" "path/filepath" "github.com/openshift/installer/data" @@ -10,17 +11,12 @@ import ( const ( // StateFileName is the default name for Terraform state files. - StateFileName string = "terraform.tfstate" + StateFileName string = "terraform.tfstate" + executablePath string = "bin/terraform" ) func terraformExec(clusterDir string, args ...string) error { - // Create an executor - ex, err := newExecutor() - if err != nil { - return errors.Wrap(err, "failed to create Terraform executor") - } - - err = ex.execute(clusterDir, args...) + err := Execute(clusterDir, args...) if err != nil { return errors.Wrap(err, "failed to execute Terraform") } @@ -93,6 +89,16 @@ func unpackAndInit(dir string, platform string) (err error) { return errors.Wrap(err, "failed to unpack Terraform modules") } + err = data.Unpack(filepath.Join(dir, executablePath), filepath.FromSlash(executablePath)) + if err != nil { + return errors.Wrap(err, "failed to unpack Terraform binary") + } + + err = os.Chmod(filepath.Join(dir, executablePath), 0555) + if err != nil { + return errors.Wrap(err, "failed to make Terraform binary executable") + } + err = terraformExec(dir, "init", "-input=false", "-no-color") if err != nil { return errors.Wrap(err, "failed to initialize Terraform")