Skip to content

Commit

Permalink
Most of the changes to get the survey working (openshift#26)
Browse files Browse the repository at this point in the history
initial install-config.yaml generation via the user survey
  • Loading branch information
23TNC authored and clnperez committed Sep 2, 2021
1 parent 04a86b6 commit 5f2f8a8
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 10 deletions.
2 changes: 1 addition & 1 deletion pkg/asset/installconfig/installconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func (a *InstallConfig) Generate(parents asset.Parents) error {
a.Config.BareMetal = platform.BareMetal
a.Config.Ovirt = platform.Ovirt
a.Config.Kubevirt = platform.Kubevirt

a.Config.PowerVS = platform.PowerVS
return a.finish("")
}

Expand Down
7 changes: 7 additions & 0 deletions pkg/asset/installconfig/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
libvirtconfig "github.com/openshift/installer/pkg/asset/installconfig/libvirt"
openstackconfig "github.com/openshift/installer/pkg/asset/installconfig/openstack"
ovirtconfig "github.com/openshift/installer/pkg/asset/installconfig/ovirt"
powervsconfig "github.com/openshift/installer/pkg/asset/installconfig/powervs"
vsphereconfig "github.com/openshift/installer/pkg/asset/installconfig/vsphere"
"github.com/openshift/installer/pkg/types"
"github.com/openshift/installer/pkg/types/aws"
Expand All @@ -30,6 +31,7 @@ import (
"github.com/openshift/installer/pkg/types/none"
"github.com/openshift/installer/pkg/types/openstack"
"github.com/openshift/installer/pkg/types/ovirt"
"github.com/openshift/installer/pkg/types/powervs"
"github.com/openshift/installer/pkg/types/vsphere"
)

Expand Down Expand Up @@ -106,6 +108,11 @@ func (a *platform) Generate(asset.Parents) error {
if err != nil {
return err
}
case powervs.Name:
a.PowerVS, err = powervsconfig.Platform()
if err != nil {
return err
}
default:
return fmt.Errorf("unknown platform type %q", platform)
}
Expand Down
134 changes: 134 additions & 0 deletions pkg/asset/installconfig/powervs/platform.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package powervs

import (
"fmt"
"os"
"sort"
"strings"

survey "github.com/AlecAivazis/survey/v2"
"github.com/AlecAivazis/survey/v2/core"
"github.com/openshift/installer/pkg/types/powervs"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)

// Platform collects powervs-specific configuration.
func Platform() (*powervs.Platform, error) {
regions := knownRegions()

// TODO(cklokman): This section came from aws and transforms the response from knownRegions
// into long and short regions to prompt the user for region select this section
// need need to be different based on powervs's implementation of knownRegions
//

longRegions := make([]string, 0, len(regions))
shortRegions := make([]string, 0, len(regions))
for id, location := range regions {
longRegions = append(longRegions, fmt.Sprintf("%s (%s)", id, location))
shortRegions = append(shortRegions, id)
}

var regionTransform survey.Transformer = func(ans interface{}) interface{} {
switch v := ans.(type) {
case core.OptionAnswer:
return core.OptionAnswer{Value: strings.SplitN(v.Value, " ", 2)[0], Index: v.Index}
case string:
return strings.SplitN(v, " ", 2)[0]
}
return ""
}

ssn, err := GetSession()
if err != nil {
return nil, err
}

var region string

sessionRegion := ssn.Session.Region
if sessionRegion != "" {
if IsKnownRegion(sessionRegion) {
region = sessionRegion
} else {
logrus.Warnf("Unrecognized Power VS region %s, ignoring IC_REGION", sessionRegion)
}
}

sort.Strings(longRegions)
sort.Strings(shortRegions)
if region == "" {
err = survey.Ask([]*survey.Question{
{
Prompt: &survey.Select{
Message: "Region",
Help: "The Power VS region to be used for installation.",
// Default: fmt.Sprintf("%s (%s)", defaultRegion, regions[defaultRegion]),
Options: longRegions,
},
Validate: survey.ComposeValidators(survey.Required, func(ans interface{}) error {
choice := regionTransform(ans).(core.OptionAnswer).Value
i := sort.SearchStrings(shortRegions, choice)
if i == len(shortRegions) || shortRegions[i] != choice {
return errors.Errorf("invalid region %q", choice)
}
return nil
}),
Transform: regionTransform,
},
}, &region)
if err != nil {
return nil, err
}
}

zones := knownZones(region)
defaultZone := zones[0]

var zoneTransform survey.Transformer = func(ans interface{}) interface{} {
switch v := ans.(type) {
case core.OptionAnswer:
return core.OptionAnswer{Value: strings.SplitN(v.Value, " ", 2)[0], Index: v.Index}
case string:
return strings.SplitN(v, " ", 2)[0]
}
return ""
}

var zone string
err = survey.Ask([]*survey.Question{
{
Prompt: &survey.Select{
Message: "Zone",
Help: "The powervs zone within the region to be used for installation.",
Default: fmt.Sprintf("%s", defaultZone),
Options: zones,
},
Validate: survey.ComposeValidators(survey.Required, func(ans interface{}) error {
choice := zoneTransform(ans).(core.OptionAnswer).Value
i := sort.SearchStrings(zones, choice)
if i == len(zones) || zones[i] != choice {
return errors.Errorf("invalid zone %q", choice)
}
return nil
}),
Transform: zoneTransform,
},
}, &zone)
if err != nil {
return nil, err
}

var p powervs.Platform
if osOverride := os.Getenv("OPENSHIFT_INSTALL_OS_IMAGE_OVERRIDE"); len(osOverride) != 0 {
p.BootstrapOSImage = osOverride
p.ClusterOSImage = osOverride
}

p.Region = region
p.Zone = zone
p.APIKey = ssn.Creds.APIKey
p.UserID = ssn.Creds.UserID

return &p, nil
}
43 changes: 43 additions & 0 deletions pkg/asset/installconfig/powervs/regions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package powervs

import (
"github.com/openshift/installer/pkg/rhcos"
)

func knownRegions() map[string]string {

regions := make(map[string]string)

for _, region := range rhcos.PowerVSRegions {
regions[region["name"]] = region["description"]
}
return regions
}

// IsKnownRegion return true is a specified region is Known to the installer.
// A known region is subset of AWS regions and the regions where RHEL CoreOS images are published.
func IsKnownRegion(region string) bool {
if _, ok := knownRegions()[region]; ok {
return true
}
return false
}

// Todo(cklokman): Need some form of error handing in this function...
func knownZones(region string) []string {
return rhcos.PowerVSZones[region]
}

// IsKnownZone return true is a specified zone is Known to the installer.
func IsKnownZone(region string, zone string) bool {
if _, ok := knownRegions()[region]; ok {
zones := knownZones(region)
for _, z := range zones {
if z == zone {
return true
}
}
return false
}
return false
}
50 changes: 41 additions & 9 deletions pkg/asset/installconfig/powervs/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/pkg/errors"

survey "github.com/AlecAivazis/survey/v2"
"github.com/IBM-Cloud/power-go-client/ibmpisession"
)

