Skip to content

Commit

Permalink
pkg/asset/cluster: Pull metadata into the Cluster asset
Browse files Browse the repository at this point in the history
Terraform can die part-way through launching a cluster for many
reasons, and we want it to be easy for users to clean up.
destroy-cluster gets its teardown information from metadata.json, but
before this commit we were only writing the file after a successful
Terraform run.  That left users with failed Terraform runs scrambling
to delete their cluster on their own (e.g. with virsh-cleanup.sh or
other external tools).

One solution to this problem would be to move the Metadata asset
before the Cluster asset in targetAssets.  But Abhinav wants:

* To provide metadata about the cluster (e.g. bootstrap and master
  IPs) that is only available after Terraform wraps up.

* To minimize the number of assets, so no pre-terraform-metadata.json
  and post-terraform-metadata.json as separate assets.

This commit squashes the metadata file into the Cluster asset, so we
can fill it in as we go.

I've also updated openshift-install to write any files from failed
assets before exiting, so we can land the metadata and recovered
Terraform state where the user can find them before we die.  To avoid
nil-dereference panics after this change, I've also updated a number
of Files() implementations to avoid returning a [<nil>] slice.

The SilenceError and SilenceUsage additions work around [1].

[1]: spf13/cobra#340
  • Loading branch information
wking committed Oct 11, 2018
1 parent bbe674e commit e7f24d6
Show file tree
Hide file tree
Showing 13 changed files with 112 additions and 119 deletions.
2 changes: 2 additions & 0 deletions cmd/openshift-install/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ func newRootCmd() *cobra.Command {
Short: "Creates OpenShift clusters",
Long: "",
PersistentPreRunE: runRootCmd,
SilenceErrors: true,
SilenceUsage: true,
}
cmd.PersistentFlags().StringVar(&rootOpts.dir, "dir", ".", "assets directory")
cmd.PersistentFlags().StringVar(&rootOpts.logLevel, "log-level", "info", "log level (e.g. \"debug | info | warn | error\")")
Expand Down
18 changes: 13 additions & 5 deletions cmd/openshift-install/targets.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/openshift/installer/pkg/asset/installconfig"
"github.com/openshift/installer/pkg/asset/kubeconfig"
"github.com/openshift/installer/pkg/asset/manifests"
"github.com/openshift/installer/pkg/asset/metadata"
)

type target struct {
Expand Down Expand Up @@ -55,7 +54,7 @@ var targets = []target{{
Short: "Create an OpenShift cluster",
Long: "",
},
assets: []asset.WritableAsset{&cluster.TerraformVariables{}, &kubeconfig.Admin{}, &cluster.Cluster{}, &metadata.Metadata{}},
assets: []asset.WritableAsset{&cluster.TerraformVariables{}, &kubeconfig.Admin{}, &cluster.Cluster{}},
}}

