github.com/joelanford/operator-sdk@v0.8.2/internal/pkg/scaffold/resource.go (about) 1 // Copyright 2018 The Operator-SDK Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Modified from github.com/kubernetes-sigs/controller-tools/pkg/scaffold/resource/resource.go 16 17 package scaffold 18 19 import ( 20 "errors" 21 "fmt" 22 "regexp" 23 "strings" 24 25 "github.com/markbates/inflect" 26 27 "k8s.io/apimachinery/pkg/util/validation" 28 ) 29 30 var ( 31 // ResourceVersionRegexp matches Kubernetes API versions. 32 // See https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-versioning 33 ResourceVersionRegexp = regexp.MustCompile("^v[1-9][0-9]*((alpha|beta)[1-9][0-9]*)?$") 34 // ResourceKindRegexp matches Kubernetes API Kind's. 35 ResourceKindRegexp = regexp.MustCompile("^[A-Z]{1}[a-zA-Z0-9]+$") 36 ) 37 38 // Resource contains the information required to scaffold files for a resource. 39 type Resource struct { 40 // APIVersion is the complete group-subdomain/version e.g app.example.com/v1alpha1 41 APIVersion string 42 43 // Kind is the API Kind e.g AppService 44 Kind string 45 46 // FullGroup is the complete group name with subdomain e.g app.example.com 47 // Parsed from APIVersion 48 FullGroup string 49 50 // Group is the API Group. Does not contain the sub-domain. e.g app 51 // Parsed from APIVersion 52 Group string 53 54 // GoImportGroup is the non-hyphenated go import group for this resource 55 GoImportGroup string 56 57 // Version is the API version - e.g. v1alpha1 58 // Parsed from APIVersion 59 Version string 60 61 // Resource is the API Resource i.e plural(lowercased(Kind)) e.g appservices 62 Resource string 63 64 // LowerKind is lowercased(Kind) e.g appservice 65 LowerKind string 66 67 // TODO: allow user to specify list of short names for Resource e.g app, myapp 68 } 69 70 func NewResource(apiVersion, kind string) (*Resource, error) { 71 r := &Resource{ 72 APIVersion: apiVersion, 73 Kind: kind, 74 } 75 if err := r.Validate(); err != nil { 76 return nil, err 77 } 78 return r, nil 79 } 80 81 // Validate defaults and checks the Resource values to make sure they are valid. 82 func (r *Resource) Validate() error { 83 if len(r.APIVersion) == 0 { 84 return errors.New("api-version cannot be empty") 85 } 86 87 if err := r.checkAndSetKinds(); err != nil { 88 return err 89 } 90 if err := r.checkAndSetGroups(); err != nil { 91 return err 92 } 93 if err := r.checkAndSetVersion(); err != nil { 94 return err 95 } 96 97 rs := inflect.NewDefaultRuleset() 98 if len(r.Resource) == 0 { 99 r.Resource = rs.Pluralize(strings.ToLower(r.Kind)) 100 } 101 102 return nil 103 } 104 105 func (r *Resource) checkAndSetKinds() error { 106 if len(r.Kind) == 0 { 107 return errors.New("kind cannot be empty") 108 } 109 110 r.LowerKind = strings.ToLower(r.Kind) 111 112 if strings.Title(r.Kind) != r.Kind { 113 return fmt.Errorf("kind must begin with uppercase (was %v)", r.Kind) 114 } 115 if !ResourceKindRegexp.MatchString(r.Kind) { 116 return errors.New("kind should consist of lower and uppercase alphabetical characters") 117 } 118 return nil 119 } 120 121 func (r *Resource) checkAndSetGroups() error { 122 fg := strings.Split(r.APIVersion, "/") 123 if len(fg) < 2 || len(fg[0]) == 0 { 124 return errors.New("full group cannot be empty") 125 } 126 g := strings.Split(fg[0], ".") 127 if len(g) == 0 || len(g[0]) == 0 { 128 return errors.New("group cannot be empty") 129 } 130 r.FullGroup = fg[0] 131 r.Group = g[0] 132 133 s := strings.ToLower(r.Group) 134 r.GoImportGroup = strings.Replace(s, "-", "", -1) 135 136 if err := validation.IsDNS1123Subdomain(r.Group); err != nil { 137 return fmt.Errorf("group name is invalid: %v", err) 138 } 139 return nil 140 } 141 142 func (r *Resource) checkAndSetVersion() error { 143 api := strings.Split(r.APIVersion, "/") 144 if len(api) < 2 || len(api[1]) == 0 { 145 return errors.New("version cannot be empty") 146 } 147 r.Version = api[1] 148 149 if !ResourceVersionRegexp.MatchString(r.Version) { 150 return errors.New("version is not in the correct Kubernetes version format, ex. v1alpha1") 151 } 152 return nil 153 }