github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/cmd/step/step_credential.go (about)

     1  package step
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"sort"
     7  
     8  	"github.com/olli-ai/jx/v2/pkg/cmd/opts/step"
     9  
    10  	"github.com/olli-ai/jx/v2/pkg/cmd/helper"
    11  
    12  	"github.com/jenkins-x/jx-logging/pkg/log"
    13  	"github.com/olli-ai/jx/v2/pkg/cmd/opts"
    14  	"github.com/olli-ai/jx/v2/pkg/cmd/templates"
    15  	"github.com/olli-ai/jx/v2/pkg/kube"
    16  	"github.com/olli-ai/jx/v2/pkg/util"
    17  	"github.com/pkg/errors"
    18  	"github.com/spf13/cobra"
    19  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    20  )
    21  
    22  // StepCredentialOptions contains the command line arguments for this command
    23  type StepCredentialOptions struct {
    24  	step.StepOptions
    25  
    26  	Namespace string
    27  	Secret    string
    28  	Key       string
    29  	File      string
    30  	Optional  bool
    31  }
    32  
    33  var (
    34  	stepCredentialLong = templates.LongDesc(`
    35  		Returns a credential from a Secret for easy scripting in pipeline steps.
    36  
    37  		Supports the [Jenkins Credentials Provider labels on the Secrets](https://jenkinsci.github.io/kubernetes-credentials-provider-plugin/examples/)
    38  
    39  		If you specify --optional then if the key or secret doesn't exist then the command will only print a warning and will not error.
    40  `)
    41  
    42  	stepCredentialExample = templates.Examples(`
    43  		# get the password of a secret 'foo' which uses the Jenkins Credentials Provider labels
    44  		export MY_PWD="$(jx step credential -s foo)"
    45  
    46  		# get the password entry of a secret 'foo' as an environment variable
    47  		export MY_PWD="$(jx step credential -s foo -k passwordj)"
    48  
    49  		# create a local file from a file based secret using the Jenkins Credentials Provider labels
    50          export MY_KEY_FILE="$(jx step credential -s foo)"
    51           
    52  		# create a local file called cheese from a given key
    53          export MY_KEY_FILE="$(jx step credential -s foo -f cheese -k data)"
    54  
    55  		# create a local file called cheese from a given key, if the key exists'
    56          export MY_KEY_FILE="$(jx step credential -s foo -f cheese -k data --optional)"
    57           
    58  `)
    59  )
    60  
    61  // NewCmdStepCredential creates the command
    62  func NewCmdStepCredential(commonOpts *opts.CommonOptions) *cobra.Command {
    63  	options := StepCredentialOptions{
    64  		StepOptions: step.StepOptions{
    65  			CommonOptions: commonOpts,
    66  		},
    67  	}
    68  	cmd := &cobra.Command{
    69  		Use:     "credential",
    70  		Short:   "Returns a secret entry for easy scripting in pipeline steps",
    71  		Long:    stepCredentialLong,
    72  		Example: stepCredentialExample,
    73  		Aliases: []string{"secret", "cred"},
    74  		Run: func(cmd *cobra.Command, args []string) {
    75  			options.Cmd = cmd
    76  			options.Args = args
    77  			err := options.Run()
    78  			helper.CheckErr(err)
    79  		},
    80  	}
    81  
    82  	cmd.Flags().StringVarP(&options.Namespace, "namespace", "n", "", "the namespace to look for a Secret")
    83  	cmd.Flags().StringVarP(&options.Secret, "name", "s", "", "the name of the Secret")
    84  	cmd.Flags().StringVarP(&options.Key, "key", "k", "", "the key in the Secret to output")
    85  	cmd.Flags().StringVarP(&options.File, "file", "f", "", "the key for the filename to use if this is a file based Secret")
    86  	cmd.Flags().BoolVarP(&options.Optional, "optional", "", false, "if true, then the command will only warn (not error) if the secret or the key doesn't exist")
    87  
    88  	return cmd
    89  }
    90  
    91  // Run runs the command
    92  func (o *StepCredentialOptions) Run() error {
    93  	kubeClient, devNs, err := o.KubeClientAndDevNamespace()
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	ns := o.Namespace
    99  	if ns == "" {
   100  		ns = devNs
   101  	}
   102  
   103  	name := o.Secret
   104  	if name == "" {
   105  		return util.MissingOption("name")
   106  	}
   107  	secret, err := kubeClient.CoreV1().Secrets(ns).Get(name, metav1.GetOptions{})
   108  	if err != nil {
   109  		if o.Optional {
   110  			log.Logger().Warnf("failed to find Secret %s in namespace %s", name, ns)
   111  			return nil
   112  		}
   113  		return errors.Wrapf(err, "failed to find Secret %s in namespace %s", name, ns)
   114  	}
   115  	data := secret.Data
   116  	if data == nil {
   117  		if o.Optional {
   118  			log.Logger().Warnf("Secret %s in namespace %s has no data", name, ns)
   119  			return nil
   120  		}
   121  		return errors.Wrapf(err, "Secret %s in namespace %s has no data", name, ns)
   122  	}
   123  	keys := []string{}
   124  	for k := range data {
   125  		keys = append(keys, k)
   126  	}
   127  	sort.Strings(keys)
   128  
   129  	filename := o.File
   130  	key := o.Key
   131  
   132  	labels := secret.Labels
   133  	if labels != nil {
   134  		kind := labels[kube.LabelCredentialsType]
   135  		if filename == "" && kind == kube.ValueCredentialTypeSecretFile {
   136  			filenameData, ok := data["filename"]
   137  			if ok {
   138  				filename = string(filenameData)
   139  			} else {
   140  				return fmt.Errorf("the Secret %s in namespace %s has label %s with value %s but has no filename key", name, ns, kube.LabelCredentialsType, kind)
   141  			}
   142  			if key == "" {
   143  				key = "data"
   144  			}
   145  		}
   146  
   147  		if key == "" && kind == kube.ValueCredentialTypeUsernamePassword {
   148  			key = "password"
   149  		}
   150  	}
   151  
   152  	if key == "" {
   153  		return util.MissingOptionWithOptions("key", keys)
   154  	}
   155  
   156  	value, ok := data[key]
   157  	if !ok {
   158  		log.Logger().Warnf("Secret %s in namespace %s does not have key %s", name, ns, key)
   159  		if o.Optional {
   160  			return nil
   161  		}
   162  		return util.InvalidOption("key", key, keys)
   163  	}
   164  	if filename != "" {
   165  		err = ioutil.WriteFile(filename, value, util.DefaultWritePermissions)
   166  		if err != nil {
   167  			return errors.Wrapf(err, "failed to store file %s", filename)
   168  		}
   169  		fmt.Fprintf(o.Out, "%s\n", filename)
   170  		return nil
   171  	}
   172  	fmt.Fprintf(o.Out, "%s\n", value)
   173  	return nil
   174  }