Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal/scaffold/olm-catalog: populate CSV customresourcedefinitions from Go type annotations #1162

Merged
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
65236bd
pkg/scaffold/olm-catalog/*: generate CSV manifest {spec,status}Descri…
estroz Mar 1, 2019
869e49a
add comments and reorg code a bit
estroz Mar 1, 2019
1aea2a2
Merge branch 'master' into csv-customresourcedefinitions
estroz Mar 22, 2019
2560266
make CSV CRD updates if fields are not set
estroz Mar 22, 2019
84f85c9
pkg/scaffold/olm-catalog/*: suggest path and x-descriptors if not pre…
estroz Mar 25, 2019
4ca3ce1
Merge branch 'master' into csv-customresourcedefinitions
estroz Apr 8, 2019
267c6de
internal/pkg/scaffold/olm-catalog: use +operator-sdk:gen-csv parse an…
estroz Apr 13, 2019
2d3dc95
sort descriptors by display name; make parser more robust
estroz Apr 13, 2019
e1e5351
add TODO's
estroz Apr 13, 2019
db75858
internal/annotations: implement annotations splitting and joining
estroz Apr 15, 2019
e07364d
rework annotations and parser
estroz Apr 15, 2019
808a6b3
ignore non-existent API dirs
estroz Apr 16, 2019
ea9f7c0
fix vet issue
estroz Apr 16, 2019
7884094
revendor
estroz Apr 16, 2019
c3b3580
add tag parsing for resources and displayName
estroz Apr 17, 2019
2be5216
parse path directly from tags, no annotation override
estroz Apr 17, 2019
c4625d8
remove print statements, refactor initial type parsing, fix test CSV
estroz Apr 18, 2019
6326cc5
append x-descriptors found by path elements to annotation-specified x…
estroz Apr 18, 2019
5f50937
remove actionDescriptor parsing
estroz Apr 18, 2019
141a384
persist actionDescriptors and required CRD's in CSV's
estroz Apr 18, 2019
4119f15
cleanup and renaming
estroz Apr 18, 2019
c5e16db
get full hierarchal path instead of immediate parent+child
estroz Apr 19, 2019
c93b958
Merge branch 'master' into csv-customresourcedefinitions
estroz Apr 23, 2019
291855c
Merge branch 'master' into csv-customresourcedefinitions
estroz May 24, 2019
8cee281
bump maxLevel to 10; use 'spec'/'status' path segments if already see…
estroz May 24, 2019
e1bf6fe
add comments; refactor getSpecStatusPkgTypesForAPI to only do search …
estroz May 24, 2019
753829d
Merge branch 'master' into csv-customresourcedefinitions
estroz Jul 10, 2019
4de0e64
fix errors after merge
estroz Jul 10, 2019
9b8addd
add required CRD's back to CSV testdata; some CRD Apply() updates
estroz Jul 10, 2019
65c97b9
Merge branch 'master' into csv-customresourcedefinitions
estroz Jul 10, 2019
e854b16
Merge branch 'master' into csv-customresourcedefinitions
estroz Jul 16, 2019
f5a1809
some refactoring
estroz Jul 16, 2019
d9c7775
internal/pkg/scaffold/olm-catalog: error message updates and defer wr…
estroz Jul 17, 2019
a5bba17
some refactoring
estroz Jul 17, 2019
8750e06
move descriptor.go to internal/pkg/descriptor, split functions into t…
estroz Jul 17, 2019
1f37cfe
fix link
estroz Jul 17, 2019
55a64c0
Merge branch 'master' into csv-customresourcedefinitions
estroz Jul 17, 2019
4ae9847
Merge branch 'master' into csv-customresourcedefinitions
estroz Aug 9, 2019
93560be
move search functions into search.go
estroz Aug 9, 2019
3fc54a6
internal/pkg/scaffold/olm-catalog: sort both owned and required CRDDe…
estroz Aug 9, 2019
86fccfa
refactor functions
estroz Aug 21, 2019
70acfae
add parse and descriptor unit tests
estroz Sep 10, 2019
bfa2912
Merge branch 'master' into csv-customresourcedefinitions
estroz Sep 18, 2019
d4f0021
refactor descriptor retrieval from types
estroz Sep 18, 2019
8725662
doc/user/olm-catalog/csv-annotations.md: document csv-gen annotations
estroz Sep 19, 2019
0141a5f
add more unit tests and test data, fix errors caught by tests
estroz Sep 20, 2019
23f48fc
use embedded SpecDescriptor in descriptor
estroz Sep 20, 2019
9d806f3
Merge branch 'master' into csv-customresourcedefinitions
estroz Oct 23, 2019
20f4390
fix unit test
estroz Oct 28, 2019
d222743
change resource name for dummy types
estroz Oct 30, 2019
3cf1a3b
prevent generators from running on dummy types
estroz Oct 31, 2019
22fb0cc
Merge branch 'master' into csv-customresourcedefinitions
estroz Nov 11, 2019
c5578ab
Apply suggestions from code review
Nov 12, 2019
1fb896d
updates based on PR comments
estroz Nov 12, 2019
5214d50
respect inlined and omitted tags
estroz Nov 13, 2019
8f07a49
Merge branch 'master' into csv-customresourcedefinitions
estroz Nov 13, 2019
fb93d43
check if prefix is local dir; chdir in tests
estroz Nov 18, 2019
6e911ce
remove print statement
estroz Nov 20, 2019
db24904
Merge branch 'master' into csv-customresourcedefinitions
estroz Nov 20, 2019
8735c3f
Merge remote-tracking branch 'origin/csv-customresourcedefinitions' i…
estroz Nov 20, 2019
c2a1843
use structtag.Parse to correctly parse tags; unit tests
estroz Nov 21, 2019
a67dc94
Merge branch 'master' into csv-customresourcedefinitions
estroz Nov 21, 2019
e0977a6
update test/test-framework go.sum
estroz Nov 21, 2019
5b2693e
consider embedded structs and add test cases
estroz Dec 2, 2019
f11b35f
Merge branch 'master' into csv-customresourcedefinitions
estroz Dec 2, 2019
4fe5f95
fix tests and return jsonTag.Name if name is -
estroz Dec 3, 2019
7e3e9a6
Merge branch 'master' into csv-customresourcedefinitions
estroz Dec 4, 2019
241f71a
fix linter error
estroz Dec 4, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions doc/user/olm-catalog/csv-annotations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# Code Annotations for Cluster Service Versions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @estroz,

We forget to add the changelog entry over this one.


## Overview

This document describes the semantics of Cluster Service Version (CSV) [code annotations][code-annotations-design] and lists all possible annotations.

## Usage

All annotations have a `+operator-sdk:gen-csv` prefix, denoting that they're parsed while executing [`operator-sdk olm-catalog gen-csv`][sdk-cli-ref].

### Paths

Paths are dot-separated string hierarchies with the above prefix that map to CSV [`spec`][csv-spec] field names.

Example: `+operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.displayName="Pod Count"`

#### customresourcedefinitions

- `customresourcedefinitions`: child path token
- `displayName`: quoted string or string literal
- `resources`: quoted string or string literal, in the format `"kind,version,\"name\""` or `` `kind,version,"name"` ``, where `kind`, `version`, and `name` are fields in each CSV `resources` entry
- `specDescriptors`, `statusDescriptors`: bool, or child path token
- `displayName`: quoted string or string literal
- `x-descriptors`: quoted string or string literal comma-separated list of [`x-descriptor`][csv-x-desc] UI hints.

**NOTES**
- `specDescriptors` and `statusDescriptors` with a value of `true` is required for each field to be included in their respective `customresourcedefinitions` CSV fields. See the examples below.
- `customresourcedefinitions` top-level `kind`, `name`, and `version` fields are parsed from API code.
- All `description` fields are parsed from type declaration and `struct` type field comments.
- `path` is parsed out of a field's JSON tag and merged with parent field path's in dot-hierarchy notation.

### Examples

These examples assume `Memcached`, `MemcachedSpec`, and `MemcachedStatus` are the example projects' kind, spec, and status.

1. Set a display name for a `customresourcedefinitions` kind entry:

```go
// +operator-sdk:gen-csv:customresourcedefinitions.displayName="Memcached App"
type Memcached struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec MemcachedSpec `json:"spec,omitempty"`
Status MemcachedStatus `json:"status,omitempty"`
}
```

2. Set `displayName`, `path`, `x-descriptors`, and `description` on a field for a `customresourcedefinitions.specDescriptors` entry:

```go
type MemcachedSpec struct {
// Size is the size of the memcached deployment. <-- This will become Size's specDescriptors.description.
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.displayName="Pod Count"
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.x-descriptors="urn:alm:descriptor:com.tectonic.ui:podCount,urn:alm:descriptor:io.kubernetes:custom"
Size int32 `json:"size"` // <-- Size's specDescriptors.path is inferred from this JSON tag.
}
```

3. Let the SDK infer all un-annotated paths on a field for a `customresourcedefinitions.specDescriptors` entry:

```go
type MemcachedSpec struct {
// Size is the size of the memcached deployment.
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true
Size int32 `json:"size"`
}
```

The SDK uses the `Size` fields' `json` tag name as `path`, `Size` as `displayName`, and field comments as `description`.

The SDK also checks `path` elements against a list of well-known path to x-descriptor string mappings and either uses a match as `x-descriptors`, or does not set `x-descriptors`. Supported mappings:

#### Spec x-descriptors

| PATH | X-DESCRIPTOR |
| --- | --- |
| `size` | `urn:alm:descriptor:com.tectonic.ui:podCount` |
| `podCount` | `urn:alm:descriptor:com.tectonic.ui:podCount` |
| `endpoints` | `urn:alm:descriptor:com.tectonic.ui:endpointList` |
| `endpointList` | `urn:alm:descriptor:com.tectonic.ui:endpointList` |
| `label` | `urn:alm:descriptor:com.tectonic.ui:label` |
| `resources` | `urn:alm:descriptor:com.tectonic.ui:resourceRequirements` |
| `resourceRequirements` | `urn:alm:descriptor:com.tectonic.ui:resourceRequirements` |
| `selector` | `urn:alm:descriptor:com.tectonic.ui:selector:` |
| `namespaceSelector` | `urn:alm:descriptor:com.tectonic.ui:namespaceSelector` |
| none | `urn:alm:descriptor:io.kubernetes:` |
| `booleanSwitch` | `urn:alm:descriptor:com.tectonic.ui:booleanSwitch` |
| `password` | `urn:alm:descriptor:com.tectonic.ui:password` |
| `checkbox` | `urn:alm:descriptor:com.tectonic.ui:checkbox` |
| `imagePullPolicy` | `urn:alm:descriptor:com.tectonic.ui:imagePullPolicy` |
| `updateStrategy` | `urn:alm:descriptor:com.tectonic.ui:updateStrategy` |
| `text` | `urn:alm:descriptor:com.tectonic.ui:text` |
| `number` | `urn:alm:descriptor:com.tectonic.ui:number` |
| `nodeAffinity` | `urn:alm:descriptor:com.tectonic.ui:nodeAffinity` |
| `podAffinity` | `urn:alm:descriptor:com.tectonic.ui:podAffinity` |
| `podAntiAffinity` | `urn:alm:descriptor:com.tectonic.ui:podAntiAffinity` |
| none | `urn:alm:descriptor:com.tectonic.ui:fieldGroup:` |
| none | `urn:alm:descriptor:com.tectonic.ui:arrayFieldGroup:` |
| none | `urn:alm:descriptor:com.tectonic.ui:select:` |
| `advanced` | `urn:alm:descriptor:com.tectonic.ui:advanced` |

#### Status x-descriptors

| PATH | X-DESCRIPTOR |
| --- | --- |
| `podStatuses` | `urn:alm:descriptor:com.tectonic.ui:podStatuses` |
| `size` | `urn:alm:descriptor:com.tectonic.ui:podCount` |
| `podCount` | `urn:alm:descriptor:com.tectonic.ui:podCount` |
| `link` | `urn:alm:descriptor:org.w3:link` |
| `w3link` | `urn:alm:descriptor:org.w3:link` |
| `conditions` | `urn:alm:descriptor:io.kubernetes.conditions` |
| `text` | `urn:alm:descriptor:text` |
| `prometheusEndpoint` | `urn:alm:descriptor:prometheusEndpoint` |
| `phase` | `urn:alm:descriptor:io.kubernetes.phase` |
| `k8sPhase` | `urn:alm:descriptor:io.kubernetes.phase` |
| `reason` | `urn:alm:descriptor:io.kubernetes.phase:reason` |
| `k8sReason` | `urn:alm:descriptor:io.kubernetes.phase:reason` |
| none | `urn:alm:descriptor:io.kubernetes:` |

**NOTE:** any x-descriptor that ends in `:` will not be inferred by `path` element, ex. `urn:alm:descriptor:io.kubernetes:`. Use the `x-descriptors` annotation if you want to enable one for your type.

4. A comprehensive example:
- Infer `path`, `description`, `displayName`, and `x-descriptors` for `specDescriptors` and `statusDescriptors` entries.
- Create three `resources` entries each with `kind`, `version`, and `name` values.

```go
// Represents a cluster of Memcached apps
// +operator-sdk:gen-csv:customresourcedefinitions.displayName="Memcached App"
// +operator-sdk:gen-csv:customresourcedefinitions.resources="Deployment,v1,\"memcached-operator\""
// +operator-sdk:gen-csv:customresourcedefinitions.resources=`Service,v1,"memcached-operator"`
type Memcached struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec MemcachedSpec `json:"spec,omitempty"`
Status MemcachedStatus `json:"status,omitempty"`
}

type MemcachedSpec struct {
Pods MemcachedPods `json:"pods"`
}

type MemcachedStatus struct {
Pods MemcachedPods `json:"podStatuses"`
}

type MemcachedPods struct {
// Size is the size of the memcached deployment.
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true
// +operator-sdk:gen-csv:customresourcedefinitions.statusDescriptors=true
Size int32 `json:"size"`
}
```

The generated `customresourcedefinitions` will look like:

```yaml
customresourcedefinitions:
owned:
- description: Represents a cluster of Memcached apps
displayName: Memcached App
kind: Memcached
name: memcacheds.cache.example.com
version: v1alpha1
resources:
- kind: Deployment
name: A Kubernetes Deployment
version: v1
- kind: ReplicaSet
name: A Kubernetes ReplicaSet
version: v1beta2
- kind: Pod
name: A Kubernetes Pod
version: v1
specDescriptors:
- description: The desired number of member Pods for the deployment.
displayName: Size
path: pods.size
x-descriptors:
- 'urn:alm:descriptor:com.tectonic.ui:podCount'
statusDescriptors:
- description: The desired number of member Pods for the deployment.
displayName: Size
path: podStatuses.size
x-descriptors:
- 'urn:alm:descriptor:com.tectonic.ui:podStatuses'
- 'urn:alm:descriptor:com.tectonic.ui:podCount'
```

[code-annotations-design]:../../proposals/sdk-code-annotations.md
[sdk-cli-ref]:../../sdk-cli-reference.md#gen-csv
[csv-x-desc]:https://github.com/openshift/console/blob/feabd61/frontend/packages/operator-lifecycle-manager/src/components/descriptors/types.ts#L3-L39
[csv-spec]:https://github.com/operator-framework/operator-lifecycle-manager/blob/e0eea22/doc/design/building-your-csv.md
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect
github.com/fatih/camelcase v1.0.0 // indirect
github.com/fatih/structtag v1.1.0
github.com/ghodss/yaml v1.0.1-0.20180820084758-c7ce16629ff4
github.com/go-logr/logr v0.1.0
github.com/go-logr/zapr v0.1.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ github.com/fatih/camelcase v0.0.0-20160318181535-f6a740d52f96/go.mod h1:yN2Sb0lF
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structtag v1.1.0 h1:6j4mUV/ES2duvnAzKMFkN6/A5mCaNYPD3xfbAkLLOF8=
github.com/fatih/structtag v1.1.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
Expand Down
26 changes: 23 additions & 3 deletions internal/annotations/prefix.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,28 @@ const (
valueSep = "="
)

// joinWithTrim is like strings.Join() but trims sep from each element in elems
// prior to joining elems. Code modified from strings.Join().
func joinWithTrim(sep string, elems ...string) string {
switch len(elems) {
case 0:
return ""
case 1:
return elems[0]
}
b := strings.Builder{}
b.WriteString(strings.Trim(elems[0], sep))
for _, e := range elems[1:] {
if e = strings.Trim(e, sep); e != "" {
b.WriteString(sep)
b.WriteString(e)
}
}
return b.String()
}

func JoinPrefix(tokens ...string) string {
return strings.Join(tokens, prefixSep)
return joinWithTrim(prefixSep, tokens...)
}

func SplitPrefix(prefix string) ([]string, error) {
Expand All @@ -51,7 +71,7 @@ func SplitPrefix(prefix string) ([]string, error) {
}

func JoinPath(elements ...string) string {
return strings.Join(elements, pathSep)
return joinWithTrim(pathSep, elements...)
}

func SplitPath(path string) ([]string, error) {
Expand All @@ -71,7 +91,7 @@ func SplitPath(path string) ([]string, error) {
}

func JoinAnnotation(prefixedPath, value string) string {
return prefixedPath + valueSep + value
return strings.Trim(prefixedPath, valueSep) + valueSep + strings.Trim(value, valueSep)
}

func SplitAnnotation(annotation string) (prefixedPath, val string, err error) {
Expand Down
35 changes: 22 additions & 13 deletions internal/scaffold/olm-catalog/csv_updaters.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"sort"
"strings"

"github.com/operator-framework/operator-sdk/internal/scaffold"
"github.com/operator-framework/operator-sdk/internal/scaffold/olm-catalog/descriptor"
"github.com/operator-framework/operator-sdk/pkg/k8sutil"

"github.com/ghodss/yaml"
Expand Down Expand Up @@ -277,6 +279,11 @@ func (store *updaterStore) AddOwnedCRD(yamlDoc []byte) error {
Kind: kind,
}
store.crds.crIDs[crdDescID(crdDesc)] = struct{}{}
// Parse CRD descriptors from source code comments and annotations.
gvk := schema.GroupVersionKind{Group: crd.Spec.Group, Version: ver, Kind: kind}
if err := descriptor.GetCRDDescriptorForGVK(scaffold.ApisDir, &crdDesc, gvk); err != nil {
return errors.Wrapf(err, "failed to set CRD descriptors for %s", gvk)
}
store.crds.Owned = append(store.crds.Owned, crdDesc)
}
return nil
Expand Down Expand Up @@ -317,22 +324,24 @@ func getGVKID(g, v, k string) string {

// Apply updates csv's "owned" CRDDescriptions. "required" CRDDescriptions are
// left as-is, since they are user-defined values.
// Apply will only make a new spec.customresourcedefinitions.owned element if
// the CRD key is not in spec.customresourcedefinitions.owned already.
// Apply will only make a new spec.customresourcedefinitions.owned element for
// a type if an annotation is present on that type's declaration.
func (u *CustomResourceDefinitionsUpdate) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) error {
set := make(map[string]olmapiv1alpha1.CRDDescription)
for _, csvDesc := range csv.Spec.CustomResourceDefinitions.Owned {
set[crdDescID(csvDesc)] = csvDesc
}
newDescs := []olmapiv1alpha1.CRDDescription{}
for _, uDesc := range u.Owned {
if csvDesc, ok := set[crdDescID(uDesc)]; !ok {
newDescs = append(newDescs, uDesc)
} else {
newDescs = append(newDescs, csvDesc)
// Currently this updater does not support ActionDescriptor annotations,
// so use those currently set in csv.
actionDescriptors := map[string][]olmapiv1alpha1.ActionDescriptor{}
for _, desc := range csv.Spec.CustomResourceDefinitions.Owned {
actionDescriptors[crdDescID(desc)] = desc.ActionDescriptor
}
// Copy owned CRDDescriptions while preserving ActionDescriptors.
owned := make([]olmapiv1alpha1.CRDDescription, len(u.Owned))
copy(owned, u.Owned)
csv.Spec.CustomResourceDefinitions.Owned = owned
for i, desc := range csv.Spec.CustomResourceDefinitions.Owned {
if ad, ok := actionDescriptors[crdDescID(desc)]; ok {
csv.Spec.CustomResourceDefinitions.Owned[i].ActionDescriptor = ad
}
}
csv.Spec.CustomResourceDefinitions.Owned = newDescs
sort.Sort(descSorter(csv.Spec.CustomResourceDefinitions.Owned))
sort.Sort(descSorter(csv.Spec.CustomResourceDefinitions.Required))
return nil
Expand Down
Loading