func newTargetsCmd() []*cobra.Command {
Expand All @@ -76,11 +75,20 @@ func runTargetCmd(targets ...asset.WritableAsset) func(cmd *cobra.Command, args
if exitError, ok := errors.Cause(err).(*exec.ExitError); ok && len(exitError.Stderr) > 0 {
logrus.Error(strings.Trim(string(exitError.Stderr), "\n"))
}
return errors.Wrapf(err, "failed to generate %s", a.Name())
err = errors.Wrapf(err, "failed to generate %s", a.Name())
}

if err := asset.PersistToFile(a, rootOpts.dir); err != nil {
return errors.Wrapf(err, "failed to write asset (%s) to disk", a.Name())
if err2 := asset.PersistToFile(a, rootOpts.dir); err2 != nil {
err2 = errors.Wrapf(err2, "failed to write asset (%s) to disk", a.Name())
if err != nil {
logrus.Error(err2)
return err
}
return err2
}

if err != nil {
return err
}
}
return nil
Expand Down
75 changes: 64 additions & 11 deletions pkg/asset/cluster/cluster.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cluster

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
Expand All @@ -10,18 +12,23 @@ import (

"github.com/openshift/installer/data"
"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/asset/installconfig"
"github.com/openshift/installer/pkg/asset/kubeconfig"
"github.com/openshift/installer/pkg/terraform"
"github.com/openshift/installer/pkg/types"
)

const (
// MetadataFilename is name of the file where clustermetadata is stored.
MetadataFilename = "metadata.json"

stateFileName = "terraform.state"
)

// Cluster uses the terraform executable to launch a cluster
// with the given terraform tfvar and generated templates.
type Cluster struct {
file *asset.File
files []*asset.File
}

var _ asset.WritableAsset = (*Cluster)(nil)
Expand All @@ -35,16 +42,18 @@ func (c *Cluster) Name() string {
// the cluster.
func (c *Cluster) Dependencies() []asset.Asset {
return []asset.Asset{
&installconfig.InstallConfig{},
&TerraformVariables{},
&kubeconfig.Admin{},
}
}

// Generate launches the cluster and generates the terraform state file on disk.
func (c *Cluster) Generate(parents asset.Parents) error {
func (c *Cluster) Generate(parents asset.Parents) (err error) {
installConfig := &installconfig.InstallConfig{}
terraformVariables := &TerraformVariables{}
adminKubeconfig := &kubeconfig.Admin{}
parents.Get(terraformVariables, adminKubeconfig)
parents.Get(installConfig, terraformVariables, adminKubeconfig)

// Copy the terraform.tfvars to a temp directory where the terraform will be invoked within.
tmpDir, err := ioutil.TempDir(os.TempDir(), "openshift-install-")
Expand All @@ -63,6 +72,50 @@ func (c *Cluster) Generate(parents asset.Parents) error {
return err
}

metadata := &types.ClusterMetadata{
ClusterName: installConfig.Config.ObjectMeta.Name,
}

defer func() {
if data, err2 := json.Marshal(metadata); err2 == nil {
c.files = append(c.files, &asset.File{
Filename: MetadataFilename,
Data: data,
})
} else {
err2 = errors.Wrap(err2, "failed to Marshal ClusterMetadata")
if err == nil {
err = err2
} else {
logrus.Error(err2)
}
}
// serialize metadata and stuff it into c.files
}()

switch {
case installConfig.Config.Platform.AWS != nil:
metadata.ClusterPlatformMetadata.AWS = &types.ClusterAWSPlatformMetadata{
Region: installConfig.Config.Platform.AWS.Region,
Identifier: map[string]string{
"tectonicClusterID": installConfig.Config.ClusterID,
},
}
case installConfig.Config.Platform.OpenStack != nil:
metadata.ClusterPlatformMetadata.OpenStack = &types.ClusterOpenStackPlatformMetadata{
Region: installConfig.Config.Platform.OpenStack.Region,
Identifier: map[string]string{
"tectonicClusterID": installConfig.Config.ClusterID,
},
}
case installConfig.Config.Platform.Libvirt != nil:
metadata.ClusterPlatformMetadata.Libvirt = &types.ClusterLibvirtPlatformMetadata{
URI: installConfig.Config.Platform.Libvirt.URI,
}
default:
return fmt.Errorf("no known platform")
}

if err := data.Unpack(filepath.Join(tmpDir, "config.tf"), "config.tf"); err != nil {
return err
}
Expand All @@ -81,24 +134,24 @@ func (c *Cluster) Generate(parents asset.Parents) error {
}

data, err2 := ioutil.ReadFile(stateFile)
if err2 != nil {
if err2 == nil {
c.files = append(c.files, &asset.File{
Filename: stateFileName,
Data: data,
})
} else {
if err == nil {
err = err2
} else {
logrus.Errorf("Failed to read tfstate: %v", err2)
}
}

c.file = &asset.File{
Filename: stateFileName,
Data: data,
}

// TODO(yifan): Use the kubeconfig to verify the cluster is up.
return nil
return err
}

// Files returns the files generated by the asset.
func (c *Cluster) Files() []*asset.File {
return []*asset.File{c.file}
return c.files
}
5 changes: 4 additions & 1 deletion pkg/asset/cluster/tfvars.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,8 @@ func (t *TerraformVariables) Generate(parents asset.Parents) error {

// Files returns the files generated by the asset.
func (t *TerraformVariables) Files() []*asset.File {
return []*asset.File{t.file}
if t.file != nil {
return []*asset.File{t.file}
}
return []*asset.File{}
}
5 changes: 4 additions & 1 deletion pkg/asset/ignition/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ func (a *Bootstrap) Name() string {

// Files returns the files generated by the asset.
func (a *Bootstrap) Files() []*asset.File {
return []*asset.File{a.file}
if a.file != nil {
return []*asset.File{a.file}
}
return []*asset.File{}
}

// getTemplateData returns the data to use to execute bootstrap templates.
Expand Down
5 changes: 4 additions & 1 deletion pkg/asset/ignition/machine/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,8 @@ func (a *Worker) Name() string {

// Files returns the files generated by the asset.
func (a *Worker) Files() []*asset.File {
return []*asset.File{a.file}
if a.file != nil {
return []*asset.File{a.file}
}
return []*asset.File{}
}
5 changes: 4 additions & 1 deletion pkg/asset/installconfig/installconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ func (a *InstallConfig) Name() string {

// Files returns the files generated by the asset.
func (a *InstallConfig) Files() []*asset.File {
return []*asset.File{a.file}
if a.file != nil {
return []*asset.File{a.file}
}
return []*asset.File{}
}

// ClusterDNSIP returns the string representation of the DNS server's IP
Expand Down
5 changes: 4 additions & 1 deletion pkg/asset/kubeconfig/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,8 @@ func (k *kubeconfig) generate(

// Files returns the files generated by the asset.
func (k *kubeconfig) Files() []*asset.File {
return []*asset.File{k.file}
if k.file != nil {
return []*asset.File{k.file}
}
return []*asset.File{}
}
5 changes: 4 additions & 1 deletion pkg/asset/manifests/kube-addon-operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,8 @@ func (kao *kubeAddonOperator) Generate(dependencies asset.Parents) error {

// Files returns the files generated by the asset.
func (kao *kubeAddonOperator) Files() []*asset.File {
return []*asset.File{kao.file}
if kao.file != nil {
return []*asset.File{kao.file}
}
return []*asset.File{}
}
5 changes: 4 additions & 1 deletion pkg/asset/manifests/kube-core-operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ func (kco *KubeCoreOperator) Generate(dependencies asset.Parents) error {

// Files returns the files generated by the asset.
func (kco *KubeCoreOperator) Files() []*asset.File {
return []*asset.File{kco.file}
if kco.file != nil {
return []*asset.File{kco.file}
}
return []*asset.File{}
}

func getEtcdServersURLs(ic *types.InstallConfig) []string {
Expand Down
2 changes: 0 additions & 2 deletions pkg/asset/metadata/doc.go

This file was deleted.

89 changes: 0 additions & 89 deletions pkg/asset/metadata/metadata.go

This file was deleted.

10 changes: 5 additions & 5 deletions pkg/destroy/destroyer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"

"github.com/openshift/installer/pkg/asset/metadata"
"github.com/openshift/installer/pkg/asset/cluster"
"github.com/openshift/installer/pkg/types"
)

Expand All @@ -26,15 +26,15 @@ var Registry = make(map[string]NewFunc)

// New returns a Destroyer based on `metadata.json` in `rootDir`.
func New(logger logrus.FieldLogger, rootDir string) (Destroyer, error) {
path := filepath.Join(rootDir, metadata.MetadataFilename)
raw, err := ioutil.ReadFile(filepath.Join(rootDir, metadata.MetadataFilename))
path := filepath.Join(rootDir, cluster.MetadataFilename)
raw, err := ioutil.ReadFile(filepath.Join(rootDir, cluster.MetadataFilename))
if err != nil {
return nil, errors.Wrapf(err, "failed to read %s file", metadata.MetadataFilename)
return nil, errors.Wrapf(err, "failed to read %s file", cluster.MetadataFilename)
}

var cmetadata *types.ClusterMetadata
if err := json.Unmarshal(raw, &cmetadata); err != nil {
return nil, errors.Wrapf(err, "failed to Unmarshal data from %s file to types.ClusterMetadata", metadata.MetadataFilename)
return nil, errors.Wrapf(err, "failed to Unmarshal data from %s file to types.ClusterMetadata", cluster.MetadataFilename)
}

platform := cmetadata.Platform()
Expand Down

0 comments on commit e7f24d6

Please sign in to comment.