Skip to content

Commit e9c68f4

Browse files
Merge branch 'main' into diamonwiggins/sc-123404/mount-ca-into-kotsadm-containers
2 parents bf12e64 + 8d0a1e3 commit e9c68f4

File tree

11 files changed

+254
-36
lines changed

11 files changed

+254
-36
lines changed

operator/charts/embedded-cluster-operator/templates/embedded-cluster-operator-deployment.yaml

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,6 @@ spec:
6464
value: {{ .Values.utilsImage }}
6565
- name: EMBEDDEDCLUSTER_IMAGE
6666
value: {{ printf "%s:%s" .Values.image.repository .Values.image.tag | quote }}
67-
{{- if .Values.privateCAs.enabled }}
68-
- name: SSL_CERT_DIR
69-
value: /certs
70-
{{- end }}
7167
name: manager
7268
{{- if .Values.livenessProbe }}
7369
livenessProbe:
@@ -77,11 +73,10 @@ spec:
7773
readinessProbe:
7874
{{ toYaml .Values.readinessProbe | indent 10 }}
7975
{{- end }}
80-
{{- if .Values.privateCAs.enabled }}
76+
{{- if .Values.extraVolumeMounts }}
8177
volumeMounts:
82-
- mountPath: /certs
83-
name: private-cas
84-
{{- end }}
78+
{{- toYaml .Values.extraVolumeMounts | nindent 8 }}
79+
{{- end }}
8580
{{- if .Values.resources }}
8681
resources:
8782
{{ toYaml .Values.resources | indent 10 }}
@@ -95,10 +90,7 @@ spec:
9590
runAsNonRoot: true
9691
serviceAccountName: {{ include "embedded-cluster-operator.serviceAccountName" $ | trunc 63 | trimAll "-"}}
9792
terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}
98-
{{- if .Values.privateCAs.enabled }}
93+
{{- if .Values.extraVolumes }}
9994
volumes:
100-
- configMap:
101-
name: "{{ .Values.privateCAs.configmapName }}"
102-
optional: true
103-
name: private-cas
104-
{{- end }}
95+
{{- toYaml .Values.extraVolumes | nindent 6 }}
96+
{{- end }}

pkg/addons/embeddedclusteroperator/embeddedclusteroperator.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,44 @@ import (
66

77
"github.com/pkg/errors"
88
ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1"
9+
"github.com/replicatedhq/embedded-cluster/pkg/kubeutils"
910
"github.com/replicatedhq/embedded-cluster/pkg/release"
1011
"github.com/replicatedhq/embedded-cluster/pkg/versions"
1112
"gopkg.in/yaml.v3"
13+
"k8s.io/apimachinery/pkg/runtime"
14+
jsonserializer "k8s.io/apimachinery/pkg/runtime/serializer/json"
1215
)
1316

17+
var (
18+
serializer runtime.Serializer
19+
)
20+
21+
func init() {
22+
scheme := kubeutils.Scheme
23+
serializer = jsonserializer.NewSerializerWithOptions(jsonserializer.DefaultMetaFactory, scheme, scheme, jsonserializer.SerializerOptions{
24+
Yaml: true,
25+
})
26+
}
27+
1428
type EmbeddedClusterOperator struct {
15-
IsAirgap bool
16-
Proxy *ecv1beta1.ProxySpec
29+
IsAirgap bool
30+
Proxy *ecv1beta1.ProxySpec
31+
// Deprecated: use HostCABundlePath instead
1732
PrivateCAs []string
33+
HostCABundlePath string
1834
ChartLocationOverride string
1935
ChartVersionOverride string
2036
ImageRepoOverride string
2137
ImageTagOverride string
2238
UtilsImageOverride string
2339
ProxyRegistryDomain string
40+
41+
// DryRun is a flag to enable dry-run mode for Velero.
42+
// If true, Velero will only render the helm template and additional manifests, but not install
43+
// the release.
44+
DryRun bool
45+
46+
dryRunManifests [][]byte
2447
}
2548

