Skip to content

Commit 7c0faaf

Browse files
committed
Add Cilium as an CNI option in tests
Signed-off-by: peppi-lotta <[email protected]>
1 parent ed36c0f commit 7c0faaf

File tree

7 files changed

+490
-138
lines changed

7 files changed

+490
-138
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ require (
4545
github.com/go-logr/stdr v1.2.2 // indirect
4646
github.com/go-logr/zapr v1.3.0 // indirect
4747
github.com/go-openapi/jsonpointer v0.21.0 // indirect
48-
github.com/go-openapi/jsonreference v0.20.2 // indirect
48+
github.com/go-openapi/jsonreference v0.21.0 // indirect
4949
github.com/go-openapi/swag v0.23.0 // indirect
5050
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
5151
github.com/gobuffalo/flect v1.0.3 // indirect

go.sum

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+
2727
github.com/coredns/corefile-migration v1.0.28 h1:O8YafUREqUcGbRtcJfOmWU6ifcw2HX76I1QvI5xZpsw=
2828
github.com/coredns/corefile-migration v1.0.28/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY=
2929
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
30-
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
3130
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3231
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3332
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@@ -59,12 +58,10 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
5958
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
6059
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
6160
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
62-
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
6361
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
6462
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
65-
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
66-
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
67-
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
63+
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
64+
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
6865
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
6966
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
7067
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
@@ -111,11 +108,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
111108
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
112109
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
113110
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
114-
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
115111
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
116112
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
117-
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
118-
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
119113
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
120114
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
121115
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=

test/e2e/config/e2e_conf.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ providers:
243243
targetName: "cluster-template-opensuse-leap.yaml"
244244

245245
variables:
246-
CNI: "/tmp/calico.yaml"
246+
CNI: "/tmp/cni.yaml"
247247
KUBETEST_CONFIGURATION: "./data/kubetest/conformance.yaml"
248248
KUBERNETES_VERSION: "${KUBERNETES_VERSION:-v1.34.1}"
249249
KUBERNETES_VERSION_MINOR: "${KUBERNETES_VERSION%.*}"
@@ -274,8 +274,11 @@ variables:
274274
UPGRADED_BMO_IMAGE_TAG: "${UPGRADED_BMO_IMAGE_TAG:-main}"
275275

276276
PROVIDER_ID_FORMAT: "metal3://{{ ds.meta_data.providerid }}"
277+
CNI_NAME: "${CNI_NAME:-calico}"
277278
# Pin Calico version
278279
CALICO_VERSION: "${CALICO_VERSION:-v3.30.3}"
280+
# Pin Cilium version
281+
CILIUM_VERSION: "${CILIUM_VERSION:-v1.18.0}"
279282
# Pin CertManager for upgrade tests
280283
CERT_MANAGER_RELEASE: v1.17.1
281284
# Default vars for the template, those values could be overridden by the env-vars.

test/e2e/e2e_suite_test.go

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111
"testing"
1212

13+
"github.com/blang/semver/v4"
1314
"github.com/jinzhu/copier"
1415
bmov1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
1516
infrav1 "github.com/metal3-io/cluster-api-provider-metal3/api/v1beta1"
@@ -18,6 +19,7 @@ import (
1819
. "github.com/onsi/ginkgo/v2"
1920
. "github.com/onsi/gomega"
2021
"gopkg.in/yaml.v3"
22+
"helm.sh/helm/v3/pkg/cli"
2123
"k8s.io/apimachinery/pkg/runtime"
2224
"k8s.io/klog/v2"
2325
clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1"
@@ -191,13 +193,22 @@ func CreateClusterctlLocalRepository(config *clusterctl.E2EConfig, repositoryFol
191193

192194
// Ensuring a CNI file is defined in the config and register a FileTransformation to inject the referenced file as in place of the CNI_RESOURCES envSubst variable.
193195
Expect(config.Variables).To(HaveKey(capi_e2e.CNIPath), "Missing %s variable in the config", capi_e2e.CNIPath)
196+
cniName := config.MustGetVariable("CNI_NAME")
194197
cniPath := config.MustGetVariable(capi_e2e.CNIPath)
195-
switch osType {
196-
case osTypeLeap:
197-
updateCalico(config, cniPath, "eth1")
198+
cniInterface := "enp2s0"
199+
if osType == osTypeLeap {
200+
cniInterface = "eth1"
201+
}
202+
203+
switch cniName {
204+
case "cilium":
205+
updateCilium(config, cniPath)
206+
case "calico":
207+
updateCalico(config, cniPath, cniInterface)
198208
default:
199-
updateCalico(config, cniPath, "enp2s0")
209+
Expect(cniName).To(Or(Equal("calico"), Equal("cilium")), "Invalid CNI type %q, only 'cilium' and 'calico' are supported", cniName)
200210
}
211+
201212
Expect(cniPath).To(BeAnExistingFile(), "The %s variable should resolve to an existing file", capi_e2e.CNIPath)
202213
createRepositoryInput.RegisterClusterResourceSetConfigMapTransformation(cniPath, capi_e2e.CNIResources)
203214

@@ -300,7 +311,7 @@ func updateCalico(config *clusterctl.E2EConfig, calicoYaml, calicoInterface stri
300311
err = copier.CopyWithOption(addItem, calicoNodeContainerEnvs.Content[0], copier.Option{IgnoreEmpty: true, DeepCopy: true})
301312
Expect(err).ToNot(HaveOccurred())
302313
addItem.Content[1].SetString("IP_AUTODETECTION_METHOD")
303-
addItem.Content[3].SetString("interface=" + calicoInterface)
314+
addItem.Content[3].SetString("interface=" + cniInterface)
304315
addItem.HeadComment = "Start section modified by CAPM3 e2e test framework"
305316
addItem.FootComment = "End section modified by CAPM3 e2e test framework"
306317
calicoNodeContainerEnvs.Content = append(calicoNodeContainerEnvs.Content, addItem)
@@ -312,6 +323,53 @@ func updateCalico(config *clusterctl.E2EConfig, calicoYaml, calicoInterface stri
312323
Expect(err).ToNot(HaveOccurred(), "Cannot print out the update to the file")
313324
}
314325

326+
// updateCilium generates and writes a Cilium CNI manifest to the CNI path specified in e2e config.
327+
// It retrieves the Cilium version from e2e configuration, downloads the corresponding Helm chart, generates a manifest from the chart template, and writes the manifest to the CNI path.
328+
func updateCilium(config *clusterctl.E2EConfig, cniPath string) {
329+
ctx = context.Background()
330+
ciliumVersion := e2eConfig.MustGetVariable("CILIUM_VERSION")
331+
if ciliumVersion[0] == 'v' {
332+
ciliumVersion = ciliumVersion[1:]
333+
}
334+
settings := cli.New()
335+
settings.SetNamespace("kube-system")
336+
helmDriver := os.Getenv("HELM_DRIVER")
337+
opts := HelmOpts{
338+
Logger: log.Default(),
339+
Settings: settings,
340+
ReleaseName: "cilium",
341+
ChartRef: fmt.Sprintf("https://helm.cilium.io/cilium-%s.tgz", ciliumVersion),
342+
ChartLocation: fmt.Sprintf("/tmp/cilium-%s.tgz", ciliumVersion),
343+
ReleaseVersion: semver.MustParse(ciliumVersion),
344+
Driver: helmDriver,
345+
}
346+
347+
manifestOverwriteValues := map[string]interface{}{
348+
"operator": map[string]interface{}{
349+
"replicas": 1,
350+
"updateStrategy": map[string]interface{}{
351+
"rollingUpdate": map[string]interface{}{
352+
"maxUnavailable": "100%",
353+
},
354+
},
355+
},
356+
}
357+
358+
manifest, err := generateTemplateFromHelmChart(ctx, opts, manifestOverwriteValues, e2eConfig)
359+
Expect(err).ToNot(HaveOccurred(), "failed to generate template: %v", err)
360+
361+
// Replace ${BIN_PATH} with /opt/cni/bin. This is done to prevent
362+
// framework.RegisterClusterResourceSetConfigMapTransformation from throwing
363+
// an error due to unresolvable "envsubst" variable.
364+
manifest = strings.ReplaceAll(manifest, "${BIN_PATH}", "/opt/cni/bin")
365+
366+
containerRegistry := config.MustGetVariable("CONTAINER_REGISTRY")
367+
manifest = strings.ReplaceAll(manifest, "quay.io", containerRegistry)
368+
369+
err = os.WriteFile(cniPath, []byte(manifest), 0600)
370+
Expect(err).ToNot(HaveOccurred(), "Failed to write Cilium manifest to file: %v", err)
371+
}
372+
315373
// createBMHsInNamespace is a hook function that can be called after creating
316374
// a namespace, it creates the needed bmhs in the namespace hosting the cluster.
317375
func createBMHsInNamespace(clusterProxy framework.ClusterProxy, clusterNamespace string) {

test/e2e/helm_helper.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package e2e
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
8+
"github.com/blang/semver/v4"
9+
"helm.sh/helm/v3/pkg/action"
10+
"helm.sh/helm/v3/pkg/chart/loader"
11+
"helm.sh/helm/v3/pkg/chartutil"
12+
"helm.sh/helm/v3/pkg/cli"
13+
"helm.sh/helm/v3/pkg/downloader"
14+
"helm.sh/helm/v3/pkg/getter"
15+
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
16+
)
17+
18+
type HelmOpts struct {
19+
Logger *log.Logger
20+
Settings *cli.EnvSettings
21+
ReleaseName string
22+
ChartRef string
23+
ChartLocation string // Added to specify the chart location
24+
ReleaseVersion semver.Version
25+
Driver string
26+
}
27+
28+
func generateTemplateFromHelmChart(ctx context.Context, opts HelmOpts, manifestOverwriteValues map[string]interface{}, e2econfig *clusterctl.E2EConfig) (string, error) {
29+
actionConfig, err := initActionConfig(opts)
30+
if err != nil {
31+
return "", fmt.Errorf("failed to init action config: %w", err)
32+
}
33+
34+
kubeversion, err := chartutil.ParseKubeVersion(e2econfig.MustGetVariable("KUBERNETES_VERSION"))
35+
if err != nil {
36+
return "", fmt.Errorf("failed to parse kube version: %w", err)
37+
}
38+
39+
pullClient := action.NewPullWithOpts(
40+
action.WithConfig(actionConfig))
41+
pullClient.DestDir = "/tmp/"
42+
pullClient.Settings = opts.Settings
43+
pullClient.Version = opts.ReleaseVersion.String()
44+
45+
_, err = pullClient.Run(opts.ChartRef)
46+
if err != nil {
47+
return "", fmt.Errorf("failed to pull chart: %w", err)
48+
}
49+
50+
installClient := action.NewInstall(actionConfig)
51+
installClient.DryRun = true
52+
installClient.ClientOnly = true
53+
installClient.Replace = true
54+
installClient.ReleaseName = opts.ReleaseName
55+
installClient.Namespace = opts.Settings.Namespace()
56+
installClient.Version = opts.ReleaseVersion.String()
57+
installClient.KubeVersion = kubeversion
58+
59+
chartPath, err := installClient.ChartPathOptions.LocateChart(opts.ChartLocation, opts.Settings)
60+
if err != nil {
61+
return "", err
62+
}
63+
64+
providers := getter.All(opts.Settings)
65+
66+
chart, err := loader.Load(chartPath)
67+
if err != nil {
68+
return "", err
69+
}
70+
71+
// Check chart dependencies
72+
if chartDependencies := chart.Metadata.Dependencies; chartDependencies != nil {
73+
if err = action.CheckDependencies(chart, chartDependencies); err != nil {
74+
err = fmt.Errorf("failed to check chart dependencies: %w", err)
75+
if !installClient.DependencyUpdate {
76+
return "", err
77+
}
78+
79+
manager := &downloader.Manager{
80+
Out: opts.Logger.Writer(),
81+
ChartPath: chartPath,
82+
Keyring: installClient.ChartPathOptions.Keyring,
83+
SkipUpdate: false,
84+
Getters: providers,
85+
RepositoryConfig: opts.Settings.RepositoryConfig,
86+
RepositoryCache: opts.Settings.RepositoryCache,
87+
Debug: opts.Settings.Debug,
88+
RegistryClient: installClient.GetRegistryClient(),
89+
}
90+
if err = manager.Update(); err != nil {
91+
return "", err
92+
}
93+
// Reload the chart with the updated Chart.lock file.
94+
if chart, err = loader.Load(chartPath); err != nil {
95+
return "", fmt.Errorf("failed to reload chart after repo update: %w", err)
96+
}
97+
}
98+
}
99+
100+
release, err := installClient.RunWithContext(ctx, chart, manifestOverwriteValues)
101+
if err != nil {
102+
return "", fmt.Errorf("failed to run install: %w", err)
103+
}
104+
105+
return release.Manifest, nil
106+
}
107+
108+
func initActionConfig(opts HelmOpts) (*action.Configuration, error) {
109+
return initActionConfigList(opts, false)
110+
}
111+
112+
func initActionConfigList(opts HelmOpts, allNamespaces bool) (*action.Configuration, error) {
113+
actionConfig := new(action.Configuration)
114+
namespace := func() string {
115+
// For list action, you can pass an empty string instead of settings.Namespace() to list
116+
// all namespaces
117+
if allNamespaces {
118+
return ""
119+
}
120+
return opts.Settings.Namespace()
121+
}()
122+
123+
if err := actionConfig.Init(
124+
opts.Settings.RESTClientGetter(),
125+
namespace,
126+
opts.Driver,
127+
opts.Logger.Printf); err != nil {
128+
return nil, err
129+
}
130+
131+
return actionConfig, nil
132+
}

0 commit comments

Comments
 (0)