Skip to content

Commit eb32425

Browse files
Populate resources and apply from cache
Signed-off-by: Danil-Grigorev <[email protected]>
1 parent ad061b0 commit eb32425

File tree

3 files changed

+241
-72
lines changed

3 files changed

+241
-72
lines changed

internal/controller/genericprovider_controller.go

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"errors"
2424
"fmt"
2525
"hash"
26-
"strings"
2726

2827
corev1 "k8s.io/api/core/v1"
2928
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -33,6 +32,8 @@ import (
3332
operatorv1 "sigs.k8s.io/cluster-api-operator/api/v1alpha2"
3433
"sigs.k8s.io/cluster-api-operator/internal/controller/genericprovider"
3534
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
35+
configclient "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
36+
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/yamlprocessor"
3637
"sigs.k8s.io/cluster-api/util/conditions"
3738
"sigs.k8s.io/cluster-api/util/patch"
3839
ctrl "sigs.k8s.io/controller-runtime"
@@ -360,7 +361,7 @@ func calculateHash(ctx context.Context, k8sClient client.Client, provider generi
360361
func applyFromCache(ctx context.Context, cl client.Client, provider genericprovider.GenericProvider) (bool, error) {
361362
log := log.FromContext(ctx)
362363

363-
configMap, err := providerConfigMap(ctx, cl, provider)
364+
configMap, err := providerCacheConfigMap(ctx, cl, provider)
364365
if err != nil {
365366
log.Error(err, "failed to get provider config map")
366367

@@ -393,37 +394,81 @@ func applyFromCache(ctx context.Context, cl client.Client, provider genericprovi
393394
return false, nil
394395
}
395396

396-
components, err := getComponentsData(*configMap)
397-
if err != nil {
398-
log.Error(err, "failed to get provider components")
397+
log.Info("Applying provider configuration from cache")
398+
errs := []error{}
399+
400+
mr := configclient.NewMemoryReader()
399401

402+
if err := mr.Init(ctx, ""); err != nil {
400403
return false, err
401404
}
402405

403-
additionalManifests, err := fetchAdditionalManifests(ctx, cl, provider)
404-
if err != nil {
405-
log.Error(err, "failed to get additional manifests")
406+
// Fetch configuration variables from the secret. See API field docs for more info.
407+
initReaderVariables(ctx, cl, mr, provider)
406408

407-
return false, err
408-
}
409+
processor := yamlprocessor.NewSimpleProcessor()
410+
for _, manifest := range configMap.Data {
411+
manifest, err := processor.Process([]byte(manifest), mr.Get)
412+
if err != nil {
413+
log.Error(err, "failed to process manifest")
409414

410-
if additionalManifests != "" {
411-
components = components + "\n---\n" + additionalManifests
412-
}
415+
return false, err
416+
}
413417

414-
for _, manifests := range strings.Split(components, "---") {
415-
manifests, err := utilyaml.ToUnstructured([]byte(manifests))
418+
manifests, err := utilyaml.ToUnstructured(manifest)
416419
if err != nil {
417420
log.Error(err, "failed to convert yaml to unstructured")
418421

419422
return false, err
420423
}
421424

425+
if len(manifest) > 1 {
426+
return false, fmt.Errorf("multiple manifests found: %d", len(manifests))
427+
} else if len(manifests) == 0 {
428+
continue
429+
}
430+
422431
if err := cl.Patch(ctx, &manifests[0], client.Apply, client.ForceOwnership, client.FieldOwner(cacheOwner)); err != nil {
423-
log.Error(err, "failed to apply object from cache")
432+
errs = append(errs, err)
433+
}
434+
}
424435

425-
return false, nil
436+
for _, binaryManifest := range configMap.BinaryData {
437+
manifest, err := decompressYaml(binaryManifest)
438+
if err != nil {
439+
log.Error(err, "failed to decompress yaml")
440+
441+
return false, err
426442
}
443+
444+
manifest, err = processor.Process([]byte(manifest), mr.Get)
445+
if err != nil {
446+
log.Error(err, "failed to process manifest")
447+
448+
return false, err
449+
}
450+
manifests, err := utilyaml.ToUnstructured([]byte(manifest))
451+
if err != nil {
452+
log.Error(err, "failed to convert yaml to unstructured")
453+
454+
return false, err
455+
}
456+
457+
if len(manifest) > 1 {
458+
return false, fmt.Errorf("multiple manifests found: %d", len(manifests))
459+
} else if len(manifests) == 0 {
460+
continue
461+
}
462+
463+
if err := cl.Patch(ctx, &manifests[0], client.Apply, client.ForceOwnership, client.FieldOwner(cacheOwner)); err != nil {
464+
errs = append(errs, err)
465+
}
466+
}
467+
468+
if err := kerrors.NewAggregate(errs); err != nil {
469+
log.Error(err, "failed to apply objects from cache")
470+
471+
return false, err
427472
}
428473

429474
log.Info("Applied all objects from cache")
@@ -433,7 +478,7 @@ func applyFromCache(ctx context.Context, cl client.Client, provider genericprovi
433478

434479
// setCacheHash calculates current provider and configMap hash, and updates it on the configMap.
435480
func setCacheHash(ctx context.Context, cl client.Client, provider genericprovider.GenericProvider) error {
436-
configMap, err := providerConfigMap(ctx, cl, provider)
481+
configMap, err := providerCacheConfigMap(ctx, cl, provider)
437482
if err != nil {
438483
return err
439484
}

internal/controller/manifests_downloader.go

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import (
2121
"compress/gzip"
2222
"context"
2323
"fmt"
24+
"io"
2425

2526
corev1 "k8s.io/api/core/v1"
27+
apierrors "k8s.io/apimachinery/pkg/api/errors"
2628
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2729
"k8s.io/apimachinery/pkg/labels"
2830
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
@@ -40,6 +42,11 @@ const (
4042
configMapSourceLabel = "provider.cluster.x-k8s.io/source"
4143
configMapSourceAnnotation = "provider.cluster.x-k8s.io/source"
4244
operatorManagedLabel = "managed-by.operator.cluster.x-k8s.io"
45+
operatorCacheLabel = "cached-by.operator.cluster.x-k8s.io"
46+
47+
cacheVersionLabelName = "provider-cache.cluster.x-k8s.io/version"
48+
cacheTypeLabel = "provider-cache.cluster.x-k8s.io/type"
49+
cacheNameLabel = "provider-cache.cluster.x-k8s.io/name"
4350

4451
maxConfigMapSize = 1 * 1024 * 1024
4552
ociSource = "oci"
@@ -188,17 +195,10 @@ func TemplateManifestsConfigMap(provider operatorv1.GenericProvider, labels map[
188195
configMap.Data[operatorv1.ComponentsConfigMapKey] = string(components)
189196
} else {
190197
var componentsBuf bytes.Buffer
191-
zw := gzip.NewWriter(&componentsBuf)
192-
193-
_, err := zw.Write(components)
194-
if err != nil {
198+
if err := compressYaml(&componentsBuf, components); err != nil {
195199
return nil, fmt.Errorf("cannot compress data for provider %s/%s: %w", provider.GetNamespace(), provider.GetName(), err)
196200
}
197201

198-
if err := zw.Close(); err != nil {
199-
return nil, err
200-
}
201-
202202
configMap.BinaryData = map[string][]byte{
203203
operatorv1.ComponentsConfigMapKey: componentsBuf.Bytes(),
204204
}
@@ -221,6 +221,41 @@ func TemplateManifestsConfigMap(provider operatorv1.GenericProvider, labels map[
221221
return configMap, nil
222222
}
223223

224+
// compressYaml takes a bytes.Buffer and data, and compresses data into it.
225+
func compressYaml(componentsBuf *bytes.Buffer, data []byte) (err error) {
226+
zw := gzip.NewWriter(componentsBuf)
227+
228+
_, err = zw.Write(data)
229+
defer func() {
230+
err = zw.Close()
231+
}()
232+
233+
if err != nil {
234+
return fmt.Errorf("cannot compress data: %w", err)
235+
}
236+
237+
return
238+
}
239+
240+
// decompressYaml takes a compressed data, and decompresses it.
241+
func decompressYaml(compressedData []byte) (data []byte, err error) {
242+
zr, err := gzip.NewReader(bytes.NewReader(compressedData))
243+
if err != nil {
244+
return nil, fmt.Errorf("cannot open gzip reader from data: %w", err)
245+
}
246+
247+
defer func() {
248+
err = zr.Close()
249+
}()
250+
251+
decompressedData, err := io.ReadAll(zr)
252+
if err != nil {
253+
return nil, fmt.Errorf("cannot decompress data: %w", err)
254+
}
255+
256+
return decompressedData, nil
257+
}
258+
224259
// OCIConfigMap templates config from the OCI source.
225260
func OCIConfigMap(ctx context.Context, provider operatorv1.GenericProvider, auth *auth.Credential) (*corev1.ConfigMap, error) {
226261
store, err := FetchOCI(ctx, provider, auth)
@@ -295,25 +330,16 @@ func providerLabelSelector(provider operatorv1.GenericProvider) *metav1.LabelSel
295330
}
296331
}
297332

298-
// providerConfigMap finds a ConfigMap the given provider label selector.
299-
func providerConfigMap(ctx context.Context, cl client.Client, provider operatorv1.GenericProvider) (*corev1.ConfigMap, error) {
300-
labelSelector := providerLabelSelector(provider)
301-
labelSet := labels.Set(labelSelector.MatchLabels)
302-
listOpts := []client.ListOption{
303-
client.MatchingLabelsSelector{Selector: labels.SelectorFromSet(labelSet)},
304-
client.InNamespace(provider.GetNamespace()),
305-
}
306-
307-
configMapList := &corev1.ConfigMapList{}
308-
if err := cl.List(ctx, configMapList, listOpts...); err != nil {
333+
// providerCacheConfigMap finds a cached ConfigMap the given provider label selector.
334+
func providerCacheConfigMap(ctx context.Context, cl client.Client, provider operatorv1.GenericProvider) (*corev1.ConfigMap, error) {
335+
configMap := &corev1.ConfigMap{}
336+
if err := cl.Get(ctx, client.ObjectKey{Name: ProviderCacheName(provider), Namespace: provider.GetNamespace()}, configMap); apierrors.IsNotFound(err) {
337+
return nil, nil
338+
} else if err != nil {
309339
return nil, fmt.Errorf("failed to list ConfigMaps: %w", err)
310340
}
311341

312-
if len(configMapList.Items) > 1 {
313-
return nil, fmt.Errorf("multiple ConfigMaps found for provider %q", provider.GetName())
314-
}
315-
316-
return &configMapList.Items[0], nil
342+
return configMap, nil
317343
}
318344

319345
// ProviderLabels returns default set of labels that identify a config map with downloaded manifests.
@@ -332,6 +358,12 @@ func ProviderLabels(provider operatorv1.GenericProvider) map[string]string {
332358
return labels
333359
}
334360

361+
// ProviderCacheName generates a cache name for a given provider.
362+
363+
func ProviderCacheName(provider operatorv1.GenericProvider) string {
364+
return fmt.Sprintf("%s-%s-%s-cache", provider.GetType(), provider.GetName(), provider.GetSpec().Version)
365+
}
366+
335367
// needToCompress checks whether the input data exceeds the maximum configmap
336368
// size limit and returns whether it should be compressed.
337369
func needToCompress(bs ...[]byte) bool {

0 commit comments

Comments
 (0)