Skip to content
Merged
Changes from all 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
208 changes: 158 additions & 50 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,208 @@ 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

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

// Frab variables from the users environment
logrus.Debug("Gathering variables from user environment")
err = getPISessionVarsFromEnv(&pisv)
if err != nil {
return nil, err
}

// 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
}

// 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
s, err := ibmpisession.New(pisv.APIKey, pisv.Region, false, defSessionTimeout, pisv.ID, pisv.Zone)
if err != nil {
return nil, err
}

return s, err

}

func getPISessionVarsFromAuthFile(pisv *PISessionVars) error {

if pisv == nil {
return errors.New("PISession Variable Object pointer cannot be nil")
}

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 pisv == nil {
return errors.New("PISession Variable Object pointer cannot be nil")
}

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 pisv == nil {
return errors.New("PISession Variable Object pointer cannot be nil")
}

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",
Help: "The API key installation.\nhttps://cloud.ibm.com/iam/apikeys",
},
},
}, &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