Expand All @@ -21,6 +22,14 @@ var (
// Session is an object representing a session for the IBM Power VS API.
type Session struct {
Session *ibmpisession.IBMPISession
Creds *UserCredentials
}

// UserCredentials is an object representing the credentials used for IBM Power VS during
// the creation of the install_config.yaml
type UserCredentials struct {
APIKey string
UserID string
}

// GetSession returns an IBM Cloud session by using credentials found in default locations in order:
Expand All @@ -40,35 +49,56 @@ type Session struct {
4) put it into Platform {userid: , iamtoken: , ...}
*/
func GetSession() (*Session, error) {
s, err := getPISession()
s, uc, err := getPISession()
if err != nil {
return nil, errors.Wrap(err, "failed to load credentials")
}

return &Session{Session: s}, nil
return &Session{Session: s, Creds: uc}, nil
}

/*
// https://github.com/IBM-Cloud/power-go-client/blob/master/ibmpisession/ibmpowersession.go
*/
func getPISession() (*ibmpisession.IBMPISession, error) {
func getPISession() (*ibmpisession.IBMPISession, *UserCredentials, error) {

var (
id, passwd, region, zone string
id, passwd, apikey, region, zone string
)

if id = os.Getenv("IBMID"); len(id) == 0 {
return nil, errors.New("empty IBMID environment variable")
err := survey.Ask([]*survey.Question{
{
Prompt: &survey.Input{
Message: "IBM Cloud User ID",
Help: "The login for \nhttps://cloud.ibm.com/",
},
},
}, &id)
if err != nil {
return nil, nil, errors.New("Error saving the IBMID variable")
}
}
if passwd = os.Getenv("IBMID_PASSWORD"); len(passwd) == 0 {
return nil, errors.New("empty IBMID_PASSWORD variable")

if apikey = os.Getenv("API_KEY"); len(apikey) == 0 {
err := survey.Ask([]*survey.Question{
{
Prompt: &survey.Password{
Message: "IBM Cloud API Key",
Help: "The api key installation.\nhttps://cloud.ibm.com/iam/apikeys",
},
},
}, &apikey)
if err != nil {
return nil, nil, errors.New("Error saving the API_KEY variable")
}
}

region = os.Getenv("IBMCLOUD_REGION")
// this can also be pulled from ~/bluemix/config.json
if r2 := os.Getenv("IC_REGION"); len(r2) > 0 {
if len(region) > 0 && region != r2 {
return nil, errors.New(fmt.Sprintf("conflicting values for IBM Cloud Region: IBMCLOUD_REGION: %s and IC_REGION: %s", region, r2))
return nil, nil, errors.New(fmt.Sprintf("conflicting values for IBM Cloud Region: IBMCLOUD_REGION: %s and IC_REGION: %s", region, r2))
}
if len(region) == 0 {
region = r2
Expand All @@ -81,5 +111,7 @@ func getPISession() (*ibmpisession.IBMPISession, error) {

// @TOOD: query if region is multi-zone? or just pass through err...
// @TODO: pass through debug?
return ibmpisession.New(passwd, region, false, defSessionTimeout, id, zone)
s, err := ibmpisession.New(passwd, region, false, defSessionTimeout, id, zone)
uc := &UserCredentials{UserID: id, APIKey: apikey}
return s, uc, err
}
21 changes: 21 additions & 0 deletions pkg/rhcos/powervs_regions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package rhcos

// TODO(cklokman): This entire file should be programatically generated similar to aws

// PowerVSZones holds the zones cooresponding to each region found in PowerVS Regions
// TODO(cklokman): These are fictional zones for testing
var PowerVSZones = map[string][]string{
"us-south": []string{
"us-south-zone1",
"us-south-zone2",
},
}

// PowerVSRegions holds the regions for IBM Power VS, and descriptions used during the survey
// TODO(cklokman): These are fictional regtions for testing
var PowerVSRegions = []map[string]string{
map[string]string{
"name": "us-south",
"description": "This is the us-south test region.",
},
}
4 changes: 4 additions & 0 deletions pkg/types/defaults/machinepools.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package defaults
import (
"github.com/openshift/installer/pkg/types"
"github.com/openshift/installer/pkg/types/libvirt"
"github.com/openshift/installer/pkg/types/powervs"
"github.com/openshift/installer/pkg/version"
)

Expand All @@ -11,7 +12,10 @@ func SetMachinePoolDefaults(p *types.MachinePool, platform string) {
defaultReplicaCount := int64(3)
if platform == libvirt.Name {
defaultReplicaCount = 1
} else if platform == powervs.Name {
p.Architecture = "ppc64le"
}

if p.Replicas == nil {
p.Replicas = &defaultReplicaCount
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/types/powervs/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ type Platform struct {
// Required for multi-zone regions.
Zone string `json:"zone"`

UserID string `json:"userid"`
APIKey string `json:"apikey"`

// Subnets specifies existing subnets (by ID) where cluster
// resources will be created. Leave unset to have the installer
// create subnets in a new VPC on your behalf.
Expand Down

0 comments on commit 5f2f8a8

Please sign in to comment.