diff --git a/cmd/Gopkg.lock b/cmd/Gopkg.lock index 0c6ab5cf4b..b3f48e3132 100644 --- a/cmd/Gopkg.lock +++ b/cmd/Gopkg.lock @@ -256,7 +256,7 @@ revision = "fdcf9f9480fdd5bf2b3c3df9bf4ecd22b25b87e2" [[projects]] - digest = "1:30621a2b23b5cfc672d3130350a9aa5449724d47899705b16e33ec994f806c4c" + digest = "1:384789cb768e13fec37c3a15613e21cde8d32ff945d8d4ec4bcef87a2ee41ff4" name = "sigs.k8s.io/controller-tools" packages = [ "pkg/scaffold", @@ -268,8 +268,8 @@ "pkg/util", ] pruneopts = "NT" - revision = "a92b5ff3ebb5767eb3d8344bd097a95863e52c3f" - version = "v0.1.2" + revision = "bfd3eefb7f22ed714b2acd38730c36992ee6af70" + version = "v0.1.5" [solve-meta] analyzer-name = "dep" diff --git a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/crd/generator/testData/pkg/apis/fun/v1alpha1/toy_types.go b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/crd/generator/testData/pkg/apis/fun/v1alpha1/toy_types.go index 29e41cfa0e..d63caf5b42 100644 --- a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/crd/generator/testData/pkg/apis/fun/v1alpha1/toy_types.go +++ b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/crd/generator/testData/pkg/apis/fun/v1alpha1/toy_types.go @@ -49,12 +49,15 @@ type ToySpec struct { Template v1.PodTemplateSpec `json:"template"` Claim v1.PersistentVolumeClaim `json:"claim,omitempty"` + + Replicas *int32 `json:"replicas"` } // ToyStatus defines the observed state of Toy type ToyStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file + Replicas int32 `json:"replicas"` } // +genclient @@ -62,6 +65,8 @@ type ToyStatus struct { // Toy is the Schema for the toys API // +k8s:openapi-gen=true +// +kubebuilder:subresource:status +// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath= type Toy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/crd.go b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/crd.go index 7da964d85a..81ea25765a 100644 --- a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/crd.go +++ b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/crd.go @@ -26,7 +26,6 @@ import ( "strings" "text/template" - "github.com/pkg/errors" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" @@ -41,8 +40,12 @@ func (b *APIs) parseCRDs() { for _, resource := range version.Resources { if IsAPIResource(resource.Type) { resource.JSONSchemaProps, resource.Validation = - b.typeToJSONSchemaProps(resource.Type, sets.NewString(), []string{}) + b.typeToJSONSchemaProps(resource.Type, sets.NewString(), []string{}, true) + // Note: Drop the Type field at the root level of validation + // schema. Refer to following issue for details. + // https://github.com/kubernetes/kubernetes/issues/65293 + resource.JSONSchemaProps.Type = "" j, err := json.MarshalIndent(resource.JSONSchemaProps, "", " ") if err != nil { log.Fatalf("Could not Marshall validation %v\n", err) @@ -76,19 +79,39 @@ func (b *APIs) parseCRDs() { resource.CRD.Spec.Scope = "Namespaced" } - if HasCategories(resource.Type) { + if hasCategories(resource.Type) { categoriesTag := getCategoriesTag(resource.Type) categories := strings.Split(categoriesTag, ",") resource.CRD.Spec.Names.Categories = categories resource.Categories = categories } - if HasStatusSubresource(resource.Type) { - subresources := &v1beta1.CustomResourceSubresources{ - Status: &v1beta1.CustomResourceSubresourceStatus{}, + if hasStatusSubresource(resource.Type) { + if resource.CRD.Spec.Subresources == nil { + resource.CRD.Spec.Subresources = &v1beta1.CustomResourceSubresources{} + } + resource.CRD.Spec.Subresources.Status = &v1beta1.CustomResourceSubresourceStatus{} + } + + resource.CRD.Status.Conditions = []v1beta1.CustomResourceDefinitionCondition{} + resource.CRD.Status.StoredVersions = []string{} + + if hasScaleSubresource(resource.Type) { + if resource.CRD.Spec.Subresources == nil { + resource.CRD.Spec.Subresources = &v1beta1.CustomResourceSubresources{} + } + jsonPath, err := parseScaleParams(resource.Type) + if err != nil { + log.Fatalf("failed in parsing CRD, error: %v", err.Error()) + } + resource.CRD.Spec.Subresources.Scale = &v1beta1.CustomResourceSubresourceScale{ + SpecReplicasPath: jsonPath[specReplicasPath], + StatusReplicasPath: jsonPath[statusReplicasPath], + } + labelSelctor, ok := jsonPath[labelSelectorPath] + if ok && labelSelctor != "" { + resource.CRD.Spec.Subresources.Scale.LabelSelectorPath = &labelSelctor } - resource.CRD.Spec.Subresources = subresources - resource.HasStatusSubresource = true } if len(resource.ShortName) > 0 { @@ -115,7 +138,7 @@ func (b *APIs) getMeta() string { // typeToJSONSchemaProps returns a JSONSchemaProps object and its serialization // in Go that describe the JSONSchema validations for the given type. -func (b *APIs) typeToJSONSchemaProps(t *types.Type, found sets.String, comments []string) (v1beta1.JSONSchemaProps, string) { +func (b *APIs) typeToJSONSchemaProps(t *types.Type, found sets.String, comments []string, isRoot bool) (v1beta1.JSONSchemaProps, string) { // Special cases time := types.Name{Name: "Time", Package: "k8s.io/apimachinery/pkg/apis/meta/v1"} meta := types.Name{Name: "ObjectMeta", Package: "k8s.io/apimachinery/pkg/apis/meta/v1"} @@ -137,7 +160,7 @@ func (b *APIs) typeToJSONSchemaProps(t *types.Type, found sets.String, comments case types.Builtin: v, s = b.parsePrimitiveValidation(t, found, comments) case types.Struct: - v, s = b.parseObjectValidation(t, found, comments) + v, s = b.parseObjectValidation(t, found, comments, isRoot) case types.Map: v, s = b.parseMapValidation(t, found, comments) case types.Slice: @@ -145,9 +168,9 @@ func (b *APIs) typeToJSONSchemaProps(t *types.Type, found sets.String, comments case types.Array: v, s = b.parseArrayValidation(t, found, comments) case types.Pointer: - v, s = b.typeToJSONSchemaProps(t.Elem, found, comments) + v, s = b.typeToJSONSchemaProps(t.Elem, found, comments, false) case types.Alias: - v, s = b.typeToJSONSchemaProps(t.Underlying, found, comments) + v, s = b.typeToJSONSchemaProps(t.Underlying, found, comments, false) default: log.Fatalf("Unknown supported Kind %v\n", t.Kind) } @@ -257,7 +280,7 @@ var mapTemplate = template.Must(template.New("map-template").Parse( // parseMapValidation returns a JSONSchemaProps object and its serialization in // Go that describe the validations for the given map type. func (b *APIs) parseMapValidation(t *types.Type, found sets.String, comments []string) (v1beta1.JSONSchemaProps, string) { - additionalProps, result := b.typeToJSONSchemaProps(t.Elem, found, comments) + additionalProps, result := b.typeToJSONSchemaProps(t.Elem, found, comments, false) props := v1beta1.JSONSchemaProps{ Type: "object", } @@ -305,7 +328,7 @@ type arrayTemplateArgs struct { // parseArrayValidation returns a JSONSchemaProps object and its serialization in // Go that describe the validations for the given array type. func (b *APIs) parseArrayValidation(t *types.Type, found sets.String, comments []string) (v1beta1.JSONSchemaProps, string) { - items, result := b.typeToJSONSchemaProps(t.Elem, found, comments) + items, result := b.typeToJSONSchemaProps(t.Elem, found, comments, false) props := v1beta1.JSONSchemaProps{ Type: "array", Items: &v1beta1.JSONSchemaPropsOrArray{Schema: &items}, @@ -331,11 +354,14 @@ type objectTemplateArgs struct { v1beta1.JSONSchemaProps Fields map[string]string Required []string + IsRoot bool } var objectTemplate = template.Must(template.New("object-template").Parse( `v1beta1.JSONSchemaProps{ + {{ if not .IsRoot -}} Type: "object", + {{ end -}} Properties: map[string]v1beta1.JSONSchemaProps{ {{ range $k, $v := .Fields -}} "{{ $k }}": {{ $v }}, @@ -350,14 +376,14 @@ var objectTemplate = template.Must(template.New("object-template").Parse( // parseObjectValidation returns a JSONSchemaProps object and its serialization in // Go that describe the validations for the given object type. -func (b *APIs) parseObjectValidation(t *types.Type, found sets.String, comments []string) (v1beta1.JSONSchemaProps, string) { +func (b *APIs) parseObjectValidation(t *types.Type, found sets.String, comments []string, isRoot bool) (v1beta1.JSONSchemaProps, string) { buff := &bytes.Buffer{} props := v1beta1.JSONSchemaProps{ Type: "object", } if strings.HasPrefix(t.Name.String(), "k8s.io/api") { - if err := objectTemplate.Execute(buff, objectTemplateArgs{props, nil, nil}); err != nil { + if err := objectTemplate.Execute(buff, objectTemplateArgs{props, nil, nil, false}); err != nil { log.Fatalf("%v", err) } } else { @@ -370,7 +396,7 @@ func (b *APIs) parseObjectValidation(t *types.Type, found sets.String, comments getValidation(l, &props) } - if err := objectTemplate.Execute(buff, objectTemplateArgs{props, result, required}); err != nil { + if err := objectTemplate.Execute(buff, objectTemplateArgs{props, result, required, isRoot}); err != nil { log.Fatalf("%v", err) } } @@ -532,7 +558,7 @@ func (b *APIs) getMembers(t *types.Type, found sets.String) (map[string]v1beta1. } required = append(required, re...) } else { - m, r := b.typeToJSONSchemaProps(member.Type, found, member.CommentLines) + m, r := b.typeToJSONSchemaProps(member.Type, found, member.CommentLines, false) members[name] = m result[name] = r if !strings.HasSuffix(strat, "omitempty") { @@ -544,13 +570,3 @@ func (b *APIs) getMembers(t *types.Type, found sets.String) (map[string]v1beta1. defer found.Delete(t.Name.String()) return members, result, required } - -// getCategoriesTag returns the value of the +kubebuilder:categories tags -func getCategoriesTag(c *types.Type) string { - comments := Comments(c.CommentLines) - resource := comments.getTag("kubebuilder:categories", "=") - if len(resource) == 0 { - panic(errors.Errorf("Must specify +kubebuilder:categories comment for type %v", c.Name)) - } - return resource -} diff --git a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/util.go b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/util.go index 4e285e2133..5eea079f3c 100644 --- a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/util.go +++ b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/util.go @@ -29,6 +29,13 @@ import ( "k8s.io/gengo/types" ) +const ( + specReplicasPath = "specpath" + statusReplicasPath = "statuspath" + labelSelectorPath = "selectorpath" + jsonPathError = "invalid scale path. specpath, statuspath key-value pairs are required, only selectorpath key-value is optinal. For example: // +kubebuilder:subresource:scale:specpath=.spec.replica,statuspath=.status.replica,selectorpath=.spec.Label" +) + // Options contains the parser options type Options struct { SkipMapValidation bool @@ -130,16 +137,16 @@ func HasSubresource(t *types.Type) bool { return false } for _, c := range t.CommentLines { - if strings.Contains(c, "+subresource") { + if strings.Contains(c, "subresource") { return true } } return false } -// HasStatusSubresource returns true if t is an APIResource annotated with -// +kubebuilder:subresource:status. -func HasStatusSubresource(t *types.Type) bool { +// hasStatusSubresource returns true if t is an APIResource annotated with +// +kubebuilder:subresource:status +func hasStatusSubresource(t *types.Type) bool { if !IsAPIResource(t) { return false } @@ -151,9 +158,23 @@ func HasStatusSubresource(t *types.Type) bool { return false } -// HasCategories returns true if t is an APIResource annotated with -// +kubebuilder:categories. -func HasCategories(t *types.Type) bool { +// hasScaleSubresource returns true if t is an APIResource annotated with +// +kubebuilder:subresource:scale +func hasScaleSubresource(t *types.Type) bool { + if !IsAPIResource(t) { + return false + } + for _, c := range t.CommentLines { + if strings.Contains(c, "+kubebuilder:subresource:scale") { + return true + } + } + return false +} + +// hasCategories returns true if t is an APIResource annotated with +// +kubebuilder:categories +func hasCategories(t *types.Type) bool { if !IsAPIResource(t) { return false } @@ -264,6 +285,16 @@ func (c Comments) getTags(name, sep string) []string { return tags } +// getCategoriesTag returns the value of the +kubebuilder:categories tags +func getCategoriesTag(c *types.Type) string { + comments := Comments(c.CommentLines) + resource := comments.getTag("kubebuilder:categories", "=") + if len(resource) == 0 { + panic(errors.Errorf("Must specify +kubebuilder:categories comment for type %v", c.Name)) + } + return resource +} + // getDocAnnotation parse annotations of "+kubebuilder:doc:" with tags of "warning" or "doc" for control generating doc config. // E.g. +kubebuilder:doc:warning=foo +kubebuilder:doc:note=bar func getDocAnnotation(t *types.Type, tags ...string) map[string]string { @@ -326,3 +357,41 @@ func checkType(props *v1beta1.JSONSchemaProps, s string, enums *[]v1beta1.JSON) *enums = append(*enums, v1beta1.JSON{Raw: []byte(`"` + s + `"`)}) } } + +// Scale subresource requires specpath, statuspath, selectorpath key values, represents for JSONPath of +// SpecReplicasPath, StatusReplicasPath, LabelSelectorPath separately. e.g. +// +kubebuilder:subresource:scale:specpath=.spec.replica,statuspath=.status.replica,selectorpath= +func parseScaleParams(t *types.Type) (map[string]string, error) { + jsonPath := make(map[string]string) + for _, c := range t.CommentLines { + if strings.Contains(c, "+kubebuilder:subresource:scale") { + paths := strings.Replace(c, "+kubebuilder:subresource:scale:", "", -1) + path := strings.Split(paths, ",") + if len(path) < 2 { + return nil, fmt.Errorf(jsonPathError) + } + for _, s := range path { + fmt.Printf("\n[debug] %s", s) + } + for _, s := range path { + kv := strings.Split(s, "=") + if kv[0] == specReplicasPath || kv[0] == statusReplicasPath || kv[0] == labelSelectorPath { + jsonPath[kv[0]] = kv[1] + } else { + return nil, fmt.Errorf(jsonPathError) + } + } + var ok bool + _, ok = jsonPath[specReplicasPath] + if !ok { + return nil, fmt.Errorf(jsonPathError) + } + _, ok = jsonPath[statusReplicasPath] + if !ok { + return nil, fmt.Errorf(jsonPathError) + } + return jsonPath, nil + } + } + return nil, fmt.Errorf(jsonPathError) +} diff --git a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/types.go b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/types.go index cc136083a0..ebfaf620d0 100644 --- a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/types.go +++ b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/types.go @@ -171,8 +171,6 @@ type APIResource struct { ValidationComments string // DocAnnotation is a map of annotations by name for doc. e.g. warning, notes message DocAnnotation map[string]string - // HasStatusSubresource indicates that the resource has a status subresource - HasStatusSubresource bool // Categories is a list of categories the resource is part of. Categories []string } diff --git a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/project/gitignore.go b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/project/gitignore.go index 4e86fc0770..a5bc8f8d7c 100644 --- a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/project/gitignore.go +++ b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/project/gitignore.go @@ -43,6 +43,7 @@ var gitignoreTemplate = ` *.dll *.so *.dylib +bin # Test binary, build with ` + "`go test -c`" + ` *.test diff --git a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/project/kustomize.go b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/project/kustomize.go index 4ffbba5c26..fd39062190 100644 --- a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/project/kustomize.go +++ b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/project/kustomize.go @@ -71,8 +71,9 @@ namePrefix: {{.Prefix}}- # YAML string, with resources separated by document # markers ("---"). resources: -- ../rbac/*.yaml -- ../manager/*.yaml +- ../rbac/rbac_role.yaml +- ../rbac/rbac_role_binding.yaml +- ../manager/manager.yaml patches: - manager_image_patch.yaml diff --git a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/resource/typestest.go b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/resource/typestest.go index fafa9b733c..2471963c61 100644 --- a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/resource/typestest.go +++ b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/resource/typestest.go @@ -64,8 +64,19 @@ import ( ) func TestStorage{{ .Resource.Kind }}(t *testing.T) { - key := types.NamespacedName{Name: "foo", Namespace: "default"} - created := &{{ .Resource.Kind }}{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}} + key := types.NamespacedName{ + Name: "foo", + {{ if .Resource.Namespaced -}} + Namespace: "default", + {{ end -}} + } + created := &{{ .Resource.Kind }}{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + {{ if .Resource.Namespaced -}} + Namespace: "default", + {{ end -}} + }} g := gomega.NewGomegaWithT(t) // Test Create diff --git a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/scaffold.go b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/scaffold.go index 08850a5b69..7f7181d39d 100644 --- a/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/scaffold.go +++ b/cmd/vendor/sigs.k8s.io/controller-tools/pkg/scaffold/scaffold.go @@ -86,7 +86,7 @@ func (s *Scaffold) setFieldsAndValidate(t input.File) error { // GetProject reads the project file and deserializes it into a Project func getProject(path string) (input.ProjectFile, error) { - in, err := ioutil.ReadFile(path) + in, err := ioutil.ReadFile(path) // nolint: gosec if err != nil { return input.ProjectFile{}, err } @@ -100,7 +100,7 @@ func getProject(path string) (input.ProjectFile, error) { // GetBoilerplate reads the boilerplate file func getBoilerplate(path string) (string, error) { - b, err := ioutil.ReadFile(path) + b, err := ioutil.ReadFile(path) // nolint: gosec return string(b), err }