Skip to content

Commit 8abbabd

Browse files
authored
Merge pull request #173 from justinsb/set_namespace_direct_applier
Fix namespace setting with direct applier
2 parents 1249b4b + 198f997 commit 8abbabd

File tree

5 files changed

+104
-25
lines changed

5 files changed

+104
-25
lines changed

pkg/patterns/declarative/pkg/applier/direct.go

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,71 @@ package applier
22

33
import (
44
"context"
5+
"fmt"
56
"os"
67
"strings"
78

9+
"k8s.io/apimachinery/pkg/api/meta"
810
"k8s.io/cli-runtime/pkg/genericclioptions"
911
"k8s.io/cli-runtime/pkg/printers"
1012
"k8s.io/cli-runtime/pkg/resource"
13+
"k8s.io/client-go/discovery"
14+
"k8s.io/client-go/rest"
1115
"k8s.io/kubectl/pkg/cmd/apply"
1216
cmdDelete "k8s.io/kubectl/pkg/cmd/delete"
1317
cmdutil "k8s.io/kubectl/pkg/cmd/util"
1418
)
1519

1620
type DirectApplier struct {
17-
a apply.ApplyOptions
1821
}
1922

23+
var _ Applier = &DirectApplier{}
24+
2025
func NewDirectApplier() *DirectApplier {
2126
return &DirectApplier{}
2227
}
2328

