mirror of
https://github.com/openshift/installer.git
synced 2026-02-05 06:46:36 +01:00
889 lines
27 KiB
Go
889 lines
27 KiB
Go
package image
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
igntypes "github.com/coreos/ignition/v2/config/v3_2/types"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/vincent-petithory/dataurl"
|
|
v1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
hiveext "github.com/openshift/assisted-service/api/hiveextension/v1beta1"
|
|
aiv1beta1 "github.com/openshift/assisted-service/api/v1beta1"
|
|
"github.com/openshift/assisted-service/models"
|
|
hivev1 "github.com/openshift/hive/apis/hive/v1"
|
|
"github.com/openshift/installer/pkg/asset"
|
|
"github.com/openshift/installer/pkg/asset/agent/agentconfig"
|
|
"github.com/openshift/installer/pkg/asset/agent/common"
|
|
"github.com/openshift/installer/pkg/asset/agent/gencrypto"
|
|
"github.com/openshift/installer/pkg/asset/agent/joiner"
|
|
"github.com/openshift/installer/pkg/asset/agent/manifests"
|
|
"github.com/openshift/installer/pkg/asset/agent/mirror"
|
|
"github.com/openshift/installer/pkg/asset/agent/workflow"
|
|
"github.com/openshift/installer/pkg/asset/password"
|
|
"github.com/openshift/installer/pkg/asset/tls"
|
|
"github.com/openshift/installer/pkg/types/agent"
|
|
)
|
|
|
|
// Unable to test Generate because bootstrap.AddStorageFiles
|
|
// returns error in unit test:
|
|
// open data/agent/files: no such file or directory
|
|
// Unit test working directory is ./pkg/asset/agent/image
|
|
// While normal execution working directory is ./data
|
|
// func TestIgnition_Generate(t *testing.T) {}
|
|
|
|
func TestIgnition_getTemplateData(t *testing.T) {
|
|
clusterImageSet := &hivev1.ClusterImageSet{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "openshift-v4.10.0",
|
|
},
|
|
Spec: hivev1.ClusterImageSetSpec{
|
|
ReleaseImage: "quay.io:443/openshift-release-dev/ocp-release:4.10.0-rc.1-x86_64",
|
|
},
|
|
}
|
|
pullSecret := "pull-secret"
|
|
agentClusterInstall := &hiveext.AgentClusterInstall{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-agent-cluster-install",
|
|
Namespace: "cluster0",
|
|
},
|
|
Spec: hiveext.AgentClusterInstallSpec{
|
|
APIVIP: "192.168.111.2",
|
|
SSHPublicKey: "ssh-rsa AAAAmyKey",
|
|
ProvisionRequirements: hiveext.ProvisionRequirements{
|
|
ControlPlaneAgents: 3,
|
|
WorkerAgents: 5,
|
|
ArbiterAgents: 1,
|
|
},
|
|
},
|
|
}
|
|
releaseImage := "quay.io:443/openshift-release-dev/ocp-release:4.10.0-rc.1-x86_64"
|
|
releaseImageMirror := "virthost.ostest.test.metalkube.org:5000/localimages/local-release-image"
|
|
infraEnvID := "random-infra-env-id"
|
|
haveMirrorConfig := true
|
|
publicContainerRegistries := "quay.io,registry.ci.openshift.org"
|
|
|
|
releaseImageList, err := releaseImageList(clusterImageSet.Spec.ReleaseImage, "x86_64", []string{"86_64"})
|
|
assert.NoError(t, err)
|
|
|
|
arch := "x86_64"
|
|
ov := "4.12"
|
|
isoURL := "https://rhcos.mirror.openshift.com/art/storage/releases/rhcos-4.12/412.86.202208101039-0/x86_64/rhcos-412.86.202208101039-0-live.x86_64.iso"
|
|
ver := "412.86.202208101039-0"
|
|
osImage := &models.OsImage{
|
|
CPUArchitecture: &arch,
|
|
OpenshiftVersion: &ov,
|
|
URL: &isoURL,
|
|
Version: &ver,
|
|
}
|
|
|
|
proxy := &aiv1beta1.Proxy{
|
|
HTTPProxy: "http://1.1.1.1:80",
|
|
HTTPSProxy: "https://1.1.1.1:443",
|
|
NoProxy: "valid-proxy.com,172.30.0.0/16",
|
|
}
|
|
clusterName := "test-agent-cluster-install.test"
|
|
|
|
publicKey := "-----BEGIN EC PUBLIC KEY-----\nMHcCAQEEIOSCfDNmx0qe6dncV4tg==\n-----END EC PUBLIC KEY-----\n"
|
|
agentAuthToken := "agentAuthToken"
|
|
userAuthToken := "userAuthToken"
|
|
watcherAuthToken := "watcherAuthToken"
|
|
templateData := getTemplateData(clusterName, pullSecret, releaseImageList, releaseImage, releaseImageMirror, publicContainerRegistries, "minimal-iso", infraEnvID, publicKey, gencrypto.AuthType, agentAuthToken, userAuthToken, watcherAuthToken, "", "", haveMirrorConfig, agentClusterInstall.Spec.ProvisionRequirements.ControlPlaneAgents, agentClusterInstall.Spec.ProvisionRequirements.ArbiterAgents, agentClusterInstall.Spec.ProvisionRequirements.WorkerAgents, osImage, proxy)
|
|
assert.Equal(t, clusterName, templateData.ClusterName)
|
|
assert.Equal(t, "http", templateData.ServiceProtocol)
|
|
assert.Equal(t, pullSecret, templateData.PullSecret)
|
|
assert.Equal(t, agentClusterInstall.Spec.ProvisionRequirements.ControlPlaneAgents, templateData.ControlPlaneAgents)
|
|
assert.Equal(t, agentClusterInstall.Spec.ProvisionRequirements.ArbiterAgents, templateData.ArbiterAgents)
|
|
assert.Equal(t, agentClusterInstall.Spec.ProvisionRequirements.WorkerAgents, templateData.WorkerAgents)
|
|
assert.Equal(t, releaseImageList, templateData.ReleaseImages)
|
|
assert.Equal(t, releaseImage, templateData.ReleaseImage)
|
|
assert.Equal(t, releaseImageMirror, templateData.ReleaseImageMirror)
|
|
assert.Equal(t, haveMirrorConfig, templateData.HaveMirrorConfig)
|
|
assert.Equal(t, publicContainerRegistries, templateData.PublicContainerRegistries)
|
|
assert.Equal(t, infraEnvID, templateData.InfraEnvID)
|
|
assert.Equal(t, osImage, templateData.OSImage)
|
|
assert.Equal(t, proxy, templateData.Proxy)
|
|
assert.Equal(t, publicKey, templateData.PublicKeyPEM)
|
|
assert.Equal(t, gencrypto.AuthType, templateData.AuthType)
|
|
assert.Equal(t, agentAuthToken, templateData.AgentAuthToken)
|
|
assert.Equal(t, userAuthToken, templateData.UserAuthToken)
|
|
assert.Equal(t, watcherAuthToken, templateData.WatcherAuthToken)
|
|
|
|
}
|
|
|
|
func TestIgnition_getRendezvousHostEnv(t *testing.T) {
|
|
nodeZeroIP := "2001:db8::dead:beef"
|
|
agentAuthtoken := "agentAuthtoken"
|
|
userAuthToken := "userAuthToken"
|
|
rendezvousHostEnv := getRendezvousHostEnv(&agentTemplateData{
|
|
ServiceProtocol: "http",
|
|
AgentAuthToken: agentAuthtoken,
|
|
UserAuthToken: userAuthToken,
|
|
}, nodeZeroIP, workflow.AgentWorkflowTypeInstall)
|
|
assert.Equal(t,
|
|
"#\nNODE_ZERO_IP="+nodeZeroIP+"\nSERVICE_BASE_URL=http://["+nodeZeroIP+"]:8090/\nIMAGE_SERVICE_BASE_URL=http://["+nodeZeroIP+"]:8888/\nPULL_SECRET_TOKEN="+agentAuthtoken+"\nUSER_AUTH_TOKEN="+userAuthToken+"\nWORKFLOW_TYPE=install\nAIUI_APP_API_URL=http://["+nodeZeroIP+"]:8090/\nAIUI_URL=http://["+nodeZeroIP+"]:3001/\n",
|
|
rendezvousHostEnv)
|
|
}
|
|
|
|
func TestIgnition_addStaticNetworkConfig(t *testing.T) {
|
|
_, execErr := exec.LookPath("nmstatectl")
|
|
if execErr != nil {
|
|
t.Skip("No nmstatectl binary available")
|
|
}
|
|
|
|
cases := []struct {
|
|
Name string
|
|
staticNetworkConfig []*models.HostStaticNetworkConfig
|
|
expectedError string
|
|
expectedFileList []string
|
|
}{
|
|
{
|
|
Name: "default",
|
|
staticNetworkConfig: []*models.HostStaticNetworkConfig{
|
|
{
|
|
MacInterfaceMap: models.MacInterfaceMap{
|
|
{LogicalNicName: "eth0", MacAddress: "52:54:01:aa:aa:a1"},
|
|
},
|
|
NetworkYaml: "interfaces:\n- ipv4:\n address:\n - ip: 192.168.122.21\n prefix-length: 24\n enabled: true\n mac-address: 52:54:01:aa:aa:a1\n name: eth0\n state: up\n type: ethernet\n",
|
|
},
|
|
},
|
|
expectedError: "",
|
|
expectedFileList: []string{
|
|
"/etc/assisted/network/host0/eth0.nmconnection",
|
|
"/etc/assisted/network/host0/mac_interface.ini",
|
|
"/usr/local/bin/pre-network-manager-config.sh",
|
|
},
|
|
},
|
|
{
|
|
Name: "no-static-network-configs",
|
|
staticNetworkConfig: []*models.HostStaticNetworkConfig{},
|
|
expectedError: "",
|
|
expectedFileList: nil,
|
|
},
|
|
{
|
|
Name: "error-processing-config",
|
|
staticNetworkConfig: []*models.HostStaticNetworkConfig{
|
|
{
|
|
MacInterfaceMap: models.MacInterfaceMap{
|
|
{LogicalNicName: "eth0", MacAddress: "52:54:01:aa:aa:a1"},
|
|
},
|
|
NetworkYaml: "interfaces:\n- ipv4:\n address:\n - ip: bad-ip\n prefix-length: 24\n enabled: true\n mac-address: 52:54:01:aa:aa:a1\n name: eth0\n state: up\n type: ethernet\n",
|
|
},
|
|
},
|
|
expectedError: ".*invalid IP address syntax",
|
|
expectedFileList: nil,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
config := igntypes.Config{}
|
|
err := addStaticNetworkConfig(&config, tc.staticNetworkConfig)
|
|
|
|
if tc.expectedError != "" {
|
|
assert.Regexp(t, tc.expectedError, err.Error())
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
var fileList []string
|
|
for _, file := range config.Storage.Files {
|
|
fileList = append(fileList, file.Node.Path)
|
|
}
|
|
assert.Equal(t, tc.expectedFileList, fileList)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRetrieveRendezvousIP(t *testing.T) {
|
|
rawConfig := `interfaces:
|
|
- ipv4:
|
|
address:
|
|
- ip: "192.168.122.21"`
|
|
cases := []struct {
|
|
Name string
|
|
agentConfig *agent.Config
|
|
nmStateConfigs []*aiv1beta1.NMStateConfig
|
|
expectedRendezvousIP string
|
|
expectedError string
|
|
}{
|
|
{
|
|
Name: "valid-agent-config-provided-with-RendezvousIP",
|
|
agentConfig: &agent.Config{
|
|
RendezvousIP: "192.168.122.21",
|
|
Hosts: []agent.Host{
|
|
{
|
|
Hostname: "control-0.example.org",
|
|
Role: "master",
|
|
},
|
|
},
|
|
},
|
|
expectedRendezvousIP: "192.168.122.21",
|
|
},
|
|
{
|
|
Name: "no-agent-config-provided-so-read-from-nmstateconfig",
|
|
nmStateConfigs: []*aiv1beta1.NMStateConfig{
|
|
{
|
|
Spec: aiv1beta1.NMStateConfigSpec{
|
|
NetConfig: aiv1beta1.NetConfig{
|
|
Raw: []byte(rawConfig),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedRendezvousIP: "192.168.122.21",
|
|
},
|
|
{
|
|
Name: "neither-agent-config-was-provided-with-RendezvousIP-nor-nmstateconfig-manifest",
|
|
agentConfig: &agent.Config{
|
|
Hosts: []agent.Host{
|
|
{
|
|
Hostname: "control-0.example.org",
|
|
Role: "master",
|
|
},
|
|
},
|
|
},
|
|
expectedError: "missing rendezvousIP in agent-config, at least one host networkConfig, or at least one NMStateConfig manifest",
|
|
},
|
|
{
|
|
Name: "non-canonical-ipv6-address",
|
|
agentConfig: &agent.Config{
|
|
RendezvousIP: "fd2e:6f44:5dd8:c956:0000:0000:0000:0050",
|
|
Hosts: []agent.Host{
|
|
{
|
|
Hostname: "control-0.example.org",
|
|
Role: "master",
|
|
},
|
|
},
|
|
},
|
|
expectedRendezvousIP: "fd2e:6f44:5dd8:c956::50",
|
|
},
|
|
{
|
|
Name: "invalid-ipv6-address",
|
|
agentConfig: &agent.Config{
|
|
RendezvousIP: "fd2e:6f44:5dd8:c956::0000::0050",
|
|
Hosts: []agent.Host{
|
|
{
|
|
Hostname: "control-0.example.org",
|
|
Role: "master",
|
|
},
|
|
},
|
|
},
|
|
expectedError: "invalid rendezvous IP: fd2e:6f44:5dd8:c956::0000::0050",
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
var hosts []agent.Host
|
|
if tc.agentConfig != nil {
|
|
hosts = tc.agentConfig.Hosts
|
|
}
|
|
rendezvousIP, err := RetrieveRendezvousIP(tc.agentConfig, hosts, tc.nmStateConfigs)
|
|
if tc.expectedError != "" {
|
|
assert.Regexp(t, tc.expectedError, err.Error())
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.expectedRendezvousIP, rendezvousIP)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAddHostConfig_Roles(t *testing.T) {
|
|
cases := []struct {
|
|
Name string
|
|
agentHosts *agentconfig.AgentHosts
|
|
expectedNumberOfHostConfigFiles int
|
|
}{
|
|
{
|
|
Name: "one-host-role-defined",
|
|
agentHosts: &agentconfig.AgentHosts{
|
|
Hosts: []agent.Host{
|
|
{
|
|
Role: "master",
|
|
},
|
|
},
|
|
},
|
|
expectedNumberOfHostConfigFiles: 1,
|
|
},
|
|
{
|
|
Name: "multiple-host-roles-defined",
|
|
agentHosts: &agentconfig.AgentHosts{
|
|
Hosts: []agent.Host{
|
|
{
|
|
Role: "master",
|
|
},
|
|
{
|
|
Role: "master",
|
|
},
|
|
{
|
|
Role: "master",
|
|
},
|
|
{
|
|
Role: "worker",
|
|
},
|
|
{
|
|
Role: "worker",
|
|
},
|
|
},
|
|
},
|
|
expectedNumberOfHostConfigFiles: 5,
|
|
},
|
|
{
|
|
Name: "zero-host-roles-defined",
|
|
expectedNumberOfHostConfigFiles: 0,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
config := &igntypes.Config{}
|
|
err := addHostConfig(config, tc.agentHosts)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, len(config.Storage.Files), tc.expectedNumberOfHostConfigFiles)
|
|
for _, file := range config.Storage.Files {
|
|
assert.Equal(t, true, strings.HasPrefix(file.Path, "/etc/assisted/hostconfig"))
|
|
assert.Equal(t, true, strings.HasSuffix(file.Path, "role"))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func generatedFiles(otherFiles ...string) []string {
|
|
files := []string{
|
|
"/etc/assisted/rendezvous-host.env",
|
|
"/etc/assisted/manifests/agent-config.yaml",
|
|
// TODO: ZTP manifest files should also be present. Bug?
|
|
// "/etc/assisted/manifests/cluster-deployment.yaml",
|
|
// "/etc/assisted/manifests/agent-cluster-install.yaml",
|
|
// "/etc/assisted/manifests/pull-secret.yaml",
|
|
// "/etc/assisted/manifests/cluster-image-set.yaml",
|
|
// "/etc/assisted/manifests/infraenv.yaml",
|
|
"/etc/assisted/network/host0/eth0.nmconnection",
|
|
"/etc/assisted/network/host0/mac_interface.ini",
|
|
"/usr/local/bin/oci-eval-user-data.sh",
|
|
"/usr/local/bin/pre-network-manager-config.sh",
|
|
"/opt/agent/tls/kubeadmin-password.hash"}
|
|
files = append(files, otherFiles...)
|
|
return append(files, commonFiles()...)
|
|
}
|
|
|
|
func commonFiles() []string {
|
|
return []string{
|
|
"/etc/issue",
|
|
"/etc/multipath.conf",
|
|
"/etc/containers/containers.conf",
|
|
"/etc/NetworkManager/conf.d/clientid.conf",
|
|
"/root/.docker/config.json",
|
|
"/root/assisted.te",
|
|
"/usr/local/bin/agent-config-image-wait.sh",
|
|
"/usr/local/bin/agent-extract-tui.sh",
|
|
"/usr/local/bin/agent-gather",
|
|
"/usr/local/bin/extract-agent.sh",
|
|
"/usr/local/bin/get-container-images.sh",
|
|
"/usr/local/bin/set-hostname.sh",
|
|
"/usr/local/bin/start-agent.sh",
|
|
"/usr/local/bin/start-cluster-installation.sh",
|
|
"/usr/local/bin/wait-for-assisted-service.sh",
|
|
"/usr/local/bin/set-node-zero.sh",
|
|
"/usr/local/share/assisted-service/assisted-db.env",
|
|
"/usr/local/share/assisted-service/assisted-service.env",
|
|
"/usr/local/share/start-cluster/start-cluster.env",
|
|
"/usr/local/share/assisted-service/images.env",
|
|
"/usr/local/bin/bootstrap-service-record.sh",
|
|
"/usr/local/bin/release-image.sh",
|
|
"/usr/local/bin/release-image-download.sh",
|
|
"/etc/assisted/agent-installer.env",
|
|
"/etc/motd.d/10-agent-installer",
|
|
"/etc/systemd/system.conf.d/10-default-env.conf",
|
|
"/usr/local/bin/install-status.sh",
|
|
"/usr/local/bin/issue_status.sh",
|
|
"/usr/local/bin/load-config-iso.sh",
|
|
"/etc/udev/rules.d/80-agent-config-image.rules",
|
|
"/usr/local/bin/add-node.sh",
|
|
"/usr/local/bin/agent-auth-token-status.sh",
|
|
"/usr/local/bin/common.sh",
|
|
"/usr/local/bin/configure-assisted-hw-requirements.sh",
|
|
}
|
|
}
|
|
|
|
func setupEmbeddedResources(t *testing.T) func() {
|
|
t.Helper()
|
|
workingDirectory, err := os.Getwd()
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, os.Chdir(path.Join(workingDirectory, "../../../../data")))
|
|
return func() {
|
|
assert.NoError(t, os.Chdir(workingDirectory))
|
|
}
|
|
}
|
|
|
|
func TestIgnition_Generate(t *testing.T) {
|
|
skipTestIfnmstatectlIsMissing(t)
|
|
|
|
defer setupEmbeddedResources(t)()
|
|
|
|
cases := []struct {
|
|
name string
|
|
overrideAssets map[reflect.Type]func(t *testing.T, dep asset.Asset) asset.Asset
|
|
expectedError string
|
|
expectedFiles []string
|
|
expectedFileContent map[string]string
|
|
serviceEnabledMap map[string]bool
|
|
}{
|
|
{
|
|
name: "default",
|
|
serviceEnabledMap: map[string]bool{
|
|
"pre-network-manager-config.service": true,
|
|
"agent-check-config-image.service": false},
|
|
expectedFiles: generatedFiles(),
|
|
},
|
|
{
|
|
name: "with extra manifests",
|
|
overrideAssets: map[reflect.Type]func(t *testing.T, dep asset.Asset) asset.Asset{
|
|
reflect.TypeOf(manifests.ExtraManifests{}): func(t *testing.T, dep asset.Asset) asset.Asset {
|
|
t.Helper()
|
|
return &manifests.ExtraManifests{
|
|
FileList: []*asset.File{
|
|
{
|
|
Filename: "openshift/test-configmap.yaml",
|
|
Data: []byte(`
|
|
---
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: agent-test-1
|
|
---
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: agent-test-2`),
|
|
},
|
|
},
|
|
}
|
|
},
|
|
},
|
|
expectedFiles: generatedFiles("/etc/assisted/extra-manifests/test-configmap-0.yaml", "/etc/assisted/extra-manifests/test-configmap-1.yaml"),
|
|
expectedFileContent: map[string]string{
|
|
"/etc/assisted/extra-manifests/test-configmap-0.yaml": `apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: agent-test-1`,
|
|
"/etc/assisted/extra-manifests/test-configmap-1.yaml": `apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: agent-test-2
|
|
`,
|
|
},
|
|
serviceEnabledMap: map[string]bool{
|
|
"pre-network-manager-config.service": true,
|
|
"agent-check-config-image.service": false},
|
|
},
|
|
{
|
|
name: "no nmstateconfigs defined, pre-network-manager-config.service should not be enabled",
|
|
overrideAssets: map[reflect.Type]func(t *testing.T, dep asset.Asset) asset.Asset{
|
|
reflect.TypeOf(manifests.AgentManifests{}): func(t *testing.T, dep asset.Asset) asset.Asset {
|
|
t.Helper()
|
|
am, ok := dep.(*manifests.AgentManifests)
|
|
assert.True(t, ok)
|
|
// remove nmstate configuration
|
|
am.NMStateConfigs = []*aiv1beta1.NMStateConfig{}
|
|
am.StaticNetworkConfigs = []*models.HostStaticNetworkConfig{}
|
|
return am
|
|
},
|
|
},
|
|
serviceEnabledMap: map[string]bool{
|
|
"pre-network-manager-config.service": false},
|
|
},
|
|
{
|
|
name: "with additional ntp sources",
|
|
overrideAssets: map[reflect.Type]func(t *testing.T, dep asset.Asset) asset.Asset{
|
|
reflect.TypeOf(manifests.AgentManifests{}): func(t *testing.T, dep asset.Asset) asset.Asset {
|
|
t.Helper()
|
|
am, ok := dep.(*manifests.AgentManifests)
|
|
assert.True(t, ok)
|
|
am.InfraEnv.Spec.AdditionalNTPSources = []string{"0.clock.ntp.org", "1.clock.ntp.org"}
|
|
return am
|
|
},
|
|
},
|
|
expectedFiles: generatedFiles("/etc/chrony.conf"),
|
|
expectedFileContent: map[string]string{
|
|
"/etc/chrony.conf": `server 0.clock.ntp.org iburst
|
|
server 1.clock.ntp.org iburst
|
|
makestep 1.0 3
|
|
rtcsync
|
|
logdir /var/log/chrony`,
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
deps := buildIgnitionAssetDefaultDependencies(t)
|
|
|
|
if tc.overrideAssets != nil {
|
|
for i, dep := range deps {
|
|
overrideFunc, found := tc.overrideAssets[reflect.TypeOf(dep).Elem()]
|
|
if !found {
|
|
continue
|
|
}
|
|
deps[i] = overrideFunc(t, dep)
|
|
}
|
|
}
|
|
|
|
parents := asset.Parents{}
|
|
parents.Add(deps...)
|
|
|
|
ignitionAsset := &Ignition{}
|
|
err := ignitionAsset.Generate(context.Background(), parents)
|
|
|
|
if tc.expectedError != "" {
|
|
assert.Equal(t, tc.expectedError, err.Error())
|
|
} else {
|
|
assert.NoError(t, err)
|
|
|
|
assert.Len(t, ignitionAsset.Config.Storage.Directories, 1)
|
|
assert.Equal(t, "/etc/assisted/extra-manifests", ignitionAsset.Config.Storage.Directories[0].Node.Path)
|
|
|
|
assertExpectedFiles(t, ignitionAsset.Config, tc.expectedFiles, tc.expectedFileContent)
|
|
|
|
assertServiceEnabled(t, ignitionAsset.Config, tc.serviceEnabledMap)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func skipTestIfnmstatectlIsMissing(t *testing.T) {
|
|
t.Helper()
|
|
// Generate calls addStaticNetworkConfig which calls nmstatectl
|
|
_, execErr := exec.LookPath("nmstatectl")
|
|
if execErr != nil {
|
|
t.Skip("No nmstatectl binary available")
|
|
}
|
|
}
|
|
|
|
func overrideDeps(deps []asset.Asset, overrides []asset.Asset) {
|
|
for _, od := range overrides {
|
|
for i, d := range deps {
|
|
if d.Name() == od.Name() {
|
|
deps[i] = od
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertServiceEnabled(t *testing.T, config *igntypes.Config, serviceEnabledMap map[string]bool) {
|
|
t.Helper()
|
|
for serviceName, enabled := range serviceEnabledMap {
|
|
for _, unit := range config.Systemd.Units {
|
|
if unit.Name == serviceName {
|
|
if unit.Enabled == nil {
|
|
assert.Equal(t, enabled, false)
|
|
} else {
|
|
assert.Equal(t, enabled, *unit.Enabled)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertExpectedFiles(t *testing.T, config *igntypes.Config, expectedFiles []string, expectedFileContent map[string]string) {
|
|
t.Helper()
|
|
if len(expectedFiles) > 0 {
|
|
assert.Equal(t, len(expectedFiles), len(config.Storage.Files))
|
|
|
|
for _, f := range expectedFiles {
|
|
found := false
|
|
for _, i := range config.Storage.Files {
|
|
if i.Node.Path == f {
|
|
if expectedData, ok := expectedFileContent[i.Node.Path]; ok {
|
|
actualData, err := dataurl.DecodeString(*i.FileEmbedded1.Contents.Source)
|
|
assert.NoError(t, err)
|
|
assert.Regexp(t, expectedData, string(actualData.Data))
|
|
}
|
|
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, fmt.Sprintf("Expected file %s not found", f))
|
|
}
|
|
}
|
|
}
|
|
|
|
// This test util create the minimum valid set of dependencies for the
|
|
// Ignition asset
|
|
func buildIgnitionAssetDefaultDependencies(t *testing.T) []asset.Asset {
|
|
t.Helper()
|
|
secretDataBytes, err := base64.StdEncoding.DecodeString("c3VwZXItc2VjcmV0Cg==")
|
|
assert.NoError(t, err)
|
|
|
|
return []asset.Asset{
|
|
&workflow.AgentWorkflow{Workflow: workflow.AgentWorkflowTypeInstall},
|
|
&joiner.ClusterInfo{},
|
|
&joiner.AddNodesConfig{},
|
|
&manifests.AgentManifests{
|
|
InfraEnv: &aiv1beta1.InfraEnv{
|
|
Spec: aiv1beta1.InfraEnvSpec{
|
|
SSHAuthorizedKey: "my-ssh-key",
|
|
},
|
|
},
|
|
ClusterDeployment: &hivev1.ClusterDeployment{
|
|
Spec: hivev1.ClusterDeploymentSpec{
|
|
ClusterName: "ostest",
|
|
BaseDomain: "ostest",
|
|
},
|
|
},
|
|
ClusterImageSet: &hivev1.ClusterImageSet{
|
|
Spec: hivev1.ClusterImageSetSpec{
|
|
ReleaseImage: "registry.ci.openshift.org/origin/release:4.11",
|
|
},
|
|
},
|
|
PullSecret: &v1.Secret{
|
|
Data: map[string][]byte{
|
|
".dockerconfigjson": secretDataBytes,
|
|
},
|
|
},
|
|
AgentClusterInstall: &hiveext.AgentClusterInstall{
|
|
Spec: hiveext.AgentClusterInstallSpec{
|
|
APIVIP: "192.168.111.5",
|
|
ProvisionRequirements: hiveext.ProvisionRequirements{
|
|
ControlPlaneAgents: 3,
|
|
WorkerAgents: 5,
|
|
ArbiterAgents: 1,
|
|
},
|
|
},
|
|
},
|
|
NMStateConfigs: []*aiv1beta1.NMStateConfig{
|
|
{
|
|
Spec: aiv1beta1.NMStateConfigSpec{
|
|
Interfaces: []*aiv1beta1.Interface{
|
|
{
|
|
Name: "eth0",
|
|
MacAddress: "00:01:02:03:04:05",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
StaticNetworkConfigs: []*models.HostStaticNetworkConfig{
|
|
{
|
|
MacInterfaceMap: models.MacInterfaceMap{
|
|
{LogicalNicName: "eth0", MacAddress: "00:01:02:03:04:05"},
|
|
},
|
|
NetworkYaml: "interfaces:\n- ipv4:\n address:\n - ip: 192.168.122.21\n prefix-length: 24\n enabled: true\n mac-address: 00:01:02:03:04:05\n name: eth0\n state: up\n type: ethernet\n",
|
|
},
|
|
},
|
|
},
|
|
&agentconfig.AgentConfig{
|
|
Config: &agent.Config{
|
|
RendezvousIP: "192.168.111.80",
|
|
},
|
|
File: &asset.File{
|
|
Filename: "/cluster-manifests/agent-config.yaml",
|
|
},
|
|
},
|
|
&agentconfig.AgentHosts{},
|
|
&manifests.ExtraManifests{},
|
|
&mirror.RegistriesConf{},
|
|
&mirror.CaBundle{},
|
|
&password.KubeadminPassword{},
|
|
&tls.KubeAPIServerLBSignerCertKey{},
|
|
&tls.KubeAPIServerLocalhostSignerCertKey{},
|
|
&tls.KubeAPIServerServiceNetworkSignerCertKey{},
|
|
&tls.AdminKubeConfigSignerCertKey{},
|
|
&tls.AdminKubeConfigClientCertKey{},
|
|
&gencrypto.AuthConfig{},
|
|
&common.InfraEnvID{},
|
|
}
|
|
}
|
|
|
|
func TestIgnition_getMirrorFromRelease(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
name string
|
|
release string
|
|
registriesConf mirror.RegistriesConf
|
|
expectedMirror string
|
|
}{
|
|
{
|
|
name: "no-mirror",
|
|
release: "registry.ci.openshift.org/ocp/release:latest",
|
|
registriesConf: mirror.RegistriesConf{},
|
|
expectedMirror: "",
|
|
},
|
|
{
|
|
name: "mirror-no-match",
|
|
release: "registry.ci.openshift.org/ocp/release:4.11.0-0.nightly-foo",
|
|
registriesConf: mirror.RegistriesConf{
|
|
File: &asset.File{
|
|
Filename: "registries.conf",
|
|
Data: []byte(""),
|
|
},
|
|
MirrorConfig: []mirror.RegistriesConfig{
|
|
{
|
|
Location: "some.registry.org/release",
|
|
Mirrors: []string{"some.mirror.org"},
|
|
},
|
|
},
|
|
},
|
|
expectedMirror: "",
|
|
},
|
|
{
|
|
name: "mirror-match",
|
|
release: "registry.ci.openshift.org/ocp/release:4.11.0-0.nightly-foo",
|
|
registriesConf: mirror.RegistriesConf{
|
|
File: &asset.File{
|
|
Filename: "registries.conf",
|
|
Data: []byte(""),
|
|
},
|
|
MirrorConfig: []mirror.RegistriesConfig{
|
|
{
|
|
Location: "registry.ci.openshift.org/ocp/release",
|
|
Mirrors: []string{"virthost.ostest.test.metalkube.org:5000/localimages/local-release-image"},
|
|
},
|
|
},
|
|
},
|
|
expectedMirror: "virthost.ostest.test.metalkube.org:5000/localimages/local-release-image:4.11.0-0.nightly-foo",
|
|
},
|
|
{
|
|
name: "mirror-match-with-checksum",
|
|
release: "quay.io/openshift-release-dev/ocp-release@sha256:300bce8246cf880e792e106607925de0a404484637627edf5f517375517d54a4",
|
|
registriesConf: mirror.RegistriesConf{
|
|
File: &asset.File{
|
|
Filename: "registries.conf",
|
|
Data: []byte(""),
|
|
},
|
|
MirrorConfig: []mirror.RegistriesConfig{
|
|
{
|
|
Location: "quay.io/openshift-release-dev/ocp-v4.0-art-dev",
|
|
Mirrors: []string{"localhost:5000/openshift4/openshift/release"},
|
|
},
|
|
{
|
|
Location: "quay.io/openshift-release-dev/ocp-release",
|
|
Mirrors: []string{"localhost:5000/openshift-release-dev/ocp-release"},
|
|
},
|
|
},
|
|
},
|
|
expectedMirror: "localhost:5000/openshift-release-dev/ocp-release@sha256:300bce8246cf880e792e106607925de0a404484637627edf5f517375517d54a4",
|
|
},
|
|
{
|
|
name: "mirror-match-multiple-mirrors",
|
|
release: "registry.ci.openshift.org/ocp/release:4.18.0-0.nightly-foo",
|
|
registriesConf: mirror.RegistriesConf{
|
|
File: &asset.File{
|
|
Filename: "registries.conf",
|
|
Data: []byte(""),
|
|
},
|
|
MirrorConfig: []mirror.RegistriesConfig{
|
|
{
|
|
Location: "quay.io/openshift-release-dev/ocp-v4.0-art-dev",
|
|
Mirrors: []string{"virthost.ostest.test.metalkube.org:5000/localimages/ocp-release",
|
|
"virthost.ostest.test.metalkube.org:5000/localimages/local-release-image"},
|
|
},
|
|
{
|
|
Location: "registry.ci.openshift.org/ocp/release",
|
|
Mirrors: []string{"virthost.ostest.test.metalkube.org:5000/localimages/ocp-release",
|
|
"virthost.ostest.test.metalkube.org:5000/localimages/local-release-image"},
|
|
},
|
|
},
|
|
},
|
|
expectedMirror: "virthost.ostest.test.metalkube.org:5000/localimages/ocp-release:4.18.0-0.nightly-foo",
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
mirror := mirror.GetMirrorFromRelease(tc.release, &tc.registriesConf)
|
|
|
|
assert.Equal(t, tc.expectedMirror, mirror)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIgnition_getPublicContainerRegistries(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
name string
|
|
registriesConf mirror.RegistriesConf
|
|
expectedRegistries string
|
|
}{
|
|
{
|
|
name: "no-mirror",
|
|
registriesConf: mirror.RegistriesConf{},
|
|
expectedRegistries: "quay.io",
|
|
},
|
|
{
|
|
name: "mirror-one-entry",
|
|
registriesConf: mirror.RegistriesConf{
|
|
File: &asset.File{
|
|
Filename: "registries.conf",
|
|
Data: []byte(""),
|
|
},
|
|
MirrorConfig: []mirror.RegistriesConfig{
|
|
{
|
|
Location: "some.registry.org/release",
|
|
Mirrors: []string{"some.mirror.org"},
|
|
},
|
|
},
|
|
},
|
|
expectedRegistries: "some.registry.org",
|
|
},
|
|
{
|
|
name: "mirror-multiple-entries",
|
|
registriesConf: mirror.RegistriesConf{
|
|
File: &asset.File{
|
|
Filename: "registries.conf",
|
|
Data: []byte(""),
|
|
},
|
|
MirrorConfig: []mirror.RegistriesConfig{
|
|
{
|
|
Location: "quay.io/openshift-release-dev/ocp-v4.0-art-dev",
|
|
Mirrors: []string{"localhost:5000/openshift4/openshift/release"},
|
|
},
|
|
{
|
|
Location: "registry.ci.openshift.org/ocp/release",
|
|
Mirrors: []string{"localhost:5000/openshift-release-dev/ocp-release"},
|
|
},
|
|
},
|
|
},
|
|
expectedRegistries: "quay.io,registry.ci.openshift.org",
|
|
},
|
|
{
|
|
name: "duplicate-entries",
|
|
registriesConf: mirror.RegistriesConf{
|
|
File: &asset.File{
|
|
Filename: "registries.conf",
|
|
Data: []byte(""),
|
|
},
|
|
MirrorConfig: []mirror.RegistriesConfig{
|
|
{
|
|
Location: "registry.ci.openshift.org/ocp-v4.0-art-dev",
|
|
Mirrors: []string{"localhost:5000/openshift4/openshift/release"},
|
|
},
|
|
{
|
|
Location: "registry.ci.openshift.org/ocp/release",
|
|
Mirrors: []string{"localhost:5000/openshift-release-dev/ocp-release"},
|
|
},
|
|
},
|
|
},
|
|
expectedRegistries: "registry.ci.openshift.org",
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
publicContainerRegistries := getPublicContainerRegistries(&tc.registriesConf)
|
|
|
|
assert.Equal(t, tc.expectedRegistries, publicContainerRegistries)
|
|
})
|
|
}
|
|
}
|