1
0
mirror of https://github.com/openshift/installer.git synced 2026-02-05 15:47:14 +01:00

OCPBUGS-38956: failed to install Nutanix OCP 4.16 cluster with DHCP

This commit is contained in:
Yanhua Li
2024-08-26 16:38:53 -04:00
parent 0b2e4425f0
commit 3af7850d43
9 changed files with 128 additions and 24 deletions

View File

@@ -22,6 +22,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/vincent-petithory/dataurl"
utilsnet "k8s.io/utils/net"
"k8s.io/utils/ptr"
configv1 "github.com/openshift/api/config/v1"
"github.com/openshift/installer/data"
@@ -39,6 +40,7 @@ import (
"github.com/openshift/installer/pkg/asset/tls"
"github.com/openshift/installer/pkg/types"
baremetaltypes "github.com/openshift/installer/pkg/types/baremetal"
nutanixtypes "github.com/openshift/installer/pkg/types/nutanix"
vspheretypes "github.com/openshift/installer/pkg/types/vsphere"
)
@@ -111,6 +113,7 @@ func (a *Common) Dependencies() []asset.Asset {
&baremetal.IronicCreds{},
&CVOIgnore{},
&installconfig.InstallConfig{},
&installconfig.ClusterID{},
&kubeconfig.AdminInternalClient{},
&kubeconfig.Kubelet{},
&kubeconfig.LoopbackClient{},
@@ -167,7 +170,8 @@ func (a *Common) Dependencies() []asset.Asset {
func (a *Common) generateConfig(dependencies asset.Parents, templateData *bootstrapTemplateData) error {
installConfig := &installconfig.InstallConfig{}
bootstrapSSHKeyPair := &tls.BootstrapSSHKeyPair{}
dependencies.Get(installConfig, bootstrapSSHKeyPair)
clusterID := &installconfig.ClusterID{}
dependencies.Get(installConfig, bootstrapSSHKeyPair, clusterID)
a.Config = &igntypes.Config{
Ignition: igntypes.Ignition{
@@ -218,6 +222,24 @@ func (a *Common) generateConfig(dependencies asset.Parents, templateData *bootst
}},
)
if platform == nutanixtypes.Name {
// Inserts the file "/etc/hostname" with the bootstrap machine name to the bootstrap ignition data
hostname := fmt.Sprintf("%s-bootstrap", clusterID.InfraID)
hostnameFile := igntypes.File{
Node: igntypes.Node{
Path: "/etc/hostname",
Overwrite: ptr.To(true),
},
FileEmbedded1: igntypes.FileEmbedded1{
Mode: ptr.To(420),
Contents: igntypes.Resource{
Source: ptr.To(dataurl.EncodeBytes([]byte(hostname))),
},
},
}
a.Config.Storage.Files = append(a.Config.Storage.Files, hostnameFile)
}
return nil
}

View File

@@ -61,7 +61,7 @@ func GenerateMachines(clusterID string, config *types.InstallConfig, pool *types
Spec: capv1.MachineSpec{
ClusterName: clusterID,
Bootstrap: capv1.Bootstrap{
DataSecretName: ptr.To(fmt.Sprintf("%s-%s", clusterID, role)),
DataSecretName: ptr.To(ntxMachine.Name),
},
InfrastructureRef: v1.ObjectReference{
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",

View File

@@ -17,6 +17,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
"github.com/coreos/stream-metadata-go/arch"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
"k8s.io/utils/ptr"
capz "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -512,7 +513,7 @@ func randomString(length int) string {
// Ignition provisions the Azure container that holds the bootstrap ignition
// file.
func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([]byte, error) {
func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([]*corev1.Secret, error) {
session, err := in.InstallConfig.Azure.Session()
if err != nil {
return nil, fmt.Errorf("failed to get session: %w", err)
@@ -557,5 +558,10 @@ func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([]
return nil, fmt.Errorf("failed to create ignition shim: %w", err)
}
return ignShim, nil
ignSecrets := []*corev1.Secret{
clusterapi.IgnitionSecret(ignShim, in.InfraID, "bootstrap"),
clusterapi.IgnitionSecret(in.MasterIgnData, in.InfraID, "master"),
}
return ignSecrets, nil
}

View File

@@ -249,10 +249,12 @@ func (i *InfraProvider) Provision(ctx context.Context, dir string, parents asset
logrus.Debugf("No infrastructure ready requirements for the %s provider", i.impl.Name())
}
masterIgnData := masterIgnAsset.Files()[0].Data
bootstrapIgnData, err := injectInstallInfo(bootstrapIgnAsset.Files()[0].Data)
if err != nil {
return fileList, fmt.Errorf("unable to inject installation info: %w", err)
}
ignitionSecrets := []*corev1.Secret{}
// The cloud-platform may need to override the default
// bootstrap ignition behavior.
@@ -260,22 +262,27 @@ func (i *InfraProvider) Provision(ctx context.Context, dir string, parents asset
ignInput := IgnitionInput{
Client: cl,
BootstrapIgnData: bootstrapIgnData,
MasterIgnData: masterIgnData,
InfraID: clusterID.InfraID,
InstallConfig: installConfig,
TFVarsAsset: tfvarsAsset,
}
timer.StartTimer(ignitionStage)
if bootstrapIgnData, err = p.Ignition(ctx, ignInput); err != nil {
if ignitionSecrets, err = p.Ignition(ctx, ignInput); err != nil {
return fileList, fmt.Errorf("failed preparing ignition data: %w", err)
}
timer.StopTimer(ignitionStage)
} else {
logrus.Debugf("No Ignition requirements for the %s provider", i.impl.Name())
logrus.Debugf("Using default ignition for the %s provider", i.impl.Name())
bootstrapIgnSecret := IgnitionSecret(bootstrapIgnData, clusterID.InfraID, "bootstrap")
masterIgnSecret := IgnitionSecret(masterIgnData, clusterID.InfraID, "master")
ignitionSecrets = append(ignitionSecrets, bootstrapIgnSecret, masterIgnSecret)
}
for _, secret := range ignitionSecrets {
machineManifests = append(machineManifests, secret)
}
bootstrapIgnSecret := IgnitionSecret(bootstrapIgnData, clusterID.InfraID, "bootstrap")
masterIgnSecret := IgnitionSecret(masterIgnAsset.Files()[0].Data, clusterID.InfraID, "master")
machineManifests = append(machineManifests, bootstrapIgnSecret, masterIgnSecret)
// Create the machine manifests.
timer.StartTimer(machineStage)

View File

@@ -4,6 +4,7 @@ import (
"context"
"time"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/openshift/installer/pkg/asset/cluster/tfvars"
@@ -46,18 +47,20 @@ type PreProvisionInput struct {
WorkersAsset *machines.Worker
}
// IgnitionProvider handles preconditions for bootstrap ignition and
// generates ignition data for the CAPI bootstrap ignition secret.
// IgnitionProvider handles preconditions for bootstrap ignition,
// such as pushing to cloud storage. Returns bootstrap and master
// ignition secrets.
//
// WARNING! Low-level primitive. Use only if absolutely necessary.
type IgnitionProvider interface {
Ignition(ctx context.Context, in IgnitionInput) ([]byte, error)
Ignition(ctx context.Context, in IgnitionInput) ([]*corev1.Secret, error)
}
// IgnitionInput collects the args passed to the IgnitionProvider call.
type IgnitionInput struct {
Client client.Client
BootstrapIgnData []byte
MasterIgnData []byte
InfraID string
InstallConfig *installconfig.InstallConfig
TFVarsAsset *tfvars.TerraformVariables

View File

@@ -8,6 +8,7 @@ import (
"strings"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
capg "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -76,7 +77,7 @@ func (p Provider) PreProvision(ctx context.Context, in clusterapi.PreProvisionIn
// populate the metadata field of the bootstrap instance as the data can be too large. Instead, the data is
// added to a bucket. A signed url is generated to point to the bucket and the ignition data will be
// updated to point to the url. This is also allows for bootstrap data to be edited after its initial creation.
func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([]byte, error) {
func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([]*corev1.Secret, error) {
// Create the bucket and presigned url. The url is generated using a known/expected name so that the
// url can be retrieved from the api by this name.
bucketName := gcp.GetBootstrapStorageName(in.InfraID)
@@ -104,6 +105,7 @@ func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([]
return nil, fmt.Errorf("ignition failed to fill bucket: %w", err)
}
var ignShim string
for _, file := range in.TFVarsAsset.Files() {
if file.Filename == tfvars.TfPlatformVarsFileName {
var found bool
@@ -113,16 +115,19 @@ func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([]
return nil, fmt.Errorf("failed to unmarshal %s to json: %w", tfvars.TfPlatformVarsFileName, err)
}
ignShim, found := tfvarsData["gcp_ignition_shim"].(string)
ignShim, found = tfvarsData["gcp_ignition_shim"].(string)
if !found {
return nil, fmt.Errorf("failed to find ignition shim")
}
return []byte(ignShim), nil
}
}
return nil, fmt.Errorf("failed to complete ignition process")
ignSecrets := []*corev1.Secret{
clusterapi.IgnitionSecret([]byte(ignShim), in.InfraID, "bootstrap"),
clusterapi.IgnitionSecret(in.MasterIgnData, in.InfraID, "master"),
}
return ignSecrets, nil
}
// InfraReady is called once cluster.Status.InfrastructureReady

View File

@@ -8,6 +8,7 @@ import (
nutanixclientv3 "github.com/nutanix-cloud-native/prism-go-client/v3"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/utils/ptr"
@@ -120,10 +121,10 @@ func (p Provider) PreProvision(ctx context.Context, in infracapi.PreProvisionInp
return nil
}
// Ignition handles preconditions for bootstrap ignition and
// generates ignition data for the CAPI bootstrap ignition secret.
// Ignition handles preconditions for bootstrap and master ignition and
// generates the CAPI ignition data secrets.
// Load the ignition iso image to prism_central.
func (p Provider) Ignition(ctx context.Context, in infracapi.IgnitionInput) ([]byte, error) {
func (p Provider) Ignition(ctx context.Context, in infracapi.IgnitionInput) ([]*corev1.Secret, error) {
ic := in.InstallConfig.Config
nutanixCl, err := nutanixtypes.CreateNutanixClientFromPlatform(ic.Platform.Nutanix)
if err != nil {
@@ -207,11 +208,25 @@ func (p Provider) Ignition(ctx context.Context, in infracapi.IgnitionInput) ([]b
err = fmt.Errorf("failed to create/upload the bootstrap image object %s in PC: %w", imgName, err)
}
return in.BootstrapIgnData, err
return nil, err
}
logrus.Infof("Successfully created the bootstrap image object %s and uploaded its image data", imgName)
return in.BootstrapIgnData, nil
ignSecrets := []*corev1.Secret{
infracapi.IgnitionSecret(in.BootstrapIgnData, in.InfraID, "bootstrap"),
}
for i := 0; i < int(*in.InstallConfig.Config.ControlPlane.Replicas); i++ {
// Inserts the file "/etc/hostname" with the master machine name to the ignition data
hostname := fmt.Sprintf("%s-master-%v", in.InfraID, i)
masterIgnData, err := nutanixtypes.InsertHostnameIgnition(in.MasterIgnData, hostname)
if err != nil {
return nil, fmt.Errorf("failed to insert the file '/etc/hostname' to the ignition data for machine %q: %w", hostname, err)
}
ignSecrets = append(ignSecrets, infracapi.IgnitionSecret(masterIgnData, in.InfraID, fmt.Sprintf("master-%v", i)))
}
return ignSecrets, nil
}
// createImage creates the image object in PC, with the provided request input.

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
capo "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
@@ -131,7 +132,7 @@ func (p Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput)
var _ clusterapi.IgnitionProvider = Provider{}
// Ignition uploads the bootstrap machine's Ignition file to OpenStack.
func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([]byte, error) {
func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([]*corev1.Secret, error) {
logrus.Debugf("Uploading the bootstrap machine's Ignition file to OpenStack")
var (
bootstrapIgnData = in.BootstrapIgnData
@@ -139,7 +140,16 @@ func (p Provider) Ignition(ctx context.Context, in clusterapi.IgnitionInput) ([]
installConfig = in.InstallConfig
)
return preprovision.UploadIgnitionAndBuildShim(ctx, installConfig.Config.Platform.OpenStack.Cloud, infraID, installConfig.Config.Proxy, bootstrapIgnData)
ignShim, err := preprovision.UploadIgnitionAndBuildShim(ctx, installConfig.Config.Platform.OpenStack.Cloud, infraID, installConfig.Config.Proxy, bootstrapIgnData)
if err != nil {
return nil, fmt.Errorf("failed to upload and build ignition shim: %w", err)
}
ignSecrets := []*corev1.Secret{
clusterapi.IgnitionSecret(ignShim, in.InfraID, "bootstrap"),
clusterapi.IgnitionSecret(in.MasterIgnData, in.InfraID, "master"),
}
return ignSecrets, nil
}
var _ clusterapi.PostProvider = Provider{}

View File

@@ -9,11 +9,14 @@ import (
"strings"
"time"
igntypes "github.com/coreos/ignition/v2/config/v3_2/types"
"github.com/google/uuid"
"github.com/kdomanski/iso9660"
"github.com/nutanix-cloud-native/prism-go-client/utils"
nutanixclientv3 "github.com/nutanix-cloud-native/prism-go-client/v3"
"github.com/pkg/errors"
"github.com/vincent-petithory/dataurl"
"k8s.io/utils/ptr"
)
const (
@@ -194,3 +197,36 @@ func CategoryKey(infraID string) string {
categoryKey := fmt.Sprintf("%s%s", categoryKeyPrefix, infraID)
return categoryKey
}
// InsertHostnameIgnition inserts the file "/etc/hostname" with the given hostname to the provided Ignition config data.
func InsertHostnameIgnition(ignData []byte, hostname string) ([]byte, error) {
ignConfig := &igntypes.Config{}
if err := json.Unmarshal(ignData, &ignConfig); err != nil {
return nil, fmt.Errorf("failed to unmarshal Ignition config: %w", err)
}
hostnameFile := igntypes.File{
Node: igntypes.Node{
Path: "/etc/hostname",
Overwrite: ptr.To(true),
},
FileEmbedded1: igntypes.FileEmbedded1{
Mode: ptr.To(420),
Contents: igntypes.Resource{
Source: ptr.To(dataurl.EncodeBytes([]byte(hostname))),
},
},
}
if ignConfig.Storage.Files == nil {
ignConfig.Storage.Files = make([]igntypes.File, 0)
}
ignConfig.Storage.Files = append(ignConfig.Storage.Files, hostnameFile)
ign, err := json.Marshal(ignConfig)
if err != nil {
return nil, fmt.Errorf("failed to marshal ignition data: %w", err)
}
return ign, nil
}