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 }