24-
func (d *DirectApplier) Apply(ctx context.Context,
25-
namespace string,
26-
manifest string,
27-
validate bool,
28-
extraArgs ...string,
29-
) error {
29+
func (d *DirectApplier) Apply(ctx context.Context, opt ApplierOptions) error {
3030
ioStreams := genericclioptions.IOStreams{
3131
In: os.Stdin,
3232
Out: os.Stdout,
3333
ErrOut: os.Stderr,
3434
}
35-
restClient := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag()
36-
ioReader := strings.NewReader(manifest)
35+
ioReader := strings.NewReader(opt.Manifest)
36+
37+
restClientGetter := &staticRESTClientGetter{
38+
RESTMapper: opt.RESTMapper,
39+
RESTConfig: opt.RESTConfig,
40+
}
41+
b := resource.NewBuilder(restClientGetter)
42+
43+
if opt.Validate {
44+
// This potentially causes redundant work, but validation isn't the common path
45+
v, err := cmdutil.NewFactory(&genericclioptions.ConfigFlags{}).Validator(true)
46+
if err != nil {
47+
return err
48+
}
49+
b.Schema(v)
50+
}
3751

38-
b := resource.NewBuilder(restClient)
3952
res := b.Unstructured().Stream(ioReader, "manifestString").Do()
4053
infos, err := res.Infos()
4154
if err != nil {
4255
return err
4356
}
4457

58+
// Populate the namespace on any namespace-scoped objects
59+
if opt.Namespace != "" {
60+
visitor := resource.SetNamespace(opt.Namespace)
61+
for _, info := range infos {
62+
if err := info.Visit(visitor); err != nil {
63+
return fmt.Errorf("error from SetNamespace: %w", err)
64+
}
65+
}
66+
}
67+
4568
applyOpts := apply.NewApplyOptions(ioStreams)
46-
applyOpts.Namespace = namespace
69+
applyOpts.Namespace = opt.Namespace
4770
applyOpts.SetObjects(infos)
4871
applyOpts.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
4972
applyOpts.PrintFlags.NamePrintFlags.Operation = operation
@@ -56,3 +79,31 @@ func (d *DirectApplier) Apply(ctx context.Context,
5679

5780
return applyOpts.Run()
5881
}
82+
83+
// staticRESTClientGetter returns a fixed RESTClient
84+
type staticRESTClientGetter struct {
85+
RESTConfig *rest.Config
86+
DiscoveryClient discovery.CachedDiscoveryInterface
87+
RESTMapper meta.RESTMapper
88+
}
89+
90+
var _ resource.RESTClientGetter = &staticRESTClientGetter{}
91+
92+
func (s *staticRESTClientGetter) ToRESTConfig() (*rest.Config, error) {
93+
if s.RESTConfig == nil {
94+
return nil, fmt.Errorf("RESTConfig not set")
95+
}
96+
return s.RESTConfig, nil
97+
}
98+
func (s *staticRESTClientGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
99+
if s.DiscoveryClient == nil {
100+
return nil, fmt.Errorf("DiscoveryClient not set")
101+
}
102+
return s.DiscoveryClient, nil
103+
}
104+
func (s *staticRESTClientGetter) ToRESTMapper() (meta.RESTMapper, error) {
105+
if s.RESTMapper == nil {
106+
return nil, fmt.Errorf("RESTMapper not set")
107+
}
108+
return s.RESTMapper, nil
109+
}

pkg/patterns/declarative/pkg/applier/exec.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ type ExecKubectl struct {
3737
cmdSite commandSite
3838
}
3939

40+
var _ Applier = &ExecKubectl{}
41+
4042
// commandSite allows for tests to mock cmd.Run() events
4143
type commandSite interface {
4244
Run(*exec.Cmd) error
@@ -49,26 +51,25 @@ func (console) Run(c *exec.Cmd) error {
4951
}
5052

5153
// Apply runs the kubectl apply with the provided manifest argument
52-
func (c *ExecKubectl) Apply(ctx context.Context, namespace string, manifest string, validate bool,
53-
extraArgs ...string) error {
54+
func (c *ExecKubectl) Apply(ctx context.Context, opt ApplierOptions) error {
5455
log := log.Log
5556

5657
log.Info("applying manifest")
5758

5859
args := []string{"apply"}
59-
if namespace != "" {
60-
args = append(args, "-n", namespace)
60+
if opt.Namespace != "" {
61+
args = append(args, "-n", opt.Namespace)
6162
}
6263

6364
// Not doing --validate avoids downloading the OpenAPI
6465
// which can save a lot work & memory
65-
args = append(args, "--validate="+strconv.FormatBool(validate))
66+
args = append(args, "--validate="+strconv.FormatBool(opt.Validate))
6667

67-
args = append(args, extraArgs...)
68+
args = append(args, opt.ExtraArgs...)
6869
args = append(args, "-f", "-")
6970

7071
cmd := exec.Command("kubectl", args...)
71-
cmd.Stdin = strings.NewReader(manifest)
72+
cmd.Stdin = strings.NewReader(opt.Manifest)
7273

7374
var stdout bytes.Buffer
7475
var stderr bytes.Buffer
@@ -80,7 +81,7 @@ func (c *ExecKubectl) Apply(ctx context.Context, namespace string, manifest stri
8081
err := c.cmdSite.Run(cmd)
8182
if err != nil {
8283
log.WithValues("stdout", stdout.String()).WithValues("stderr", stderr.String()).Error(err, "error from running kubectl apply")
83-
log.Info(fmt.Sprintf("manifest:\n%v", manifest))
84+
log.Info(fmt.Sprintf("manifest:\n%v", opt.Manifest))
8485
return fmt.Errorf("error from running kubectl apply: %v", err)
8586
}
8687

pkg/patterns/declarative/pkg/applier/exec_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,13 @@ func TestKubectlApply(t *testing.T) {
8585
t.Run(test.name, func(t *testing.T) {
8686
cs := collector{Error: test.err}
8787
kubectl := &ExecKubectl{cmdSite: &cs}
88-
err := kubectl.Apply(context.Background(), test.namespace, test.manifest, test.validate, test.args...)
88+
opts := ApplierOptions{
89+
Namespace: test.namespace,
90+
Manifest: test.manifest,
91+
Validate: test.validate,
92+
ExtraArgs: test.args,
93+
}
94+
err := kubectl.Apply(context.Background(), opts)
8995

9096
if test.err != nil && err == nil {
9197
t.Error("expected error to occur")

pkg/patterns/declarative/pkg/applier/type.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,21 @@ package applier
22

33
import (
44
"context"
5+
6+
"k8s.io/apimachinery/pkg/api/meta"
7+
"k8s.io/client-go/rest"
58
)
69

710
type Applier interface {
8-
Apply(ctx context.Context, namespace string, manifest string, validate bool, extraArgs ...string) error
11+
Apply(ctx context.Context, options ApplierOptions) error
12+
}
13+
14+
type ApplierOptions struct {
15+
Manifest string
16+
17+
RESTConfig *rest.Config
18+
RESTMapper meta.RESTMapper
19+
Namespace string
20+
Validate bool
21+
ExtraArgs []string
922
}

pkg/patterns/declarative/reconciler.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,16 @@ func (r *Reconciler) reconcileExists(ctx context.Context, name types.NamespacedN
256256
}
257257
}
258258

259-
if err := r.kubectl.Apply(ctx, ns, manifestStr, r.options.validate, extraArgs...); err != nil {
259+
applyOpt := applier.ApplierOptions{
260+
RESTConfig: r.config,
261+
RESTMapper: r.restMapper,
262+
Namespace: ns,
263+
Manifest: manifestStr,
264+
Validate: r.options.validate,
265+
ExtraArgs: extraArgs,
266+
}
267+
268+
if err := r.kubectl.Apply(ctx, applyOpt); err != nil {
260269
log.Error(err, "applying manifest")
261270
return reconcile.Result{}, fmt.Errorf("error applying manifest: %v", err)
262271
}
@@ -564,14 +573,13 @@ func (r *Reconciler) CollectMetrics() bool {
564573
return r.options.metrics
565574
}
566575

567-
func GetObjectFromCluster(obj *manifest.Object, r *Reconciler) (*unstructured.
568-
Unstructured, error) {
576+
func GetObjectFromCluster(obj *manifest.Object, r *Reconciler) (*unstructured.Unstructured, error) {
569577
getOptions := metav1.GetOptions{}
570578
gvk := obj.GroupVersionKind()
571579

572580
mapping, err := r.restMapper.RESTMapping(obj.GroupKind(), gvk.Version)
573581
if err != nil {
574-
return nil, fmt.Errorf("unable to get mapping for resource: %w", err)
582+
return nil, fmt.Errorf("unable to get mapping for resource %v: %w", gvk, err)
575583
}
576584
ns := obj.UnstructuredObject().GetNamespace()
577585
unstruct, err := r.dynamicClient.Resource(mapping.Resource).Namespace(ns).Get(context.Background(),

0 commit comments

Comments
 (0)