Skip to content

Commit 3270033

Browse files
author
Eric Stroczynski
authored
generate: consider service accounts when generating a CSV (#3610)
This commit adds handling for extra RBAC objects present in `generate <bundle|packagemanifests>` input. These objects will be written to the resulting bundle. For now, only Roles, RoleBindings, their Cluster equivalents, and ServiceAccounts are written. This PR also correctly names service account for (cluster) role permissions. These are currently incorrect because the CSV generator is naively using (cluster) role names instead of actual service account names. Previously this was ok because the names match the service account, but this is no longer the case. See #3600. Old test data has been removed, and a static `basic.operator.yaml` containing the output of `kustomize build config/manifests` added; the static file's contents match a current project manifest build. internal/cmd/operator-sdk/generate: write RBAC objects to stdout or files named with object.Name + GVK, and rename `--update-crds` to `--update-objects` internal/generate/{collector/clusterserviceversion}: consider (cluster) role bindings so CSV generator can assign the correct service account names to roles
1 parent bfe13ff commit 3270033

File tree

57 files changed

+1154
-2582
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1154
-2582
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
entries:
2+
- description: >
3+
`generate <bundle|packagemanifests>` will write RBAC objects (Roles, RoleBindings, their Cluster equivalents,
4+
and ServiceAccounts) not bound to CSV deployment service accounts
5+
to the resulting manifests directory.
6+
kind: addition
7+
- description: >
8+
The `--update-crds` flag has been renamed to `--update-objects` for the `generate packagemanifests` subcommand.
9+
kind: change
10+
breaking: true
11+
migration:
12+
header: Rename `--update-crds` flag to `--update-objects` in `generate packagemanifests` invocations
13+
body: >
14+
This flag has been renamed to account for all objects that can be written to the package directory,
15+
ex. Roles.
16+
- description: >
17+
Fixed incorrect (cluster) role name assignments in generated CSVs
18+
[#3600](https://github.com/operator-framework/operator-sdk/issues/3600).
19+
kind: bugfix

internal/cmd/operator-sdk/generate/bundle/bundle.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,7 @@ func (c bundleCmd) runManifests(cfg *config.Config) (err error) {
197197
return fmt.Errorf("error generating ClusterServiceVersion: %v", err)
198198
}
199199

200-
var objs []interface{}
201-
for _, crd := range col.V1CustomResourceDefinitions {
202-
objs = append(objs, crd)
203-
}
204-
for _, crd := range col.V1beta1CustomResourceDefinitions {
205-
objs = append(objs, crd)
206-
}
200+
objs := genutil.GetManifestObjects(col)
207201
if c.stdout {
208202
if err := genutil.WriteObjects(stdout, objs...); err != nil {
209203
return err

internal/cmd/operator-sdk/generate/internal/genutil.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ import (
2323
"path/filepath"
2424
"strings"
2525

26-
"k8s.io/apimachinery/pkg/util/validation"
27-
2826
"github.com/blang/semver"
2927
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
3028
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
29+
"k8s.io/apimachinery/pkg/util/validation"
30+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3131
"sigs.k8s.io/kubebuilder/pkg/model/config"
3232
"sigs.k8s.io/yaml"
3333
)
@@ -57,7 +57,7 @@ func IsPipeReader() bool {
5757
}
5858

5959
// WriteObjects writes each object in objs to w.
60-
func WriteObjects(w io.Writer, objs ...interface{}) error {
60+
func WriteObjects(w io.Writer, objs ...controllerutil.Object) error {
6161
for _, obj := range objs {
6262
if err := writeObject(w, obj); err != nil {
6363
return err
@@ -67,7 +67,7 @@ func WriteObjects(w io.Writer, objs ...interface{}) error {
6767
}
6868

6969
// WriteObjectsToFiles creates dir then writes each object in objs to a file in dir.
70-
func WriteObjectsToFiles(dir string, objs ...interface{}) error {
70+
func WriteObjectsToFiles(dir string, objs ...controllerutil.Object) error {
7171
if err := os.MkdirAll(dir, 0755); err != nil {
7272
return err
7373
}
@@ -76,12 +76,12 @@ func WriteObjectsToFiles(dir string, objs ...interface{}) error {
7676
for _, obj := range objs {
7777
var fileName string
7878
switch t := obj.(type) {
79-
case apiextv1.CustomResourceDefinition:
79+
case *apiextv1.CustomResourceDefinition:
8080
fileName = makeCRDFileName(t.Spec.Group, t.Spec.Names.Plural)
81-
case apiextv1beta1.CustomResourceDefinition:
81+
case *apiextv1beta1.CustomResourceDefinition:
8282
fileName = makeCRDFileName(t.Spec.Group, t.Spec.Names.Plural)
8383
default:
84-
return fmt.Errorf("unknown object type: %T", t)
84+
fileName = makeObjectFileName(t)
8585
}
8686

8787
if _, hasFile := seenFiles[fileName]; hasFile {
@@ -99,6 +99,14 @@ func makeCRDFileName(group, resource string) string {
9999
return fmt.Sprintf("%s_%s.yaml", group, resource)
100100
}
101101

102+
func makeObjectFileName(obj controllerutil.Object) string {
103+
gvk := obj.GetObjectKind().GroupVersionKind()
104+
if gvk.Group == "" {
105+
return fmt.Sprintf("%s_%s_%s.yaml", obj.GetName(), gvk.Version, strings.ToLower(gvk.Kind))
106+
}
107+
return fmt.Sprintf("%s_%s_%s_%s.yaml", obj.GetName(), gvk.Group, gvk.Version, strings.ToLower(gvk.Kind))
108+
}
109+
102110
// writeObjectToFile marshals crd to bytes and writes them to dir in file.
103111
func writeObjectToFile(dir string, obj interface{}, fileName string) error {
104112
f, err := os.Create(filepath.Join(dir, fileName))
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2020 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+
package genutil
16+
17+
import (
18+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
19+
20+
"github.com/operator-framework/operator-sdk/internal/generate/collector"
21+
)
22+
23+
// GetManifestObjects returns all objects to be written to a manifests directory from collector.Manifests.
24+
func GetManifestObjects(c *collector.Manifests) (objs []controllerutil.Object) {
25+
// All CRDs passed in should be written.
26+
for i := range c.V1CustomResourceDefinitions {
27+
objs = append(objs, &c.V1CustomResourceDefinitions[i])
28+
}
29+
for i := range c.V1beta1CustomResourceDefinitions {
30+
objs = append(objs, &c.V1beta1CustomResourceDefinitions[i])
31+
}
32+
33+
// All ServiceAccounts passed in should be written.
34+
for i := range c.ServiceAccounts {
35+
objs = append(objs, &c.ServiceAccounts[i])
36+
}
37+
38+
// RBAC objects that are not a part of the CSV should be written.
39+
_, roleObjs := c.SplitCSVPermissionsObjects()
40+
objs = append(objs, roleObjs...)
41+
_, clusterRoleObjs := c.SplitCSVClusterPermissionsObjects()
42+
objs = append(objs, clusterRoleObjs...)
43+
44+
return objs
45+
}

internal/cmd/operator-sdk/generate/packagemanifests/cmd.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,17 @@ import (
2828
//nolint:maligned
2929
type packagemanifestsCmd struct {
3030
// Common options.
31-
projectName string
32-
version string
33-
fromVersion string
34-
inputDir string
35-
outputDir string
36-
kustomizeDir string
37-
deployDir string
38-
crdsDir string
39-
updateCRDs bool
40-
stdout bool
41-
quiet bool
31+
projectName string
32+
version string
33+
fromVersion string
34+
inputDir string
35+
outputDir string
36+
kustomizeDir string
37+
deployDir string
38+
crdsDir string
39+
updateObjects bool
40+
stdout bool
41+
quiet bool
4242

4343
// Package manifest options.
4444
channelName string
@@ -97,7 +97,8 @@ func (c *packagemanifestsCmd) addFlagsTo(fs *pflag.FlagSet) {
9797
fs.StringVar(&c.channelName, "channel", "", "Channel name for the generated package")
9898
fs.BoolVar(&c.isDefaultChannel, "default-channel", false, "Use the channel passed to --channel "+
9999
"as the package manifest file's default channel")
100-
fs.BoolVar(&c.updateCRDs, "update-crds", true, "Update CustomResoureDefinition manifests in this package")
100+
fs.BoolVar(&c.updateObjects, "update-objects", true, "Update non-CSV objects in this package, "+
101+
"ex. CustomResoureDefinitions, Roles")
101102
fs.BoolVarP(&c.quiet, "quiet", "q", false, "Run in quiet mode")
102103
fs.BoolVar(&c.stdout, "stdout", false, "Write package to stdout")
103104
}

internal/cmd/operator-sdk/generate/packagemanifests/packagemanifests.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,8 @@ func (c packagemanifestsCmd) run(cfg *config.Config) error {
188188
return fmt.Errorf("error generating ClusterServiceVersion: %v", err)
189189
}
190190

191-
if c.updateCRDs {
192-
var objs []interface{}
193-
for _, crd := range col.V1CustomResourceDefinitions {
194-
objs = append(objs, crd)
195-
}
196-
for _, crd := range col.V1beta1CustomResourceDefinitions {
197-
objs = append(objs, crd)
198-
}
191+
if c.updateObjects {
192+
objs := genutil.GetManifestObjects(col)
199193
if c.stdout {
200194
if err := genutil.WriteObjects(stdout, objs...); err != nil {
201195
return err

internal/generate/clusterserviceversion/clusterserviceversion_test.go

Lines changed: 16 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ package clusterserviceversion
1616

1717
import (
1818
"bytes"
19-
"encoding/json"
2019
"fmt"
2120
"io/ioutil"
2221
"os"
@@ -31,7 +30,6 @@ import (
3130
"github.com/operator-framework/api/pkg/operators/v1alpha1"
3231
"github.com/operator-framework/operator-registry/pkg/lib/bundle"
3332
appsv1 "k8s.io/api/apps/v1"
34-
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3533
"sigs.k8s.io/kubebuilder/pkg/model/config"
3634
"sigs.k8s.io/yaml"
3735

@@ -45,16 +43,12 @@ var (
4543
testDataDir = filepath.Join("..", "testdata")
4644
csvDir = filepath.Join(testDataDir, "clusterserviceversions")
4745
csvBasesDir = filepath.Join(csvDir, "bases")
48-
csvNewLayoutBundleDir = filepath.Join(csvDir, "newlayout", "manifests")
49-
50-
// TODO: create a new testdata dir (top level?) that has both a "config"
51-
// dir and a "deploy" dir that contains `kustomize build config/default`
52-
// output to simulate actual manifest collection behavior. Using "config"
53-
// directly is not standard behavior.
54-
goTestDataDir = filepath.Join(testDataDir, "non-standard-layout")
55-
goAPIsDir = filepath.Join(goTestDataDir, "api")
56-
goConfigDir = filepath.Join(goTestDataDir, "config")
57-
goCRDsDir = filepath.Join(goConfigDir, "crds")
46+
csvNewLayoutBundleDir = filepath.Join(csvDir, "output")
47+
48+
goTestDataDir = filepath.Join(testDataDir, "go")
49+
goAPIsDir = filepath.Join(goTestDataDir, "api")
50+
goStaticDir = filepath.Join(goTestDataDir, "static")
51+
goBasicOperatorPath = filepath.Join(goStaticDir, "basic.operator.yaml")
5852
)
5953

6054
var (
@@ -74,7 +68,7 @@ var (
7468

7569
var _ = BeforeSuite(func() {
7670
col = &collector.Manifests{}
77-
Expect(col.UpdateFromDirs(goConfigDir, goCRDsDir)).ToNot(HaveOccurred())
71+
collectManifestsFromFileHelper(col, goBasicOperatorPath)
7872

7973
cfg = readConfigHelper(goTestDataDir)
8074

@@ -97,7 +91,7 @@ var _ = Describe("Generating a ClusterServiceVersion", func() {
9791
buf = &bytes.Buffer{}
9892
})
9993

100-
Describe("for the new Go project layout", func() {
94+
Describe("for a Go project", func() {
10195

10296
Context("with correct Options", func() {
10397

@@ -281,7 +275,7 @@ var _ = Describe("Generating a ClusterServiceVersion", func() {
281275
getBase: makeBaseGetter(newCSV),
282276
}
283277
// Update the input's and expected CSV's Deployment image.
284-
Expect(g.Collector.UpdateFromDirs(goConfigDir, goCRDsDir)).ToNot(HaveOccurred())
278+
collectManifestsFromFileHelper(g.Collector, goBasicOperatorPath)
285279
Expect(len(g.Collector.Deployments)).To(BeNumerically(">=", 1))
286280
imageTag := "controller:v" + g.Version
287281
modifyDepImageHelper(&g.Collector.Deployments[0].Spec, imageTag)
@@ -311,42 +305,6 @@ var _ = Describe("Generating a ClusterServiceVersion", func() {
311305
Expect(csv).To(Equal(upgradeCSV(newCSV, g.OperatorName, g.Version)))
312306
})
313307
})
314-
315-
Context("generate ClusterServiceVersion", func() {
316-
It("should handle CRDs with core type name", func() {
317-
g = Generator{
318-
OperatorName: operatorName,
319-
OperatorType: operatorType,
320-
Version: version,
321-
Collector: &collector.Manifests{},
322-
config: cfg,
323-
getBase: makeBaseGetter(newCSV),
324-
}
325-
err := filepath.Walk(goConfigDir, func(path string, info os.FileInfo, err error) error {
326-
if err != nil || info.IsDir() {
327-
return err
328-
}
329-
file, err := os.OpenFile(path, os.O_RDONLY, 0)
330-
if err != nil {
331-
return err
332-
}
333-
defer file.Close()
334-
return g.Collector.UpdateFromReader(file)
335-
})
336-
Expect(err).ShouldNot(HaveOccurred(), "failed to read manifests")
337-
Expect(len(g.Collector.V1beta1CustomResourceDefinitions)).Should(BeEquivalentTo(2))
338-
Expect(len(g.Collector.CustomResources)).Should(BeEquivalentTo(2))
339-
340-
csv, err := g.generate()
341-
Expect(err).ToNot(HaveOccurred())
342-
Expect(csv.Annotations["alm-examples"]).ShouldNot(BeEquivalentTo("[]"))
343-
344-
crs := []unstructured.Unstructured{}
345-
err = json.Unmarshal([]byte(csv.Annotations["alm-examples"]), &crs)
346-
Expect(err).ShouldNot(HaveOccurred(), "failed to parse 'alm-examples' annotations")
347-
Expect(crs).Should(ConsistOf(g.Collector.CustomResources), "custom resources shall match with CSV annotations")
348-
})
349-
})
350308
})
351309

352310
})
@@ -388,6 +346,13 @@ var _ = Describe("Generation requires interaction", func() {
388346
})
389347
})
390348

349+
func collectManifestsFromFileHelper(col *collector.Manifests, path string) {
350+
f, err := os.Open(path)
351+
ExpectWithOffset(1, err).ToNot(HaveOccurred())
352+
ExpectWithOffset(1, col.UpdateFromReader(f)).ToNot(HaveOccurred())
353+
ExpectWithOffset(1, f.Close()).Should(Succeed())
354+
}
355+
391356
func readConfigHelper(dir string) *config.Config {
392357
wd, err := os.Getwd()
393358
ExpectWithOffset(1, err).ToNot(HaveOccurred())

0 commit comments

Comments
 (0)