Skip to content
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 150 additions & 49 deletions pkg/asset/installconfig/powervs/session.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package powervs

import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"time"

"github.com/pkg/errors"

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

"github.com/sirupsen/logrus"
)

var (
Expand All @@ -19,105 +24,201 @@ var (
)

// Session is an object representing a session for the IBM Power VS API.
// A bluemix session object may be a better fit here
type Session struct {
Session *ibmpisession.IBMPISession
}

// GetSession returns an IBM Cloud session by using credentials found in default locations in order:
// env IBMID & env IBMID_PASSWORD,
// ~/.bluemix/config.json ? (see TODO below)
// and, if no creds are found, asks for them
/* @TODO: if you do an `ibmcloud login` (or in my case ibmcloud login --sso), you get
// a very nice creds file at ~/.bluemix/config.json, with an IAMToken. There's no username,
// though (just the account's owner id, but that's not the same). It may be necessary
// to use the IAMToken vs the password env var mentioned here:
// https://github.com/IBM-Cloud/power-go-client#ibm-cloud-sdk-for-power-cloud
// Yes, I think we'll need to use the IAMToken. There's a two-factor auth built into the ibmcloud login,
// so the password alone isn't enough. The IAMToken is generated as a result. So either:
1) require the user has done this already and pull from the file
2) ask the user to paste in their IAMToken.
3) let the password env var be the IAMToken? (Going with this atm since it's how I started)
4) put it into Platform {userid: , iamtoken: , ...}
*/
// PISessionVars is an object that holds the variables required to create an ibmpisession object
type PISessionVars struct {
ID string `json:"id,omitempty"`
APIKey string `json:"apikey,omitempty"`
Region string `json:"region,omitempty"`
Zone string `json:"zone,omitempty"`
}

// GetSession returns an ibmpisession object
func GetSession() (*Session, error) {
s, err := getPISession()
if err != nil {
return nil, errors.Wrap(err, "failed to load credentials")
}

return &Session{Session: s}, nil
}

/*
// https://github.com/IBM-Cloud/power-go-client/blob/master/ibmpisession/ibmpowersession.go
*/
var (
defaultAuthFilePath = filepath.Join(os.Getenv("HOME"), ".powervs", "config.json")
)

func getPISession() (*ibmpisession.IBMPISession, error) {

var (
id, iamtoken, apikey, region, zone string
)
var err error
var pisv PISessionVars

// First grab variables from the installer written authFilePath
logrus.Debug("Gathering variables from AuthFile")
err = getPISessionVarsFromAuthFile(&pisv)
if err != nil {
return nil, err
}

// Second grab variables from files, currently there are no other files to check
Comment thread
clnperez marked this conversation as resolved.
Outdated

// Third grab variables from the users enviornment
logrus.Debug("Gathering variables from user enviornment")
Comment thread
clnperez marked this conversation as resolved.
Outdated
err = getPISessionVarsFromEnv(&pisv)
if err != nil {
return nil, err
}

// Fourth prompt the user for the remaining variables
logrus.Debug("Gathering variables from user")
err = getPISessionVarsFromUser(&pisv)
if err != nil {
return nil, err
}

// Save variables to disk
err = savePISessionVars(&pisv)
if err != nil {
return nil, err
}

logrus.Debug("Done")
Comment thread
clnperez marked this conversation as resolved.
Outdated

// This is needed by ibmcloud code to gather dns information later
os.Setenv("IC_API_KEY", pisv.APIKey)
// We are using the iamtoken field to hold the api key
iamtoken := pisv.APIKey
Comment thread
clnperez marked this conversation as resolved.
Outdated

s, err := ibmpisession.New(iamtoken, pisv.Region, false, defSessionTimeout, pisv.ID, pisv.Zone)
if err != nil {
return nil, err
}

return s, err

}

func getPISessionVarsFromAuthFile(pisv *PISessionVars) error {

authFilePath := defaultAuthFilePath
if f := os.Getenv("POWERVS_AUTH_FILEPATH"); len(f) > 0 {
authFilePath = f
}

// Check if AuthFile exists, return if it does not
if _, err := os.Stat(authFilePath); os.IsNotExist(err) {
return nil
}

content, err := ioutil.ReadFile(authFilePath)
if err != nil {
return err
}

err = json.Unmarshal(content, pisv)
if err != nil {
return err
}

return nil
}

func getPISessionVarsFromEnv(pisv *PISessionVars) error {

if len(pisv.ID) == 0 {
Comment thread
clnperez marked this conversation as resolved.
pisv.ID = os.Getenv("IBMID")
}

if len(pisv.APIKey) == 0 {
// APIKeyEnvVars is a list of environment variable names containing an IBM Cloud API key
var APIKeyEnvVars = []string{"IC_API_KEY", "IBMCLOUD_API_KEY", "BM_API_KEY", "BLUEMIX_API_KEY"}
pisv.APIKey = getEnv(APIKeyEnvVars)
}

if len(pisv.Region) == 0 {
var regionEnvVars = []string{"IBMCLOUD_REGION", "IC_REGION"}
pisv.Region = getEnv(regionEnvVars)
}

if len(pisv.Zone) == 0 {
var zoneEnvVars = []string{"IBMCLOUD_ZONE"}
pisv.Zone = getEnv(zoneEnvVars)
}

return nil
}

func getPISessionVarsFromUser(pisv *PISessionVars) error {
var err error

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

// APIKeyEnvVars is a list of environment variable names containing an IBM Cloud API key
var APIKeyEnvVars = []string{"IC_API_KEY", "IBMCLOUD_API_KEY", "BM_API_KEY", "BLUEMIX_API_KEY"}
apikey = getEnv(APIKeyEnvVars)
}

if len(apikey) == 0 {
if len(pisv.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",
Comment thread
clnperez marked this conversation as resolved.
Outdated
},
},
}, &apikey)
}, &pisv.APIKey)
if err != nil {
return nil, errors.New("Error saving the API Key")
return errors.New("Error saving the API Key")
}

}
os.Setenv("IC_API_KEY", apikey)

// this can also be pulled from ~/bluemix/config.json
var regionEnvVars = []string{"IBMCLOUD_REGION", "IC_REGION"}
region = getEnv(regionEnvVars)
if len(region) == 0 {
region, err = GetRegion()
if len(pisv.Region) == 0 {
pisv.Region, err = GetRegion()
if err != nil {
return nil, err
return err
}

}

var zoneEnvVars = []string{"IBMCLOUD_ZONE"}
zone = getEnv(zoneEnvVars)
if len(zone) == 0 {
zone, err = GetZone(region)
if len(pisv.Zone) == 0 {
pisv.Zone, err = GetZone(pisv.Region)
if err != nil {
return nil, err
return err
}
}

iamtoken = apikey
s, err := ibmpisession.New(iamtoken, region, false, defSessionTimeout, id, zone)
return nil
}

func savePISessionVars(pisv *PISessionVars) error {

authFilePath := defaultAuthFilePath
if f := os.Getenv("POWERVS_AUTH_FILEPATH"); len(f) > 0 {
authFilePath = f
}

jsonVars, err := json.Marshal(*pisv)
if err != nil {
return nil, err
return err
}

return s, err
err = os.MkdirAll(filepath.Dir(authFilePath), 0700)
if err != nil {
return err
}
logrus.Debug("Saving variables to ", authFilePath)
return ioutil.WriteFile(authFilePath, jsonVars, 0600)
Comment thread
clnperez marked this conversation as resolved.
}

func getEnv(envs []string) string {
Expand Down