Skip to content

Commit

Permalink
ref(credentials.go): split credential fetch/read from listing
Browse files Browse the repository at this point in the history
  • Loading branch information
vdice committed Jun 7, 2019
1 parent fd238ce commit 0e348ef
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 54 deletions.
129 changes: 80 additions & 49 deletions pkg/porter/credentials.go
Expand Up @@ -15,7 +15,6 @@ import (

dtprinter "github.com/carolynvs/datetime-printer"
credentials "github.com/deislabs/cnab-go/credentials"

tablewriter "github.com/olekukonko/tablewriter"
"github.com/pkg/errors"
yaml "gopkg.in/yaml.v2"
Expand Down Expand Up @@ -47,7 +46,20 @@ func (l CredentialsFileList) Less(i, j int) bool {
return l[i].Modified.Before(l[j].Modified)
}

// fetchCredentials fetches all credentials from the designated credentials dir
// fetchCredential returns a *credentials.CredentialsSet according to the supplied
// credential name, or an error if encountered
func (p *Porter) fetchCredential(name string) (*credentials.CredentialSet, error) {
credsDir, err := p.Config.GetCredentialsDir()
if err != nil {
return nil, errors.Wrap(err, "unable to determine credentials directory")
}

path := filepath.Join(credsDir, fmt.Sprintf("%s.yaml", name))
return p.readCredential(name, path)
}

// fetchCredentials fetches all credentials in the form of a CredentialsFileList
// from the designated credentials dir, or an error if encountered
func (p *Porter) fetchCredentials() (*CredentialsFileList, error) {
credsDir, err := p.Config.GetCredentialsDir()
if err != nil {
Expand All @@ -58,18 +70,10 @@ func (p *Porter) fetchCredentials() (*CredentialsFileList, error) {
if ok, _ := p.Context.FileSystem.DirExists(credsDir); ok {
p.Context.FileSystem.Walk(credsDir, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
credSet := &credentials.CredentialSet{}
data, err := p.Context.FileSystem.ReadFile(path)
credName := strings.Split(info.Name(), ".")[0]
credSet, err := p.readCredential(credName, path)
if err != nil {
if p.Debug {
fmt.Fprintf(p.Err, "unable to load credential set from %s: %s\n", path, err)
}
return nil
}
if err = yaml.Unmarshal(data, credSet); err != nil {
if p.Debug {
fmt.Fprintf(p.Err, "unable to unmarshal credential set from file %s: %s\n", info.Name(), err)
}
// If an error is encountered while reading, just return and move on to the next
return nil
}
credentialsFiles = append(credentialsFiles,
Expand All @@ -82,6 +86,31 @@ func (p *Porter) fetchCredentials() (*CredentialsFileList, error) {
return &credentialsFiles, nil
}

// readCredential reads a credential with the given name via the provided path
// and returns a CredentialSet or an error, if encountered
func (p *Porter) readCredential(name, path string) (*credentials.CredentialSet, error) {
credSet := &credentials.CredentialSet{}

data, err := p.Context.FileSystem.ReadFile(path)
if err != nil {
readErr := errors.Wrapf(err, "unable to load credential %s", name)
if p.Debug {
fmt.Fprint(p.Err, readErr.Error())
}
return credSet, readErr
}

if err = yaml.Unmarshal(data, credSet); err != nil {
unmarshalErr := errors.Wrapf(err, "unable to unmarshal credential %s", name)
if p.Debug {
fmt.Fprint(p.Err, unmarshalErr.Error())
}
return credSet, unmarshalErr
}

return credSet, nil
}

// ListCredentials lists credentials using the provided printer.PrintOptions
func (p *Porter) ListCredentials(opts printer.PrintOptions) error {
credentialsFiles, err := p.fetchCredentials()
Expand Down Expand Up @@ -239,19 +268,9 @@ func (o *CredentialShowOptions) Validate(args []string) error {
// ShowCredential shows the credential set corresponding to the provided name, using
// the provided printer.PrintOptions for display.
func (p *Porter) ShowCredential(opts CredentialShowOptions) error {
credsDir, err := p.Config.GetCredentialsDir()
if err != nil {
return errors.Wrap(err, "unable to determine credentials directory")
}

credSet := &credentials.CredentialSet{}
data, err := p.Context.FileSystem.ReadFile(filepath.Join(credsDir, fmt.Sprintf("%s.yaml", opts.Name)))
credSet, err := p.fetchCredential(opts.Name)
if err != nil {
return errors.Wrapf(err, "unable to load credential set %s", opts.Name)
}

if err = yaml.Unmarshal(data, credSet); err != nil {
return errors.Wrapf(err, "unable to unmarshal credential set %s", opts.Name)
return err
}

switch opts.Format {
Expand All @@ -260,30 +279,15 @@ func (p *Porter) ShowCredential(opts CredentialShowOptions) error {
case printer.FormatYaml:
return printer.PrintYaml(p.Out, credSet)
case printer.FormatTable:
var data [][]string
// Here we use an instance of olekukonko/tablewriter as our table,
// rather than using the printer pkg variant, as we wish to decorate
// the table a bit differently from the default
var rows [][]string

// Iterate through all CredentialStrategies to build up our data set
// Iterate through all CredentialStrategies and add to rows
for _, cs := range credSet.Credentials {
// Build a reflected Source of type reflectedStruct, for use below
reflectedSource := reflectedStruct{
Value: reflect.ValueOf(cs.Source),
Type: reflect.TypeOf(cs.Source),
}

// Determine the source type ('Path', 'EnvVar', etc.) by seeing which
// reflected source's field corresponds to a non-empty reflected source value

// Iterate through all of the fields of a credentials.Source struct
for i := 0; i < reflectedSource.Type.NumField(); i++ {
// A Field name would be 'Path', 'EnvVar', etc.
fieldName := reflectedSource.Type.Field(i).Name
// Get the value for said Field
fieldValue := reflect.Indirect(reflectedSource.Value).FieldByName(fieldName).String()
// If not empty, this field value and name represent our source and source type, respectively
if fieldValue != "" {
data = append(data, []string{cs.Name, fieldValue, fieldName})
}
}
sourceVal, sourceType := GetCredentialSourceValueAndType(cs.Source)
rows = append(rows, []string{cs.Name, sourceVal, sourceType})
}

// Build and configure our tablewriter
Expand All @@ -300,8 +304,8 @@ func (p *Porter) ShowCredential(opts CredentialShowOptions) error {

// Now print the table
table.SetHeader([]string{"Name", "Local Source", "Source Type"})
for _, v := range data {
table.Append(v)
for _, row := range rows {
table.Append(row)
}
table.Render()
return nil
Expand All @@ -314,3 +318,30 @@ type reflectedStruct struct {
Value reflect.Value
Type reflect.Type
}

// GetCredentialSourceValueAndType takes a given credentials.Source struct and
// returns the source value itself as well as source type, e.g., 'Path', 'EnvVar', etc.,
// both in their string forms
func GetCredentialSourceValueAndType(cs credentials.Source) (string, string) {
var sourceVal, sourceType string

// Build a reflected credentials.Source struct
reflectedSource := reflectedStruct{
Value: reflect.ValueOf(cs),
Type: reflect.TypeOf(cs),
}

// Iterate through all of the fields of a credentials.Source struct
for i := 0; i < reflectedSource.Type.NumField(); i++ {
// A Field name would be 'Path', 'EnvVar', etc.
fieldName := reflectedSource.Type.Field(i).Name
// Get the value for said Field
fieldValue := reflect.Indirect(reflectedSource.Value).FieldByName(fieldName).String()
// If value non-empty, this field value and name represent our source value
// and source type, respectively
if fieldValue != "" {
sourceVal, sourceType = fieldValue, fieldName
}
}
return sourceVal, sourceType
}
60 changes: 55 additions & 5 deletions pkg/porter/credentials_test.go
Expand Up @@ -6,6 +6,7 @@ import (
cnabprovider "github.com/deislabs/porter/pkg/cnab/provider"
printer "github.com/deislabs/porter/pkg/printer"

credentials "github.com/deislabs/cnab-go/credentials"
"github.com/deislabs/duffle/pkg/bundle"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -202,7 +203,7 @@ func TestCredentialsList_BadCred(t *testing.T) {
name: "unmarshal error",
format: printer.FormatTable,
wantContains: []string{
"unable to unmarshal credential set from file bad-creds.yaml: yaml: unmarshal errors",
"unable to unmarshal credential bad-creds: yaml: unmarshal errors",
`NAME MODIFIED
good-creds now`},
errorMsg: "",
Expand Down Expand Up @@ -321,10 +322,7 @@ func TestShowCredential_NotFound(t *testing.T) {
err := p.ShowCredential(opts)
assert.Error(t, err, "an error should have occurred")
assert.EqualError(t, err,
"unable to load credential set non-existent-cred: open /root/.porter/credentials/non-existent-cred.yaml: file does not exist")

gotOutput := p.TestConfig.TestContext.GetOutput()
assert.Equal(t, "", gotOutput)
"unable to load credential non-existent-cred: open /root/.porter/credentials/non-existent-cred.yaml: file does not exist")
}

func TestShowCredential_Found(t *testing.T) {
Expand Down Expand Up @@ -422,3 +420,55 @@ credentials:
})
}
}

type SourceTest struct {
name string
source credentials.Source
wantValue string
wantType string
}

func TestGetCredentialSourceValueAndType(t *testing.T) {
testcases := []SourceTest{
{
name: "Source: EnvVar",
source: credentials.Source{
EnvVar: "ENVY",
},
wantValue: "ENVY",
wantType: "EnvVar",
},
{
name: "Source: Path",
source: credentials.Source{
Path: "/pathy/patheson",
},
wantValue: "/pathy/patheson",
wantType: "Path",
},
{
name: "Source: Command",
source: credentials.Source{
Command: "sed s/true/false/g",
},
wantValue: "sed s/true/false/g",
wantType: "Command",
},
{
name: "Source: Value",
source: credentials.Source{
Value: "abc123",
},
wantValue: "abc123",
wantType: "Value",
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
sv, st := GetCredentialSourceValueAndType(tc.source)
assert.Equal(t, tc.wantValue, sv)
assert.Equal(t, tc.wantType, st)
})
}
}

0 comments on commit 0e348ef

Please sign in to comment.