Skip to content

Commit b816733

Browse files
committed
test/framework: improves the way we inject the debian script
Signed-off-by: Stefan Büringer [email protected] Co-authored-by: Jack Francis [email protected]
1 parent 0452e8b commit b816733

File tree

3 files changed

+157
-114
lines changed

3 files changed

+157
-114
lines changed

test/framework/kubernetesversions/data/debian_injection_script.envsubst.sh renamed to test/framework/kubernetesversions/data/debian_injection_script.envsubst.sh.tpl

+34-20
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ set -o nounset
2222
set -o pipefail
2323
set -o errexit
2424

25+
function retry {
26+
attempt=0
27+
max_attempts=$${1}
28+
interval=$${2}
29+
shift; shift
30+
until [[ $${attempt} -ge "$${max_attempts}" ]] ; do
31+
attempt=$((attempt+1))
32+
set +e
33+
eval "$*" && return || echo "failed $${attempt} times: $*"
34+
set -e
35+
sleep "$${interval}"
36+
done
37+
echo "error: reached max attempts at retry($*)"
38+
return 1
39+
}
40+
2541
[[ $(id -u) != 0 ]] && SUDO="sudo" || SUDO=""
2642

2743
USE_CI_ARTIFACTS=${USE_CI_ARTIFACTS:=false}
@@ -31,19 +47,6 @@ if [ ! "${USE_CI_ARTIFACTS}" = true ]; then
3147
exit 0
3248
fi
3349

