Skip to content

Commit

Permalink
Merge pull request #1763 from abhinavdahiya/gather_update
Browse files Browse the repository at this point in the history
gather: update the state handling for terraform 0.12
  • Loading branch information
openshift-merge-robot authored May 28, 2019
2 parents 5f631ff + 3c89497 commit cc44c74
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 91 deletions.
112 changes: 21 additions & 91 deletions cmd/openshift-install/gather.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
package main

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

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

"github.com/openshift/installer/pkg/asset/installconfig"
assetstore "github.com/openshift/installer/pkg/asset/store"
"github.com/openshift/installer/pkg/terraform"
gatheraws "github.com/openshift/installer/pkg/terraform/gather/aws"
gatherlibvirt "github.com/openshift/installer/pkg/terraform/gather/libvirt"
gatheropenstack "github.com/openshift/installer/pkg/terraform/gather/openstack"
"github.com/openshift/installer/pkg/types"
awstypes "github.com/openshift/installer/pkg/types/aws"
libvirttypes "github.com/openshift/installer/pkg/types/libvirt"
Expand Down Expand Up @@ -85,16 +85,10 @@ func runGatherBootstrapCmd(directory string) error {
return errors.Wrapf(err, "failed to fetch %s", config.Name())
}

sfRaw, err := ioutil.ReadFile(tfStateFilePath)
tfstate, err := terraform.ReadState(tfStateFilePath)
if err != nil {
return errors.Wrapf(err, "failed to read %q", tfStateFilePath)
return errors.Wrapf(err, "failed to read state from %q", tfStateFilePath)
}

var tfstate terraformState
if err := json.Unmarshal(sfRaw, &tfstate); err != nil {
return errors.Wrapf(err, "failed to unmarshal %q", tfStateFilePath)
}

