From 0e348ef8e2aa7a66b0aa57f04d67f3e3ca4684a0 Mon Sep 17 00:00:00 2001 From: Vaughn Dice Date: Fri, 7 Jun 2019 17:05:07 -0600 Subject: [PATCH] ref(credentials.go): split credential fetch/read from listing --- pkg/porter/credentials.go | 129 ++++++++++++++++++++------------- pkg/porter/credentials_test.go | 60 +++++++++++++-- 2 files changed, 135 insertions(+), 54 deletions(-) diff --git a/pkg/porter/credentials.go b/pkg/porter/credentials.go index f97694f7ad..ea437a471e 100644 --- a/pkg/porter/credentials.go +++ b/pkg/porter/credentials.go @@ -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" @@ -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 { @@ -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, @@ -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() @@ -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 { @@ -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 @@ -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 @@ -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 +} diff --git a/pkg/porter/credentials_test.go b/pkg/porter/credentials_test.go index 8e02ead2cb..66bba40207 100644 --- a/pkg/porter/credentials_test.go +++ b/pkg/porter/credentials_test.go @@ -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" @@ -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: "", @@ -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) { @@ -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) + }) + } +}