github.com/pachyderm/pachyderm@v1.13.4/src/client/pps/util.go (about)

     1  package pps
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  
     8  	"github.com/pachyderm/pachyderm/src/client/pfs"
     9  
    10  	"github.com/pachyderm/pachyderm/src/client/pkg/errors"
    11  	"gopkg.in/src-d/go-git.v4"
    12  )
    13  
    14  var (
    15  	// format strings for state name parsing errors
    16  	errInvalidJobStateName      string
    17  	errInvalidPipelineStateName string
    18  )
    19  
    20  func init() {
    21  	// construct error messages from all current job and pipeline state names
    22  	var states []string
    23  	for i := int32(0); JobState_name[i] != ""; i++ {
    24  		states = append(states, strings.ToLower(strings.TrimPrefix(JobState_name[i], "JOB_")))
    25  	}
    26  	errInvalidJobStateName = fmt.Sprintf("state %%s must be one of %s, or %s, etc", strings.Join(states, ", "), JobState_name[0])
    27  	states = states[:0]
    28  	for i := int32(0); PipelineState_name[i] != ""; i++ {
    29  		states = append(states, strings.ToLower(strings.TrimPrefix(PipelineState_name[i], "PIPELINE_")))
    30  	}
    31  	errInvalidPipelineStateName = fmt.Sprintf("state %%s must be one of %s, or %s, etc", strings.Join(states, ", "), PipelineState_name[0])
    32  }
    33  
    34  // VisitInput visits each input recursively in ascending order (root last)
    35  func VisitInput(input *Input, f func(*Input)) {
    36  	switch {
    37  	case input == nil:
    38  		return // Spouts may have nil input
    39  	case input.Cross != nil:
    40  		for _, input := range input.Cross {
    41  			VisitInput(input, f)
    42  		}
    43  	case input.Join != nil:
    44  		for _, input := range input.Join {
    45  			VisitInput(input, f)
    46  		}
    47  	case input.Group != nil:
    48  		for _, input := range input.Group {
    49  			VisitInput(input, f)
    50  		}
    51  	case input.Union != nil:
    52  		for _, input := range input.Union {
    53  			VisitInput(input, f)
    54  		}
    55  	}
    56  	f(input)
    57  }
    58  
    59  // InputName computes the name of an Input.
    60  func InputName(input *Input) string {
    61  	switch {
    62  	case input == nil:
    63  		return ""
    64  	case input.Pfs != nil:
    65  		return input.Pfs.Name
    66  	case input.Cross != nil:
    67  		if len(input.Cross) > 0 {
    68  			return InputName(input.Cross[0])
    69  		}
    70  	case input.Join != nil:
    71  		if len(input.Join) > 0 {
    72  			return InputName(input.Join[0])
    73  		}
    74  	case input.Group != nil:
    75  		if len(input.Group) > 0 {
    76  			return InputName(input.Group[0])
    77  		}
    78  	case input.Union != nil:
    79  		if len(input.Union) > 0 {
    80  			return InputName(input.Union[0])
    81  		}
    82  	}
    83  	return ""
    84  }
    85  
    86  // SortInput sorts an Input.
    87  func SortInput(input *Input) {
    88  	VisitInput(input, func(input *Input) {
    89  		SortInputs := func(inputs []*Input) {
    90  			sort.SliceStable(inputs, func(i, j int) bool { return InputName(inputs[i]) < InputName(inputs[j]) })
    91  		}
    92  		switch {
    93  		case input.Cross != nil:
    94  			SortInputs(input.Cross)
    95  		case input.Join != nil:
    96  			SortInputs(input.Join)
    97  		case input.Group != nil:
    98  			SortInputs(input.Group)
    99  		case input.Union != nil:
   100  			SortInputs(input.Union)
   101  		}
   102  	})
   103  }
   104  
   105  // InputBranches returns the branches in an Input.
   106  func InputBranches(input *Input) []*pfs.Branch {
   107  	var result []*pfs.Branch
   108  	VisitInput(input, func(input *Input) {
   109  		if input.Pfs != nil {
   110  			result = append(result, &pfs.Branch{
   111  				Repo: &pfs.Repo{Name: input.Pfs.Repo},
   112  				Name: input.Pfs.Branch,
   113  			})
   114  		}
   115  		if input.Cron != nil {
   116  			result = append(result, &pfs.Branch{
   117  				Repo: &pfs.Repo{Name: input.Cron.Repo},
   118  				Name: "master",
   119  			})
   120  		}
   121  		if input.Git != nil {
   122  			result = append(result, &pfs.Branch{
   123  				Repo: &pfs.Repo{Name: input.Git.Name},
   124  				Name: input.Git.Branch,
   125  			})
   126  		}
   127  	})
   128  	return result
   129  }
   130  
   131  // ValidateGitCloneURL returns an error if the provided URL is invalid
   132  func ValidateGitCloneURL(url string) error {
   133  	exampleURL := "https://github.com/org/foo.git"
   134  	if url == "" {
   135  		return errors.Errorf("clone URL is missing (example clone URL %v)", exampleURL)
   136  	}
   137  	// Use the git client's validator to make sure its a valid URL
   138  	o := &git.CloneOptions{
   139  		URL: url,
   140  	}
   141  	if err := o.Validate(); err != nil {
   142  		return err
   143  	}
   144  
   145  	// Make sure its the type that we want. Of the following we
   146  	// only accept the 'clone' type of url:
   147  	//     git_url: "git://github.com/sjezewski/testgithook.git",
   148  	//     ssh_url: "git@github.com:sjezewski/testgithook.git",
   149  	//     clone_url: "https://github.com/sjezewski/testgithook.git",
   150  	//     svn_url: "https://github.com/sjezewski/testgithook",
   151  
   152  	if !strings.HasSuffix(url, ".git") {
   153  		// svn_url case
   154  		return errors.Errorf("clone URL is missing .git suffix (example clone URL %v)", exampleURL)
   155  	}
   156  	if !strings.HasPrefix(url, "https://") {
   157  		// git_url or ssh_url cases
   158  		return errors.Errorf("clone URL must use https protocol (example clone URL %v)", exampleURL)
   159  	}
   160  
   161  	return nil
   162  }
   163  
   164  // JobStateFromName attempts to interpret a string as a JobState,
   165  // accepting either the enum names or the pretty printed state names
   166  func JobStateFromName(name string) (JobState, error) {
   167  	canonical := "JOB_" + strings.TrimPrefix(strings.ToUpper(name), "JOB_")
   168  	if value, ok := JobState_value[canonical]; ok {
   169  		return JobState(value), nil
   170  	}
   171  	return 0, fmt.Errorf(errInvalidJobStateName, name)
   172  }
   173  
   174  // PipelineStateFromName attempts to interpret a string as a PipelineState,
   175  // accepting either the enum names or the pretty printed state names
   176  func PipelineStateFromName(name string) (PipelineState, error) {
   177  	canonical := "PIPELINE_" + strings.TrimPrefix(strings.ToUpper(name), "PIPELINE_")
   178  	if value, ok := PipelineState_value[canonical]; ok {
   179  		return PipelineState(value), nil
   180  	}
   181  	return 0, fmt.Errorf(errInvalidPipelineStateName, name)
   182  }