2649
const (
@@ -88,6 +111,10 @@ func (e *EmbeddedClusterOperator) ChartVersion() string {
88111
return Metadata.Version
89112
}
90113

114+
func (v *EmbeddedClusterOperator) DryRunManifests() [][]byte {
115+
return v.dryRunManifests
116+
}
117+
91118
func getBackupLabels() map[string]string {
92119
return map[string]string{
93120
"replicated.com/disaster-recovery": "infra",

pkg/addons/embeddedclusteroperator/install.go

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package embeddedclusteroperator
22

33
import (
4+
"bytes"
45
"context"
56
"fmt"
67
"os"
@@ -14,7 +15,7 @@ import (
1415
)
1516

1617
func (e *EmbeddedClusterOperator) Install(ctx context.Context, kcli client.Client, hcli helm.Client, overrides []string, writer *spinner.MessageWriter) error {
17-
err := installEnsureCAConfigmap(ctx, kcli, e.PrivateCAs)
18+
err := e.installEnsureCAConfigmap(ctx, kcli)
1819
if err != nil {
1920
return errors.Wrap(err, "ensure CA configmap")
2021
}
@@ -24,31 +25,41 @@ func (e *EmbeddedClusterOperator) Install(ctx context.Context, kcli client.Clien
2425
return errors.Wrap(err, "generate helm values")
2526
}
2627

27-
_, err = hcli.Install(ctx, helm.InstallOptions{
28+
opts := helm.InstallOptions{
2829
ReleaseName: releaseName,
2930
ChartPath: e.ChartLocation(),
3031
ChartVersion: e.ChartVersion(),
3132
Values: values,
3233
Namespace: namespace,
3334
Labels: getBackupLabels(),
34-
})
35-
if err != nil {
36-
return errors.Wrap(err, "helm install")
35+
}
36+
37+
if e.DryRun {
38+
manifests, err := hcli.Render(ctx, opts)
39+
if err != nil {
40+
return errors.Wrap(err, "dry run values")
41+
}
42+
e.dryRunManifests = append(e.dryRunManifests, manifests...)
43+
} else {
44+
_, err = hcli.Install(ctx, opts)
45+
if err != nil {
46+
return errors.Wrap(err, "helm install")
47+
}
3748
}
3849

3950
return nil
4051
}
4152

42-
func installEnsureCAConfigmap(ctx context.Context, kcli client.Client, privateCAs []string) error {
43-
cas, err := privateCAsToMap(privateCAs)
53+
func (e *EmbeddedClusterOperator) installEnsureCAConfigmap(ctx context.Context, kcli client.Client) error {
54+
cas, err := privateCAsToMap(e.PrivateCAs)
4455
if err != nil {
4556
return errors.Wrap(err, "create private cas map")
4657
}
47-
return ensureCAConfigmap(ctx, kcli, cas)
58+
return e.ensureCAConfigmap(ctx, kcli, cas)
4859
}
4960

50-
func ensureCAConfigmap(ctx context.Context, cli client.Client, cas map[string]string) error {
51-
ecoCAConfigmap := corev1.ConfigMap{
61+
func (e *EmbeddedClusterOperator) ensureCAConfigmap(ctx context.Context, cli client.Client, cas map[string]string) error {
62+
obj := &corev1.ConfigMap{
5263
TypeMeta: metav1.TypeMeta{
5364
Kind: "ConfigMap",
5465
APIVersion: "v1",
@@ -61,7 +72,16 @@ func ensureCAConfigmap(ctx context.Context, cli client.Client, cas map[string]st
6172
Data: cas,
6273
}
6374

64-
err := cli.Create(ctx, &ecoCAConfigmap)
75+
if e.DryRun {
76+
b := bytes.NewBuffer(nil)
77+
if err := serializer.Encode(obj, b); err != nil {
78+
return errors.Wrap(err, "serialize")
79+
}
80+
e.dryRunManifests = append(e.dryRunManifests, b.Bytes())
81+
return nil
82+
}
83+
84+
err := cli.Create(ctx, obj)
6585
if client.IgnoreAlreadyExists(err) != nil {
6686
return errors.Wrap(err, "create private-cas configmap")
6787
}

pkg/addons/embeddedclusteroperator/install_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,13 @@ func Test_installEnsureCAConfigmap(t *testing.T) {
6767
cli := builder.Build()
6868

6969
// Call the function under test
70-
privateCAs := []string{
71-
filepath.Join(tmpDir, "ca_0.crt"),
72-
filepath.Join(tmpDir, "ca_1.crt"),
70+
e := &EmbeddedClusterOperator{
71+
PrivateCAs: []string{
72+
filepath.Join(tmpDir, "ca_0.crt"),
73+
filepath.Join(tmpDir, "ca_1.crt"),
74+
},
7375
}
74-
err := installEnsureCAConfigmap(context.Background(), cli, privateCAs)
76+
err := e.installEnsureCAConfigmap(context.Background(), cli)
7577

7678
// Check error expectations
7779
if tt.expectError {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package integration
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"path/filepath"
7+
"strings"
8+
"testing"
9+
10+
"github.com/replicatedhq/embedded-cluster/pkg/addons/embeddedclusteroperator"
11+
"github.com/replicatedhq/embedded-cluster/pkg/helm"
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
14+
appsv1 "k8s.io/api/apps/v1"
15+
corev1 "k8s.io/api/core/v1"
16+
"k8s.io/utils/ptr"
17+
"sigs.k8s.io/yaml"
18+
)
19+
20+
func TestHostCABundle(t *testing.T) {
21+
chartLocation, err := filepath.Abs("../../../../operator/charts/embedded-cluster-operator")
22+
require.NoError(t, err, "Failed to get chart location")
23+
24+
addon := &embeddedclusteroperator.EmbeddedClusterOperator{
25+
DryRun: true,
26+
ChartLocationOverride: chartLocation,
27+
HostCABundlePath: "/etc/ssl/certs/ca-certificates.crt",
28+
}
29+
30+
fmt.Println(addon.ChartLocation())
31+
32+
hcli, err := helm.NewClient(helm.HelmOptions{})
33+
require.NoError(t, err, "NewClient should not return an error")
34+
35+
err = addon.Install(context.Background(), nil, hcli, nil, nil)
36+
require.NoError(t, err, "embeddedclusteroperator.Install should not return an error")
37+
38+
manifests := addon.DryRunManifests()
39+
require.NotEmpty(t, manifests, "DryRunManifests should not be empty")
40+
41+
var deployment *appsv1.Deployment
42+
for _, manifest := range manifests {
43+
if strings.Contains(string(manifest), "# Source: embedded-cluster-operator/templates/embedded-cluster-operator-deployment.yaml") {
44+
err := yaml.Unmarshal(manifest, &deployment)
45+
require.NoError(t, err, "Failed to unmarshal EmbeddedClusterOperator deployment")
46+
}
47+
}
48+
49+
require.NotNil(t, deployment, "EmbeddedClusterOperator deployment should not be nil")
50+
51+
var volume *corev1.Volume
52+
for _, v := range deployment.Spec.Template.Spec.Volumes {
53+
if v.Name == "host-ca-bundle" {
54+
volume = &v
55+
}
56+
}
57+
if assert.NotNil(t, volume, "EmbeddedClusterOperator host-ca-bundle volume should not be nil") {
58+
assert.Equal(t, volume.VolumeSource.HostPath.Path, "/etc/ssl/certs/ca-certificates.crt")
59+
assert.Equal(t, volume.VolumeSource.HostPath.Type, ptr.To(corev1.HostPathFileOrCreate))
60+
}
61+
62+
var volumeMount *corev1.VolumeMount
63+
for _, v := range deployment.Spec.Template.Spec.Containers[0].VolumeMounts {
64+
if v.Name == "host-ca-bundle" {
65+
volumeMount = &v
66+
}
67+
}
68+
if assert.NotNil(t, volumeMount, "EmbeddedClusterOperator host-ca-bundle volume mount should not be nil") {
69+
assert.Equal(t, volumeMount.MountPath, "/certs/ca-certificates.crt")
70+
}
71+
72+
// Check the SSL_CERT_DIR environment variable is set correctly
73+
var sslCertDirEnv *corev1.EnvVar
74+
for _, env := range deployment.Spec.Template.Spec.Containers[0].Env {
75+
if env.Name == "SSL_CERT_DIR" {
76+
sslCertDirEnv = &env
77+
}
78+
}
79+
if assert.NotNil(t, sslCertDirEnv, "SSL_CERT_DIR environment variable should not be nil") {
80+
assert.Equal(t, sslCertDirEnv.Value, "/certs")
81+
}
82+
}

pkg/addons/embeddedclusteroperator/upgrade.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,6 @@ func UpgradeEnsureCAConfigmap(ctx context.Context, kcli client.Client) error {
6666
return errors.Wrap(err, "get kotsadm-private-cas configmap")
6767
}
6868

69-
return ensureCAConfigmap(ctx, kcli, cm.Data)
69+
addon := &EmbeddedClusterOperator{}
70+
return addon.ensureCAConfigmap(ctx, kcli, cm.Data)
7071
}

pkg/addons/embeddedclusteroperator/values.go

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,12 @@ func (e *EmbeddedClusterOperator) GenerateHelmValues(ctx context.Context, kcli c
4343
copiedValues["isAirgap"] = "true"
4444
}
4545

46+
extraEnvVars := []map[string]any{}
47+
extraVolumes := []map[string]any{}
48+
extraVolumeMounts := []map[string]any{}
49+
4650
if e.Proxy != nil {
47-
copiedValues["extraEnv"] = []map[string]interface{}{
51+
extraEnvVars = append(extraEnvVars, []map[string]any{
4852
{
4953
"name": "HTTP_PROXY",
5054
"value": e.Proxy.HTTPProxy,
@@ -57,11 +61,33 @@ func (e *EmbeddedClusterOperator) GenerateHelmValues(ctx context.Context, kcli c
5761
"name": "NO_PROXY",
5862
"value": e.Proxy.NoProxy,
5963
},
60-
}
61-
} else {
62-
delete(copiedValues, "extraEnv")
64+
}...)
65+
}
66+
67+
if e.HostCABundlePath != "" {
68+
extraVolumes = append(extraVolumes, map[string]any{
69+
"name": "host-ca-bundle",
70+
"hostPath": map[string]any{
71+
"path": e.HostCABundlePath,
72+
"type": "FileOrCreate",
73+
},
74+
})
75+
76+
extraVolumeMounts = append(extraVolumeMounts, map[string]any{
77+
"name": "host-ca-bundle",
78+
"mountPath": "/certs/ca-certificates.crt",
79+
})
80+
81+
extraEnvVars = append(extraEnvVars, map[string]any{
82+
"name": "SSL_CERT_DIR",
83+
"value": "/certs",
84+
})
6385
}
6486

87+
copiedValues["extraEnv"] = extraEnvVars
88+
copiedValues["extraVolumes"] = extraVolumes
89+
copiedValues["extraVolumeMounts"] = extraVolumeMounts
90+
6591
for _, override := range overrides {
6692
var err error
6793
copiedValues, err = helm.PatchValues(copiedValues, override)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package embeddedclusteroperator
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestGenerateHelmValues_HostCABundlePath(t *testing.T) {
12+
e := &EmbeddedClusterOperator{
13+
HostCABundlePath: "/etc/ssl/certs/ca-certificates.crt",
14+
}
15+
16+
values, err := e.GenerateHelmValues(context.Background(), nil, nil)
17+
require.NoError(t, err, "GenerateHelmValues should not return an error")
18+
19+
require.NotEmpty(t, values["extraVolumes"])
20+
require.IsType(t, []map[string]any{}, values["extraVolumes"])
21+
require.Len(t, values["extraVolumes"], 1)
22+
23+
require.NotEmpty(t, values["extraVolumeMounts"])
24+
require.IsType(t, []map[string]any{}, values["extraVolumeMounts"])
25+
require.Len(t, values["extraVolumeMounts"], 1)
26+
27+
require.NotEmpty(t, values["extraEnv"])
28+
require.IsType(t, []map[string]any{}, values["extraEnv"])
29+
30+
// Find the SSL_CERT_DIR environment variable
31+
var sslCertDirFound bool
32+
for _, env := range values["extraEnv"].([]map[string]any) {
33+
if env["name"] == "SSL_CERT_DIR" {
34+
assert.Equal(t, "/certs", env["value"])
35+
sslCertDirFound = true
36+
break
37+
}
38+
}
39+
assert.True(t, sslCertDirFound, "SSL_CERT_DIR environment variable should be present")
40+
41+
extraVolume := values["extraVolumes"].([]map[string]any)[0]
42+
assert.Equal(t, "host-ca-bundle", extraVolume["name"])
43+
if assert.NotNil(t, extraVolume["hostPath"]) {
44+
assert.Equal(t, "/etc/ssl/certs/ca-certificates.crt", extraVolume["hostPath"].(map[string]any)["path"])
45+
assert.Equal(t, "FileOrCreate", extraVolume["hostPath"].(map[string]any)["type"])
46+
}
47+
48+
extraVolumeMount := values["extraVolumeMounts"].([]map[string]any)[0]
49+
assert.Equal(t, "host-ca-bundle", extraVolumeMount["name"])
50+
assert.Equal(t, "/certs/ca-certificates.crt", extraVolumeMount["mountPath"])
51+
}

0 commit comments

Comments
 (0)