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

     1  package step
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"net/url"
     8  	"path/filepath"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/olli-ai/jx/v2/pkg/cmd/opts/step"
    13  
    14  	"github.com/olli-ai/jx/v2/pkg/cmd/helper"
    15  
    16  	"github.com/jenkins-x/jx-logging/pkg/log"
    17  	"github.com/olli-ai/jx/v2/pkg/auth"
    18  	"github.com/olli-ai/jx/v2/pkg/cloud/buckets"
    19  	"github.com/olli-ai/jx/v2/pkg/cmd/opts"
    20  	"github.com/olli-ai/jx/v2/pkg/cmd/templates"
    21  	"github.com/olli-ai/jx/v2/pkg/gits"
    22  	"github.com/olli-ai/jx/v2/pkg/util"
    23  	"github.com/pkg/errors"
    24  	"github.com/spf13/cobra"
    25  )
    26  
    27  // StepUnstashOptions contains the command line flags
    28  type StepUnstashOptions struct {
    29  	step.StepOptions
    30  
    31  	URL     string
    32  	OutDir  string
    33  	Timeout time.Duration
    34  }
    35  
    36  var (
    37  	stepUnstashLong = templates.LongDesc(`
    38  		This pipeline step unstashes the files in storage to a local file or the console
    39  ` + StorageSupportDescription + helper.SeeAlsoText("jx step stash", "jx edit storage"))
    40  
    41  	stepUnstashExample = templates.Examples(`
    42  		# unstash a file to the reports directory
    43  		jx step unstash --url s3://mybucket/tests/myOrg/myRepo/mybranch/3/junit.xml -o reports
    44  
    45  		# unstash the file to the from GCS to the console
    46  		jx step unstash -u gs://mybucket/foo/bar/output.log
    47  `)
    48  )
    49  
    50  // NewCmdStepUnstash creates the CLI command
    51  func NewCmdStepUnstash(commonOpts *opts.CommonOptions) *cobra.Command {
    52  	options := StepUnstashOptions{
    53  		StepOptions: step.StepOptions{
    54  			CommonOptions: commonOpts,
    55  		},
    56  	}
    57  	cmd := &cobra.Command{
    58  		Use:     "unstash",
    59  		Short:   "Unstashes files generated as part of a pipeline to a local file or directory or displays on the console",
    60  		Aliases: []string{"collect"},
    61  		Long:    stepUnstashLong,
    62  		Example: stepUnstashExample,
    63  		Run: func(cmd *cobra.Command, args []string) {
    64  			options.Cmd = cmd
    65  			options.Args = args
    66  			err := options.Run()
    67  			helper.CheckErr(err)
    68  		},
    69  	}
    70  	cmd.Flags().StringVarP(&options.URL, "url", "u", "", "The fully qualified URL to the file to unstash including the storage host, path and file name")
    71  	cmd.Flags().StringVarP(&options.OutDir, "output", "o", "", "The output file or directory")
    72  	cmd.Flags().DurationVarP(&options.Timeout, "timeout", "t", time.Second*30, "The timeout period before we should fail unstashing the entry")
    73  	return cmd
    74  }
    75  
    76  // Run runs the command
    77  func (o *StepUnstashOptions) Run() error {
    78  	authSvc, err := o.GitAuthConfigService()
    79  	if err != nil {
    80  		return err
    81  	}
    82  	return Unstash(o.URL, o.OutDir, o.Timeout, authSvc)
    83  }
    84  
    85  func Unstash(u string, outDir string, timeout time.Duration, authSvc auth.ConfigService) error {
    86  	if u == "" {
    87  		// TODO lets guess from the project etc...
    88  		return util.MissingOption("url")
    89  	}
    90  	file := outDir
    91  	if file != "" {
    92  		isDir, err := util.DirExists(file)
    93  		if err != nil {
    94  			return errors.Wrapf(err, "failed to check if %s is a directory", file)
    95  		}
    96  		if isDir {
    97  			u2, err := url.Parse(u)
    98  			if err != nil {
    99  				return errors.Wrapf(err, "failed to parse URL %s", u)
   100  			}
   101  			name := u2.Path
   102  			if name == "" || strings.HasSuffix(name, "/") {
   103  				name += "output.txt"
   104  			}
   105  			file = filepath.Join(file, name)
   106  		}
   107  	}
   108  
   109  	reader, err := buckets.ReadURL(u, timeout, CreateBucketHTTPFn(authSvc))
   110  	if err != nil {
   111  		return err
   112  	}
   113  	defer reader.Close()
   114  	data, err := ioutil.ReadAll(reader)
   115  	if err != nil {
   116  		return err
   117  	}
   118  	if file == "" {
   119  		log.Logger().Infof("%s", string(data))
   120  		return nil
   121  	}
   122  	err = ioutil.WriteFile(file, data, util.DefaultWritePermissions)
   123  	if err != nil {
   124  		return errors.Wrapf(err, "failed to write file %s", file)
   125  	}
   126  	log.Logger().Infof("wrote: %s", util.ColorInfo(file))
   127  	return nil
   128  }
   129  
   130  // CreateBucketHTTPFn creates a function to transform a git URL to add the token and possible header function for accessing a git based bucket
   131  func CreateBucketHTTPFn(authSvc auth.ConfigService) func(string) (string, func(*http.Request), error) {
   132  	return func(urlText string) (string, func(*http.Request), error) {
   133  		headerFunc := func(*http.Request) {
   134  			return
   135  		}
   136  		if authSvc != nil {
   137  			gitInfo, err := gits.ParseGitURL(urlText)
   138  			if err != nil {
   139  				log.Logger().Warnf("Could not find the git token to access urlText %s due to: %s", urlText, err)
   140  			}
   141  			gitServerURL := gitInfo.HostURL()
   142  			gitKind := ""
   143  			gitServer := authSvc.Config().GetServer(gitServerURL)
   144  			if gitServer != nil {
   145  				gitKind = gitServer.Kind
   146  			}
   147  			tokenPrefix := ""
   148  			auths := authSvc.Config().FindUserAuths(gitServerURL)
   149  			for _, a := range auths {
   150  				if a.ApiToken != "" {
   151  					if gitKind == gits.KindBitBucketServer {
   152  						tokenPrefix = fmt.Sprintf("%s:%s", a.Username, a.ApiToken)
   153  					} else if gitKind == gits.KindGitlab {
   154  						headerFunc = func(r *http.Request) {
   155  							r.Header.Set("PRIVATE-TOKEN", a.ApiToken)
   156  						}
   157  					} else if gitKind == gits.KindGitHub && !gitInfo.IsGitHub() {
   158  						// If we're on GitHub Enterprise, we need to put the token as a parameter to the URL.
   159  						tokenPrefix = a.ApiToken
   160  					} else {
   161  						tokenPrefix = a.ApiToken
   162  					}
   163  					break
   164  				}
   165  			}
   166  			if gitServerURL == "https://raw.githubusercontent.com" {
   167  				auths := authSvc.Config().FindUserAuths(gits.GitHubURL)
   168  				for _, a := range auths {
   169  					if a.ApiToken != "" {
   170  						tokenPrefix = a.ApiToken
   171  						break
   172  					}
   173  				}
   174  			}
   175  			if tokenPrefix != "" {
   176  				idx := strings.Index(urlText, "://")
   177  				if idx > 0 {
   178  					idx += 3
   179  					urlText = urlText[0:idx] + tokenPrefix + "@" + urlText[idx:]
   180  				}
   181  			}
   182  		}
   183  		return urlText, headerFunc, nil
   184  	}
   185  }