34-
GSUTIL=gsutil
35-
36-
if ! command -v $${GSUTIL} >/dev/null; then
37-
apt-get update
38-
apt-get install -y apt-transport-https ca-certificates gnupg curl
39-
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | $${SUDO} tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
40-
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | $${SUDO} apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -
41-
apt-get update
42-
apt-get install -y google-cloud-sdk
43-
fi
44-
45-
$${GSUTIL} version
46-
4750
# This test installs release packages or binaries that are a result of the CI and release builds.
4851
# It runs '... --version' commands to verify that the binaries are correctly installed
4952
# and finally uninstalls the packages.
@@ -62,14 +65,21 @@ if [[ "$${KUBERNETES_VERSION}" != "" ]]; then
6265
CI_DIR=/tmp/k8s-ci
6366
mkdir -p "$${CI_DIR}"
6467
declare -a PACKAGES_TO_TEST=("kubectl" "kubelet" "kubeadm")
68+
{{- if .IsControlPlaneMachine }}
6569
declare -a CONTAINERS_TO_TEST=("kube-apiserver" "kube-controller-manager" "kube-proxy" "kube-scheduler")
70+
{{- else }}
71+
declare -a CONTAINERS_TO_TEST=("kube-proxy")
72+
{{- end }}
6673
CONTAINER_EXT="tar"
6774
echo "* testing CI version $${KUBERNETES_VERSION}"
6875
# Check for semver
6976
if [[ "$${KUBERNETES_VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
70-
CI_URL="gs://kubernetes-release/release/$${KUBERNETES_VERSION}/bin/linux/amd64"
77+
CI_URL="https://storage.googleapis.com/kubernetes-release/release/$${KUBERNETES_VERSION}/bin/linux/amd64"
7178
VERSION_WITHOUT_PREFIX="$${KUBERNETES_VERSION#v}"
72-
DEBIAN_FRONTEND=noninteractive apt-get install -y apt-transport-https curl
79+
export DEBIAN_FRONTEND=noninteractive
80+
# sometimes the network is not immediately available, so we have to retry the apt-get update
81+
retry 10 5 "apt-get update"
82+
apt-get install -y apt-transport-https ca-certificates curl
7383
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
7484
echo 'deb https://apt.kubernetes.io/ kubernetes-xenial main' >/etc/apt/sources.list.d/kubernetes.list
7585
apt-get update
@@ -78,24 +88,28 @@ if [[ "$${KUBERNETES_VERSION}" != "" ]]; then
7888
PACKAGE_VERSION="$(apt-cache madison kubelet | grep "$${VERSION_REGEX}-" | head -n1 | cut -d '|' -f 2 | tr -d '[:space:]')"
7989
for CI_PACKAGE in "$${PACKAGES_TO_TEST[@]}"; do
8090
echo "* installing package: $${CI_PACKAGE} $${PACKAGE_VERSION}"
81-
DEBIAN_FRONTEND=noninteractive apt-get install -y "$${CI_PACKAGE}=$${PACKAGE_VERSION}"
91+
apt-get install -y "$${CI_PACKAGE}=$${PACKAGE_VERSION}"
8292
done
8393
else
84-
CI_URL="gs://kubernetes-release-dev/ci/$${KUBERNETES_VERSION}/bin/linux/amd64"
94+
CI_URL="https://storage.googleapis.com/k8s-release-dev/ci/$${KUBERNETES_VERSION}/bin/linux/amd64"
8595
for CI_PACKAGE in "$${PACKAGES_TO_TEST[@]}"; do
96+
# Browser: https://console.cloud.google.com/storage/browser/k8s-release-dev?project=k8s-release-dev
97+
# e.g.: https://storage.googleapis.com/k8s-release-dev/ci/v1.21.0-beta.1.378+cf3374e43491c5/bin/linux/amd64/kubectl
8698
echo "* downloading binary: $${CI_URL}/$${CI_PACKAGE}"
87-
$${GSUTIL} cp "$${CI_URL}/$${CI_PACKAGE}" "$${CI_DIR}/$${CI_PACKAGE}"
99+
wget "$${CI_URL}/$${CI_PACKAGE}" -O "$${CI_DIR}/$${CI_PACKAGE}"
88100
chmod +x "$${CI_DIR}/$${CI_PACKAGE}"
89101
mv "$${CI_DIR}/$${CI_PACKAGE}" "/usr/bin/$${CI_PACKAGE}"
90102
done
91103
systemctl restart kubelet
92104
fi
93105
for CI_CONTAINER in "$${CONTAINERS_TO_TEST[@]}"; do
106+
# Browser: https://console.cloud.google.com/storage/browser/kubernetes-release?project=kubernetes-release
107+
# e.g.: https://storage.googleapis.com/kubernetes-release/release/v1.20.4/bin/linux/amd64/kube-apiserver.tar
94108
echo "* downloading package: $${CI_URL}/$${CI_CONTAINER}.$${CONTAINER_EXT}"
95-
$${GSUTIL} cp "$${CI_URL}/$${CI_CONTAINER}.$${CONTAINER_EXT}" "$${CI_DIR}/$${CI_CONTAINER}.$${CONTAINER_EXT}"
109+
wget "$${CI_URL}/$${CI_CONTAINER}.$${CONTAINER_EXT}" -O "$${CI_DIR}/$${CI_CONTAINER}.$${CONTAINER_EXT}"
96110
$${SUDO} ctr -n k8s.io images import "$${CI_DIR}/$${CI_CONTAINER}.$${CONTAINER_EXT}" || echo "* ignoring expected 'ctr images import' result"
97111
$${SUDO} ctr -n k8s.io images tag "k8s.gcr.io/$${CI_CONTAINER}-amd64:$${KUBERNETES_VERSION//+/_}" "k8s.gcr.io/$${CI_CONTAINER}:$${KUBERNETES_VERSION//+/_}"
98-
$${SUDO} ctr -n k8s.io images tag "k8s.gcr.io/$${CI_CONTAINER}-amd64:$${KUBERNETES_VERSION//+/_}" "gcr.io/kubernetes-ci-images/$${CI_CONTAINER}:$${KUBERNETES_VERSION//+/_}"
112+
$${SUDO} ctr -n k8s.io images tag "k8s.gcr.io/$${CI_CONTAINER}-amd64:$${KUBERNETES_VERSION//+/_}" "gcr.io/k8s-staging-ci-images/$${CI_CONTAINER}:$${KUBERNETES_VERSION//+/_}"
99113
done
100114
fi
101115
echo "* checking binary versions"

test/framework/kubernetesversions/data/kustomization.yaml

+15-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,19 @@ apiVersion: kustomize.config.k8s.io/v1beta1
22
kind: Kustomization
33
namespace: default
44
resources:
5-
- ci-artifacts-source-template.yaml
5+
- ci-artifacts-source-template.yaml
66
patchesStrategicMerge:
7-
- kustomizeversions.yaml
8-
- platform-kustomization.yaml
7+
- platform-kustomization.yaml
8+
patchesJson6902:
9+
- path: kubeadmcontrolplane-patch.yaml
10+
target:
11+
group: controlplane.cluster.x-k8s.io
12+
kind: KubeadmControlPlane
13+
name: ".*-control-plane"
14+
version: v1alpha4
15+
- path: kubeadmconfigtemplate-patch.yaml
16+
target:
17+
group: bootstrap.cluster.x-k8s.io
18+
kind: KubeadmConfigTemplate
19+
name: ".*-md-0"
20+
version: v1alpha4

test/framework/kubernetesversions/template.go

+108-91
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,30 @@ limitations under the License.
1818
package kubernetesversions
1919

2020
import (
21+
"bytes"
2122
_ "embed"
2223
"errors"
24+
"fmt"
25+
"io/ioutil"
2326
"os"
2427
"os/exec"
2528
"path"
29+
"strings"
30+
"text/template"
2631

27-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28-
cabpkv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha4"
29-
kcpv1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1alpha4"
32+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3033
"sigs.k8s.io/cluster-api/test/framework"
3134
"sigs.k8s.io/yaml"
3235
)
3336

34-
const yamlSeparator = "\n---\n"
35-
3637
var (
37-
//go:embed data/kustomization.yaml
38-
kustomizationYamlBytes []byte
39-
40-
//go:embed data/debian_injection_script.envsubst.sh
38+
//go:embed data/debian_injection_script.envsubst.sh.tpl
4139
debianInjectionScriptBytes string
40+
41+
debianInjectionScriptTemplate = template.Must(template.New("").Parse(debianInjectionScriptBytes))
42+
43+
//go:embed data/kustomization.yaml
44+
kustomizationYAMLBytes string
4245
)
4346

4447
type GenerateCIArtifactsInjectedTemplateForDebianInput struct {
@@ -88,18 +91,34 @@ func GenerateCIArtifactsInjectedTemplateForDebian(input GenerateCIArtifactsInjec
8891

8992
kustomizedTemplate := path.Join(templateDir, "cluster-template-conformance-ci-artifacts.yaml")
9093

91-
if err := os.WriteFile(path.Join(overlayDir, "kustomization.yaml"), kustomizationYamlBytes, 0o600); err != nil {
94+
if err := ioutil.WriteFile(path.Join(overlayDir, "kustomization.yaml"), []byte(kustomizationYAMLBytes), 0o600); err != nil {
9295
return "", err
9396
}
9497

95-
kustomizeVersions, err := generateKustomizeVersionsYaml(input.KubeadmControlPlaneName, input.KubeadmConfigTemplateName, input.KubeadmConfigName)
98+
var debianInjectionScriptControlPlaneBytes bytes.Buffer
99+
if err := debianInjectionScriptTemplate.Execute(&debianInjectionScriptControlPlaneBytes, map[string]bool{"IsControlPlaneMachine": true}); err != nil {
100+
return "", err
101+
}
102+
patch, err := generateInjectScriptJSONPatch(input.SourceTemplate, "KubeadmControlPlane", input.KubeadmControlPlaneName, "/spec/kubeadmConfigSpec", "/usr/local/bin/ci-artifacts.sh", debianInjectionScriptControlPlaneBytes.String())
96103
if err != nil {
97104
return "", err
98105
}
106+
if err := os.WriteFile(path.Join(overlayDir, "kubeadmcontrolplane-patch.yaml"), patch, 0o600); err != nil {
107+
return "", err
108+
}
99109

100-
if err := os.WriteFile(path.Join(overlayDir, "kustomizeversions.yaml"), kustomizeVersions, 0o600); err != nil {
110+
var debianInjectionScriptWorkerBytes bytes.Buffer
111+
if err := debianInjectionScriptTemplate.Execute(&debianInjectionScriptWorkerBytes, map[string]bool{"IsControlPlaneMachine": false}); err != nil {
101112
return "", err
102113
}
114+
patch, err = generateInjectScriptJSONPatch(input.SourceTemplate, "KubeadmConfigTemplate", input.KubeadmConfigTemplateName, "/spec/template/spec", "/usr/local/bin/ci-artifacts.sh", debianInjectionScriptWorkerBytes.String())
115+
if err != nil {
116+
return "", err
117+
}
118+
if err := os.WriteFile(path.Join(overlayDir, "kubeadmconfigtemplate-patch.yaml"), patch, 0o600); err != nil {
119+
return "", err
120+
}
121+
103122
if err := os.WriteFile(path.Join(overlayDir, "ci-artifacts-source-template.yaml"), input.SourceTemplate, 0o600); err != nil {
104123
return "", err
105124
}
@@ -117,91 +136,89 @@ func GenerateCIArtifactsInjectedTemplateForDebian(input GenerateCIArtifactsInjec
117136
return kustomizedTemplate, nil
118137
}
119138

120-
func generateKustomizeVersionsYaml(kcpName, kubeadmTemplateName, kubeadmConfigName string) ([]byte, error) {
121-
kcp := generateKubeadmControlPlane(kcpName)
122-
kubeadm := generateKubeadmConfigTemplate(kubeadmTemplateName)
123-
kcpYaml, err := yaml.Marshal(kcp)
124-
if err != nil {
125-
return nil, err
126-
}
127-
kubeadmYaml, err := yaml.Marshal(kubeadm)
128-
if err != nil {
129-
return nil, err
130-
}
131-
fileStr := string(kcpYaml) + yamlSeparator + string(kubeadmYaml)
132-
if kubeadmConfigName == "" {
133-
return []byte(fileStr), nil
134-
}
139+
type jsonPatch struct {
140+
Op string `json:"op"`
141+
Path string `json:"path"`
142+
Value interface{} `json:"value"`
143+
}
135144

136-
kubeadmConfig := generateKubeadmConfig(kubeadmConfigName)
137-
kubeadmConfigYaml, err := yaml.Marshal(kubeadmConfig)
145+
// generateInjectScriptJSONPatch generates a JSON patch which injects a script
146+
// * objectKind: is the kind of the object we want to inject the script into
147+
// * objectName: is the name of the object we want to inject the script into
148+
// * jsonPatchPathPrefix: is the prefix of the 'files' and `preKubeadmCommands` arrays where we append the script
149+
// * scriptPath: is the path where the script will be stored at
150+
// * scriptContent: content of the script.
151+
func generateInjectScriptJSONPatch(sourceTemplate []byte, objectKind, objectName, jsonPatchPathPrefix, scriptPath, scriptContent string) ([]byte, error) {
152+
filesPathExists, preKubeadmCommandsPathExists, err := checkIfArraysAlreadyExist(sourceTemplate, objectKind, objectName, jsonPatchPathPrefix)
138153
if err != nil {
139154
return nil, err
140155
}
141-
fileStr = fileStr + yamlSeparator + string(kubeadmConfigYaml)
142156

143-
return []byte(fileStr), nil
144-
}
145-
146-
func generateKubeadmConfigTemplate(name string) *cabpkv1.KubeadmConfigTemplate {
147-
kubeadmSpec := generateKubeadmConfigSpec()
148-
return &cabpkv1.KubeadmConfigTemplate{
149-
TypeMeta: metav1.TypeMeta{
150-
Kind: "KubeadmConfigTemplate",
151-
APIVersion: cabpkv1.GroupVersion.String(),
152-
},
153-
ObjectMeta: metav1.ObjectMeta{
154-
Name: name,
157+
var patches []jsonPatch
158+
if !filesPathExists {
159+
patches = append(patches, jsonPatch{
160+
Op: "add",
161+
Path: fmt.Sprintf("%s/files", jsonPatchPathPrefix),
162+
Value: []interface{}{},
163+
})
164+
}
165+
patches = append(patches, jsonPatch{
166+
Op: "add",
167+
Path: fmt.Sprintf("%s/files/-", jsonPatchPathPrefix),
168+
Value: map[string]string{
169+
"content": scriptContent,
170+
"owner": "root:root",
171+
"path": scriptPath,
172+
"permissions": "0750",
155173
},
156-
Spec: cabpkv1.KubeadmConfigTemplateSpec{
157-
Template: cabpkv1.KubeadmConfigTemplateResource{
158-
Spec: *kubeadmSpec,
159-
},
160-
},
161-
}
174+
})
175+
if !preKubeadmCommandsPathExists {
176+
patches = append(patches, jsonPatch{
177+
Op: "add",
178+
Path: fmt.Sprintf("%s/preKubeadmCommands", jsonPatchPathPrefix),
179+
Value: []string{},
180+
})
181+
}
182+
patches = append(patches, jsonPatch{
183+
Op: "add",
184+
Path: fmt.Sprintf("%s/preKubeadmCommands/-", jsonPatchPathPrefix),
185+
Value: scriptPath,
186+
})
187+
188+
return yaml.Marshal(patches)
162189
}
163190

164-
func generateKubeadmConfig(name string) *cabpkv1.KubeadmConfig {
165-
kubeadmSpec := generateKubeadmConfigSpec()
166-
return &cabpkv1.KubeadmConfig{
167-
TypeMeta: metav1.TypeMeta{
168-
Kind: "KubeadmConfig",
169-
APIVersion: kcpv1.GroupVersion.String(),
170-
},
171-
ObjectMeta: metav1.ObjectMeta{
172-
Name: name,
173-
},
174-
Spec: *kubeadmSpec,
175-
}
176-
}
177-
178-
func generateKubeadmControlPlane(name string) *kcpv1.KubeadmControlPlane {
179-
kubeadmSpec := generateKubeadmConfigSpec()
180-
return &kcpv1.KubeadmControlPlane{
181-
TypeMeta: metav1.TypeMeta{
182-
Kind: "KubeadmControlPlane",
183-
APIVersion: kcpv1.GroupVersion.String(),
184-
},
185-
ObjectMeta: metav1.ObjectMeta{
186-
Name: name,
187-
},
188-
Spec: kcpv1.KubeadmControlPlaneSpec{
189-
KubeadmConfigSpec: *kubeadmSpec,
190-
Version: "${KUBERNETES_VERSION}",
191-
},
192-
}
193-
}
194-
195-
func generateKubeadmConfigSpec() *cabpkv1.KubeadmConfigSpec {
196-
return &cabpkv1.KubeadmConfigSpec{
197-
Files: []cabpkv1.File{
198-
{
199-
Path: "/usr/local/bin/ci-artifacts.sh",
200-
Content: debianInjectionScriptBytes,
201-
Owner: "root:root",
202-
Permissions: "0750",
203-
},
204-
},
205-
PreKubeadmCommands: []string{"/usr/local/bin/ci-artifacts.sh"},
206-
}
191+
// checkIfArraysAlreadyExist check is the 'files' and 'preKubeadmCommands' arrays already exist below jsonPatchPathPrefix.
192+
func checkIfArraysAlreadyExist(sourceTemplate []byte, objectKind, objectName, jsonPatchPathPrefix string) (bool, bool, error) {
193+
yamlDocs := strings.Split(string(sourceTemplate), "---")
194+
for _, yamlDoc := range yamlDocs {
195+
if yamlDoc == "" {
196+
continue
197+
}
198+
var obj unstructured.Unstructured
199+
if err := yaml.Unmarshal([]byte(yamlDoc), &obj); err != nil {
200+
return false, false, err
201+
}
202+
203+
if obj.GetKind() != objectKind {
204+
continue
205+
}
206+
if obj.GetName() != objectName {
207+
continue
208+
}
209+
210+
pathSplit := strings.Split(strings.TrimPrefix(jsonPatchPathPrefix, "/"), "/")
211+
filesPath := append(pathSplit, "files")
212+
preKubeadmCommandsPath := append(pathSplit, "preKubeadmCommands")
213+
_, filesPathExists, err := unstructured.NestedFieldCopy(obj.Object, filesPath...)
214+
if err != nil {
215+
return false, false, err
216+
}
217+
_, preKubeadmCommandsPathExists, err := unstructured.NestedFieldCopy(obj.Object, preKubeadmCommandsPath...)
218+
if err != nil {
219+
return false, false, err
220+
}
221+
return filesPathExists, preKubeadmCommandsPathExists, nil
222+
}
223+
return false, false, fmt.Errorf("could not find document with kind %q and name %q", objectKind, objectName)
207224
}

0 commit comments

Comments
 (0)