bootstrap, masters, err := extractHostAddresses(config.Config, tfstate)
if err != nil {
if err2, ok := err.(errUnSupportedGatherPlatform); ok {
Expand All @@ -117,105 +111,41 @@ func logGatherBootstrap(bootstrap string, masters []string) {
logrus.Infof("scp core@%s:~/log-bundle.tar.gz .", bootstrap)
}

func extractHostAddresses(config *types.InstallConfig, tfstate terraformState) (bootstrap string, masters []string, err error) {
mcount := *config.ControlPlane.Replicas
func extractHostAddresses(config *types.InstallConfig, tfstate *terraform.State) (bootstrap string, masters []string, err error) {
switch config.Platform.Name() {
case awstypes.Name:
bm := tfstate.Modules["root/bootstrap"]
bootstrap, _, err = unstructured.NestedString(bm.Resources["aws_instance.bootstrap"], "primary", "attributes", "public_ip")
bootstrap, err = gatheraws.BootstrapIP(tfstate)
if err != nil {
return bootstrap, masters, errors.Wrapf(err, "failed to get bootstrap host addresses")
return bootstrap, masters, err
}

mm := tfstate.Modules["root/masters"]
for idx := int64(0); idx < mcount; idx++ {
r := fmt.Sprintf("aws_instance.master.%d", idx)
if mcount == 1 {
r = "aws_instance.master"
}
var master string
master, _, err = unstructured.NestedString(mm.Resources[r], "primary", "attributes", "private_ip")
if err != nil {
return bootstrap, masters, errors.Wrapf(err, "failed to get master host addresses")
}
masters = append(masters, master)
masters, err = gatheraws.ControlPlaneIPs(tfstate)
if err != nil {
logrus.Error(err)
}
case libvirttypes.Name:
bm := tfstate.Modules["root/bootstrap"]
bootstrap, _, err = unstructured.NestedString(bm.Resources["libvirt_domain.bootstrap"], "primary", "attributes", "network_interface.0.hostname")
bootstrap, err = gatherlibvirt.BootstrapIP(tfstate)
if err != nil {
return bootstrap, masters, errors.Wrapf(err, "failed to get bootstrap host addresses")
return bootstrap, masters, err
}

rm := tfstate.Modules["root"]
for idx := int64(0); idx < mcount; idx++ {
r := fmt.Sprintf("libvirt_domain.master.%d", idx)
if mcount == 1 {
r = "libvirt_domain.master"
}
var master string
master, _, err = unstructured.NestedString(rm.Resources[r], "primary", "attributes", "network_interface.0.hostname")
if err != nil {
return bootstrap, masters, errors.Wrapf(err, "failed to get master host addresses")
}
masters = append(masters, master)
masters, err = gatherlibvirt.ControlPlaneIPs(tfstate)
if err != nil {
logrus.Error(err)
}
case openstacktypes.Name:
bm := tfstate.Modules["root/bootstrap"]
bootstrap, _, err = unstructured.NestedString(bm.Resources["openstack_compute_instance_v2.bootstrap"], "primary", "attributes", "access_ip_v4")
bootstrap, err = gatheropenstack.BootstrapIP(tfstate)
if err != nil {
return bootstrap, masters, errors.Wrapf(err, "failed to get bootstrap host addresses")
return bootstrap, masters, err
}

mm := tfstate.Modules["root/masters"]
for idx := int64(0); idx < mcount; idx++ {
r := fmt.Sprintf("openstack_compute_instance_v2.master_conf.%d", idx)
if mcount == 1 {
r = "openstack_compute_instance_v2.master_conf"
}
var master string
master, _, err = unstructured.NestedString(mm.Resources[r], "primary", "attributes", "access_ip_v4")
if err != nil {
return bootstrap, masters, errors.Wrapf(err, "failed to get master host addresses")
}
masters = append(masters, master)
masters, err = gatheropenstack.ControlPlaneIPs(tfstate)
if err != nil {
logrus.Error(err)
}
default:
return "", nil, errUnSupportedGatherPlatform{Message: fmt.Sprintf("Cannot fetch the bootstrap and control plane host addresses from state file for %s platform", config.Platform.Name())}
}
return bootstrap, masters, nil
}

type terraformState struct {
Modules map[string]terraformStateModule
}

type terraformStateModule struct {
Resources map[string]map[string]interface{} `json:"resources"`
}

func (tfs *terraformState) UnmarshalJSON(raw []byte) error {
var transform struct {
Modules []struct {
Path []string `json:"path"`
terraformStateModule
} `json:"modules"`
}
if err := json.Unmarshal(raw, &transform); err != nil {
return err
}
if tfs == nil {
tfs = &terraformState{}
}
if tfs.Modules == nil {
tfs.Modules = make(map[string]terraformStateModule)
}
for _, m := range transform.Modules {
tfs.Modules[strings.Join(m.Path, "/")] = terraformStateModule{Resources: m.Resources}
}
return nil
}

type errUnSupportedGatherPlatform struct {
Message string
}
Expand Down
32 changes: 32 additions & 0 deletions pkg/terraform/exec/state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package exec

import (
"bytes"
"os"

"github.com/hashicorp/terraform/states/statefile"
"github.com/pkg/errors"
)

// ReadState reads the terraform state from file and returns the contents in bytes
// It returns an error if reading the state was unsuccessful
// ReadState utilizes the terraform's internal wiring to upconvert versions of terraform state to return
// the state it currently recognizes.
func ReadState(file string) ([]byte, error) {
f, err := os.Open(file)
if err != nil {
return nil, errors.Wrapf(err, "failed to open %q", file)
}
defer f.Close()

sf, err := statefile.Read(f)
if err != nil {
return nil, errors.Wrapf(err, "failed to read statefile from %q", file)
}

out := bytes.Buffer{}
if err := statefile.Write(sf, &out); err != nil {
return nil, errors.Wrapf(err, "failed to write statefile")
}
return out.Bytes(), nil
}
45 changes: 45 additions & 0 deletions pkg/terraform/gather/aws/ip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Package aws contains utilities that help gather AWS specific
// information from terraform state.
package aws

import (
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
utilerrors "k8s.io/apimachinery/pkg/util/errors"

"github.com/openshift/installer/pkg/terraform"
)

// BootstrapIP returns the ip address for bootstrap host.
func BootstrapIP(tfs *terraform.State) (string, error) {
br, err := terraform.LookupResource(tfs, "module.bootstrap", "aws_instance", "bootstrap")
if err != nil {
return "", errors.Wrap(err, "failed to lookup bootstrap")
}
if len(br.Instances) == 0 {
return "", errors.New("no bootstrap instance found")
}
bootstrap, _, err := unstructured.NestedString(br.Instances[0].Attributes, "public_ip")
if err != nil {
return "", errors.New("no public_ip found for bootstrap")
}
return bootstrap, nil
}

// ControlPlaneIPs returns the ip addresses for control plane hosts.
func ControlPlaneIPs(tfs *terraform.State) ([]string, error) {
mrs, err := terraform.LookupResource(tfs, "module.masters", "aws_instance", "master")
if err != nil {
return nil, errors.Wrap(err, "failed to lookup masters")
}
var errs []error
var masters []string
for idx, inst := range mrs.Instances {
master, _, err := unstructured.NestedString(inst.Attributes, "private_ip")
if err != nil {
errs = append(errs, errors.Wrapf(err, "no private_ip for master.%d", idx))
}
masters = append(masters, master)
}
return masters, utilerrors.NewAggregate(errs)
}
60 changes: 60 additions & 0 deletions pkg/terraform/gather/libvirt/ip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Package libvirt contains utilities that help gather Libvirt specific
// information from terraform state.
package libvirt

import (
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
utilerrors "k8s.io/apimachinery/pkg/util/errors"

"github.com/openshift/installer/pkg/terraform"
)

// BootstrapIP returns the ip address for bootstrap host.
func BootstrapIP(tfs *terraform.State) (string, error) {
br, err := terraform.LookupResource(tfs, "module.bootstrap", "libvirt_domain", "bootstrap")
if err != nil {
return "", errors.Wrap(err, "failed to lookup bootstrap")
}
if len(br.Instances) == 0 {
return "", errors.New("no bootstrap instance found")
}
bootstrap, err := hostnameForDomain(br.Instances[0].Attributes)
if err != nil {
return "", errors.Wrap(err, "failed to lookup hostname")
}
return bootstrap, nil
}

// ControlPlaneIPs returns the ip addresses for control plane hosts.
func ControlPlaneIPs(tfs *terraform.State) ([]string, error) {
mrs, err := terraform.LookupResource(tfs, "", "libvirt_domain", "master")
if err != nil {
return nil, errors.Wrap(err, "failed to lookup masters")
}
var errs []error
var masters []string
for idx, inst := range mrs.Instances {
master, err := hostnameForDomain(inst.Attributes)
if err != nil {
errs = append(errs, errors.Wrapf(err, "failed to lookup hostname for master.%d", idx))
}
masters = append(masters, master)
}
return masters, utilerrors.NewAggregate(errs)
}

func hostnameForDomain(attr map[string]interface{}) (string, error) {
nics, _, err := unstructured.NestedSlice(attr, "network_interface")
if err != nil {
return "", errors.Wrap(err, "failed to lookup network_interface")
}
if len(nics) == 0 {
return "", errors.New("no network_interface found")
}
hostname, _, err := unstructured.NestedString(nics[0].(map[string]interface{}), "hostname")
if err != nil {
return "", errors.New("no hostname found")
}
return hostname, nil
}
45 changes: 45 additions & 0 deletions pkg/terraform/gather/openstack/ip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Package openstack contains utilities that help gather Openstack specific
// information from terraform state.
package openstack

import (
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
utilerrors "k8s.io/apimachinery/pkg/util/errors"

"github.com/openshift/installer/pkg/terraform"
)

// BootstrapIP returns the ip address for bootstrap host.
func BootstrapIP(tfs *terraform.State) (string, error) {
br, err := terraform.LookupResource(tfs, "module.bootstrap", "openstack_compute_instance_v2", "bootstrap")
if err != nil {
return "", errors.Wrap(err, "failed to lookup bootstrap")
}
if len(br.Instances) == 0 {
return "", errors.New("no bootstrap instance found")
}
bootstrap, _, err := unstructured.NestedString(br.Instances[0].Attributes, "access_ip_v4")
if err != nil {
return "", errors.New("no public_ip found for bootstrap")
}
return bootstrap, nil
}

// ControlPlaneIPs returns the ip addresses for control plane hosts.
func ControlPlaneIPs(tfs *terraform.State) ([]string, error) {
mrs, err := terraform.LookupResource(tfs, "module.masters", "openstack_compute_instance_v2", "master_conf")
if err != nil {
return nil, errors.Wrap(err, "failed to lookup masters")
}
var errs []error
var masters []string
for idx, inst := range mrs.Instances {
master, _, err := unstructured.NestedString(inst.Attributes, "access_ip_v4")
if err != nil {
errs = append(errs, errors.Wrapf(err, "no access_ip_v4 for master_conf.%d", idx))
}
masters = append(masters, master)
}
return masters, utilerrors.NewAggregate(errs)
}
Loading

0 comments on commit cc44c74

Please sign in to comment.