Skip to content

Commit cd048dd

Browse files
fix: check if resource is namespacescoped (#148)
* fix: check if resource is namespacescoped * chore: reduce helm max history from 10 to 3 to avoid polluting the cluster
1 parent 0b30b4e commit cd048dd

6 files changed

Lines changed: 103 additions & 110 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ These enviroment varibles can be changed in the Deployment of the composition-dy
205205
| URL_CHART_INSPECTOR | url to chart inspector | `http://chart-inspector.krateo-system.svc.cluster.local:8081/` |
206206
| KRATEO_NAMESPACE | namespace where krateo is installed | krateo-system |
207207
| HELM_REGISTRY_CONFIG_PATH | default helm config path | /tmp |
208-
| HELM_MAX_HISTORY | Max Helm History | 10 |
208+
| HELM_MAX_HISTORY | Max Helm History | 3 |
209209
| COMPOSITION_CONTROLLER_MAX_ERROR_RETRY_INTERVAL | The maximum interval between retries when an error occurs. This should be less than the half of the poll interval. | 0m |
210210
| COMPOSITION_CONTROLLER_MIN_ERROR_RETRY_INTERVAL | The minimum interval between retries when an error occurs. This should be less than max-error-retry-interval. | 1m |
211211
| COMPOSITION_CONTROLLER_METRICS_SERVER_PORT | The port where the metrics server will be listening. If not set, the metrics server is disabled. | |

internal/composition/composition.go

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ var (
3535
helmRegistryConfigPath = env.String(helmRegistryConfigPathEnvVar, helmclient.DefaultRegistryConfigPath)
3636
krateoNamespace = env.String(krateoNamespaceEnvVar, krateoNamespaceDefault)
3737
helmRegistryConfigFile = filepath.Join(helmRegistryConfigPath, registry.CredentialsFileBasename)
38-
helmMaxHistory = env.Int(helmMaxHistoryEnvvar, 10)
38+
helmMaxHistory = env.Int(helmMaxHistoryEnvvar, 3)
3939
)
4040

4141
const (
@@ -150,7 +150,7 @@ func (h *handler) Observe(ctx context.Context, mg *unstructured.Unstructured) (c
150150
return controller.ExternalObservation{}, fmt.Errorf("updating cr with values: %w", err)
151151
}
152152

153-
hc, err := h.helmClientForResource(mg, pkg.RegistryAuth)
153+
hc, _, err := h.helmClientForResource(mg, pkg.RegistryAuth)
154154
if err != nil {
155155
log.Error(err, "Getting helm client")
156156
return controller.ExternalObservation{}, err
@@ -217,7 +217,7 @@ func (h *handler) Observe(ctx context.Context, mg *unstructured.Unstructured) (c
217217
}
218218

219219
tracer := &tracer.Tracer{}
220-
hc, err = h.helmClientForResourceWithTransportWrapper(mg, pkg.RegistryAuth, func(rt http.RoundTripper) http.RoundTripper {
220+
hc, _, err = h.helmClientForResourceWithTransportWrapper(mg, pkg.RegistryAuth, func(rt http.RoundTripper) http.RoundTripper {
221221
return tracer.WithRoundTripper(rt)
222222
})
223223
if err != nil {
@@ -365,7 +365,7 @@ func (h *handler) Create(ctx context.Context, mg *unstructured.Unstructured) err
365365
}
366366

367367
// Install the helm chart
368-
hc, err := h.helmClientForResource(mg, pkg.RegistryAuth)
368+
hc, clientset, err := h.helmClientForResource(mg, pkg.RegistryAuth)
369369
if err != nil {
370370
log.Error(err, "Getting helm client")
371371
return err
@@ -398,7 +398,7 @@ func (h *handler) Create(ctx context.Context, mg *unstructured.Unstructured) err
398398
}
399399
log.Debug("Installing composition package", "package", pkg.URL)
400400

401-
all, err := helmchart.GetResourcesRefFromRelease(rel, mg.GetNamespace())
401+
all, err := helmchart.GetResourcesRefFromRelease(rel, mg.GetNamespace(), clientset)
402402
if err != nil {
403403
log.Error(err, "Getting resources from release")
404404
return fmt.Errorf("getting resources from release: %w", err)
@@ -473,7 +473,7 @@ func (h *handler) Update(ctx context.Context, mg *unstructured.Unstructured) err
473473
}
474474

475475
// Update the helm chart
476-
hc, err := h.helmClientForResource(mg, pkg.RegistryAuth)
476+
hc, clientset, err := h.helmClientForResource(mg, pkg.RegistryAuth)
477477
if err != nil {
478478
log.Error(err, "Getting helm client")
479479
return err
@@ -484,7 +484,7 @@ func (h *handler) Update(ctx context.Context, mg *unstructured.Unstructured) err
484484
return err
485485
}
486486

487-
all, err := helmchart.GetResourcesRefFromRelease(rel, mg.GetNamespace())
487+
all, err := helmchart.GetResourcesRefFromRelease(rel, mg.GetNamespace(), clientset)
488488
if err != nil {
489489
log.Error(err, "Getting resources from release")
490490
return fmt.Errorf("getting resources from release: %w", err)
@@ -576,7 +576,7 @@ func (h *handler) Delete(ctx context.Context, mg *unstructured.Unstructured) err
576576
return fmt.Errorf("helm chart package info getter must be specified")
577577
}
578578

579-
hc, err := h.helmClientForResource(mg, nil)
579+
hc, _, err := h.helmClientForResource(mg, nil)
580580
if err != nil {
581581
log.Error(err, "Getting helm client")
582582
return err
@@ -667,7 +667,7 @@ func (h *handler) Delete(ctx context.Context, mg *unstructured.Unstructured) err
667667
return nil
668668
}
669669

670-
func (h *handler) helmClientForResource(mg *unstructured.Unstructured, registryAuth *helmclient.RegistryAuth) (helmclient.Client, error) {
670+
func (h *handler) helmClientForResource(mg *unstructured.Unstructured, registryAuth *helmclient.RegistryAuth) (helmclient.Client, helmclient.CachedClientsInterface, error) {
671671
log := h.logger.WithValues("apiVersion", mg.GetAPIVersion()).
672672
WithValues("kind", mg.GetKind()).
673673
WithValues("name", mg.GetName()).
@@ -676,7 +676,7 @@ func (h *handler) helmClientForResource(mg *unstructured.Unstructured, registryA
676676
clientSet, err := helmclient.NewCachedClients(h.kubeconfig)
677677
if err != nil {
678678
log.Error(err, "Creating cached helm client set.")
679-
return nil, err
679+
return nil, nil, err
680680
}
681681

682682
opts := &helmclient.Options{
@@ -700,16 +700,17 @@ func (h *handler) helmClientForResource(mg *unstructured.Unstructured, registryA
700700
RegistryAuth: (registryAuth),
701701
}
702702

703-
return helmclient.NewCachedClientFromRestConf(
703+
hc, err := helmclient.NewCachedClientFromRestConf(
704704
&helmclient.RestConfClientOptions{
705705
Options: opts,
706706
RestConfig: h.kubeconfig,
707707
},
708-
&clientSet,
708+
clientSet,
709709
)
710+
return hc, clientSet, err
710711
}
711712

712-
func (h *handler) helmClientForResourceWithTransportWrapper(mg *unstructured.Unstructured, registryAuth *helmclient.RegistryAuth, transportWrapper func(http.RoundTripper) http.RoundTripper) (helmclient.Client, error) {
713+
func (h *handler) helmClientForResourceWithTransportWrapper(mg *unstructured.Unstructured, registryAuth *helmclient.RegistryAuth, transportWrapper func(http.RoundTripper) http.RoundTripper) (helmclient.Client, helmclient.CachedClientsInterface, error) {
713714
opts := &helmclient.Options{
714715
Namespace: mg.GetNamespace(),
715716
RepositoryCache: "/tmp/.helmcache",
@@ -723,17 +724,18 @@ func (h *handler) helmClientForResourceWithTransportWrapper(mg *unstructured.Uns
723724

724725
clientSet, err := helmclient.NewCachedClients(h.kubeconfig)
725726
if err != nil {
726-
return nil, err
727+
return nil, nil, err
727728
}
728729

729730
cfg := rest.CopyConfig(h.kubeconfig)
730731
cfg.WrapTransport = transportWrapper
731732

732-
return helmclient.NewCachedClientFromRestConf(
733+
hc, err := helmclient.NewCachedClientFromRestConf(
733734
&helmclient.RestConfClientOptions{
734735
Options: opts,
735736
RestConfig: cfg,
736737
},
737-
&clientSet,
738+
clientSet,
738739
)
740+
return hc, clientSet, err
739741
}

internal/helmclient/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func NewClientFromRestConf(options *RestConfClientOptions) (Client, error) {
8080
return newClient(options.Options, clientGetter, settings)
8181
}
8282

83-
func NewCachedClientFromRestConf(options *RestConfClientOptions, clientset *CachedClients) (Client, error) {
83+
func NewCachedClientFromRestConf(options *RestConfClientOptions, clientset CachedClientsInterface) (Client, error) {
8484
settings := cli.New()
8585

8686
clientGetter := NewCachedRESTClientGetter(options.Namespace, nil, options.RestConfig, clientset)

internal/helmclient/client_getter.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,26 @@ func (c *RESTClientGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig {
7878
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)
7979
}
8080

81-
type CachedClients struct {
81+
type CachedClientsInterface interface {
82+
DiscoveryClient() discovery.CachedDiscoveryInterface
83+
RESTMapper() meta.RESTMapper
84+
}
85+
86+
var _ CachedClientsInterface = &cachedClients{}
87+
88+
type cachedClients struct {
8289
discoveryClient discovery.CachedDiscoveryInterface
8390
_RESTMapper meta.RESTMapper
8491
}
8592

86-
func NewCachedClients(cfg *rest.Config) (CachedClients, error) {
93+
func NewCachedClients(cfg *rest.Config) (CachedClientsInterface, error) {
8794
dir, err := os.MkdirTemp("", "helmclient")
8895
if err != nil {
89-
return CachedClients{}, err
96+
return cachedClients{}, err
9097
}
9198
cachedDiscovery, err := disk.NewCachedDiscoveryClientForConfig(cfg, dir, "", 0)
9299
if err != nil {
93-
return CachedClients{}, err
100+
return cachedClients{}, err
94101
}
95102

96103
cachedDiscovery.Invalidate()
@@ -99,12 +106,20 @@ func NewCachedClients(cfg *rest.Config) (CachedClients, error) {
99106
mapper.Reset()
100107
expander := restmapper.NewShortcutExpander(mapper, cachedDiscovery, nil)
101108

102-
return CachedClients{
109+
return cachedClients{
103110
discoveryClient: cachedDiscovery,
104111
_RESTMapper: expander,
105112
}, nil
106113
}
107114

115+
func (c cachedClients) DiscoveryClient() discovery.CachedDiscoveryInterface {
116+
return c.discoveryClient
117+
}
118+
119+
func (c cachedClients) RESTMapper() meta.RESTMapper {
120+
return c._RESTMapper
121+
}
122+
108123
var _ clientcmd.ClientConfig = &namespaceClientConfig{}
109124

110125
type namespaceClientConfig struct {
@@ -129,12 +144,12 @@ func (c namespaceClientConfig) ConfigAccess() clientcmd.ConfigAccess {
129144

130145
// NewCachedRESTClientGetter returns a RESTClientGetter using the provided 'namespace', 'kubeConfig', and 'restConfig',
131146
// and uses cached clients for discovery and REST mapping to improve performance and reduce API server load.
132-
func NewCachedRESTClientGetter(namespace string, kubeConfig []byte, restConfig *rest.Config, clients *CachedClients, opts ...RESTClientOption) *CachedRESTClientGetter {
147+
func NewCachedRESTClientGetter(namespace string, kubeConfig []byte, restConfig *rest.Config, clients CachedClientsInterface, opts ...RESTClientOption) *CachedRESTClientGetter {
133148
return &CachedRESTClientGetter{
134149
kubeConfig: kubeConfig,
135150
restConfig: restConfig,
136-
discoveryClient: clients.discoveryClient,
137-
restMapper: clients._RESTMapper,
151+
discoveryClient: clients.DiscoveryClient(),
152+
restMapper: clients.RESTMapper(),
138153
namespaceConfig: &namespaceClientConfig{namespace: namespace},
139154
opts: opts,
140155
}

internal/tools/helmchart/helmchart.go

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import (
66
"io"
77
"strings"
88

9+
"k8s.io/apimachinery/pkg/api/meta"
10+
911
"github.com/krateoplatformops/plumbing/maps"
1012
"github.com/krateoplatformops/unstructured-runtime/pkg/pluralizer"
1113
unstructuredtools "github.com/krateoplatformops/unstructured-runtime/pkg/tools/unstructured"
1214

1315
"github.com/krateoplatformops/composition-dynamic-controller/internal/helmclient"
1416
"github.com/krateoplatformops/unstructured-runtime/pkg/controller/objectref"
15-
"github.com/krateoplatformops/unstructured-runtime/pkg/meta"
1617

1718
"helm.sh/helm/v3/pkg/action"
1819
"helm.sh/helm/v3/pkg/release"
@@ -84,39 +85,7 @@ type RenderTemplateOptions struct {
8485
Pluralizer pluralizer.PluralizerInterface
8586
}
8687

87-
func RenderTemplate(ctx context.Context, opts RenderTemplateOptions) (*release.Release, []objectref.ObjectRef, error) {
88-
dat, err := ExtractValuesFromSpec(opts.Resource)
89-
if err != nil {
90-
return nil, nil, err
91-
}
92-
93-
chartSpec := helmclient.ChartSpec{
94-
ReleaseName: meta.GetReleaseName(opts.Resource),
95-
Namespace: opts.Resource.GetNamespace(),
96-
ChartName: opts.PackageUrl,
97-
Version: opts.PackageVersion,
98-
ValuesYaml: string(dat),
99-
Repo: opts.Repo,
100-
}
101-
if opts.Credentials != nil {
102-
chartSpec.Username = opts.Credentials.Username
103-
chartSpec.Password = opts.Credentials.Password
104-
}
105-
106-
rel, err := opts.HelmClient.TemplateChartRaw(&chartSpec, nil)
107-
if err != nil {
108-
return nil, nil, err
109-
}
110-
111-
all, err := GetResourcesRefFromRelease(rel, opts.Resource.GetNamespace())
112-
if err != nil {
113-
return nil, nil, fmt.Errorf("failed to get resources from release: %w", err)
114-
}
115-
116-
return rel, all, nil
117-
}
118-
119-
func GetResourcesRefFromRelease(rel *release.Release, defaultNamespace string) ([]objectref.ObjectRef, error) {
88+
func GetResourcesRefFromRelease(rel *release.Release, defaultNamespace string, clientset helmclient.CachedClientsInterface) ([]objectref.ObjectRef, error) {
12089
// build an io.Reader that streams manifest + hooks without concatenating into a single []byte
12190
var readers []io.Reader
12291
if rel != nil {
@@ -159,6 +128,16 @@ func GetResourcesRefFromRelease(rel *release.Release, defaultNamespace string) (
159128

160129
if namespace == "" {
161130
namespace = defaultNamespace
131+
132+
// Check if the resource is cluster-scoped
133+
gvk := schema.FromAPIVersionAndKind(apiVersion, kind)
134+
mapping, err := clientset.RESTMapper().RESTMapping(gvk.GroupKind(), gvk.Version)
135+
if err != nil {
136+
return nil, fmt.Errorf("failed to get REST mapping for %s: %w", gvk.String(), err)
137+
}
138+
if mapping.Scope.Name() == meta.RESTScopeNameRoot {
139+
namespace = ""
140+
}
162141
}
163142
// skip helm hooks if present
164143
if hook != "" {
@@ -203,7 +182,6 @@ func CheckResource(ctx context.Context, ref objectref.ObjectRef, opts CheckResou
203182
un, err = opts.DynamicClient.Resource(gvr).
204183
Get(ctx, ref.Name, metav1.GetOptions{})
205184
}
206-
207185
if err != nil {
208186
return nil, err
209187
}
@@ -236,9 +214,7 @@ func FindAllReleases(hc helmclient.Client) ([]*release.Release, error) {
236214
}
237215

238216
res := make([]*release.Release, 0, len(all))
239-
for _, el := range all {
240-
res = append(res, el)
241-
}
217+
res = append(res, all...)
242218

243219
return res, nil
244220
}

0 commit comments

Comments
 (0)