github.com/jmrodri/operator-sdk@v0.5.0/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 // Version is the API version - e.g. v1alpha1 55 // Parsed from APIVersion 56 Version string 57 58 // Resource is the API Resource i.e plural(lowercased(Kind)) e.g appservices 59 Resource string 60 61 // LowerKind is lowercased(Kind) e.g appservice 62 LowerKind string 63 64 // TODO: allow user to specify list of short names for Resource e.g app, myapp 65 } 66 67 func NewResource(apiVersion, kind string) (*Resource, error) { 68 r := &Resource{ 69 APIVersion: apiVersion, 70 Kind: kind, 71 } 72 if err := r.Validate(); err != nil { 73 return nil, err 74 } 75 return r, nil 76 } 77 78 // Validate defaults and checks the Resource values to make sure they are valid. 79 func (r *Resource) Validate() error { 80 if len(r.APIVersion) == 0 { 81 return errors.New("api-version cannot be empty") 82 } 83 84 if err := r.checkAndSetKinds(); err != nil { 85 return err 86 } 87 if err := r.checkAndSetGroups(); err != nil { 88 return err 89 } 90 if err := r.checkAndSetVersion(); err != nil { 91 return err 92 } 93 94 rs := inflect.NewDefaultRuleset() 95 if len(r.Resource) == 0 { 96 r.Resource = rs.Pluralize(strings.ToLower(r.Kind)) 97 } 98 99 return nil 100 } 101 102 func (r *Resource) checkAndSetKinds() error { 103 if len(r.Kind) == 0 { 104 return errors.New("kind cannot be empty") 105 } 106 107 r.LowerKind = strings.ToLower(r.Kind) 108 109 if strings.Title(r.Kind) != r.Kind { 110 return fmt.Errorf("kind must begin with uppercase (was %v)", r.Kind) 111 } 112 if !ResourceKindRegexp.MatchString(r.Kind) { 113 return errors.New("kind should consist of lower and uppercase alphabetical characters") 114 } 115 return nil 116 } 117 118 func (r *Resource) checkAndSetGroups() error { 119 fg := strings.Split(r.APIVersion, "/") 120 if len(fg) < 2 || len(fg[0]) == 0 { 121 return errors.New("full group cannot be empty") 122 } 123 g := strings.Split(fg[0], ".") 124 if len(g) < 2 || len(g[0]) == 0 { 125 return errors.New("group cannot be empty") 126 } 127 r.FullGroup = fg[0] 128 r.Group = g[0] 129 130 if err := validation.IsDNS1123Subdomain(r.Group); err != nil { 131 return fmt.Errorf("group name is invalid: %v", err) 132 } 133 return nil 134 } 135 136 func (r *Resource) checkAndSetVersion() error { 137 api := strings.Split(r.APIVersion, "/") 138 if len(api) < 2 || len(api[1]) == 0 { 139 return errors.New("version cannot be empty") 140 } 141 r.Version = api[1] 142 143 if !ResourceVersionRegexp.MatchString(r.Version) { 144 return errors.New("version is not in the correct Kubernetes version format, ex. v1alpha1") 145 } 146 return nil 147 }