github.com/openshift/installer@v1.4.17/pkg/infrastructure/gcp/clusterapi/ignition.go (about)

     1  package clusterapi
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"fmt"
     8  	"strings"
     9  	"time"
    10  
    11  	igntypes "github.com/coreos/ignition/v2/config/v3_2/types"
    12  	capg "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1"
    13  	"sigs.k8s.io/controller-runtime/pkg/client"
    14  	"sigs.k8s.io/yaml"
    15  
    16  	configv1 "github.com/openshift/api/config/v1"
    17  	"github.com/openshift/installer/pkg/asset/manifests/capiutils"
    18  	"github.com/openshift/installer/pkg/infrastructure/clusterapi"
    19  	"github.com/openshift/installer/pkg/types/gcp"
    20  )
    21  
    22  const (
    23  	infrastructureFilepath = "/opt/openshift/manifests/cluster-infrastructure-02-config.yml"
    24  
    25  	// replaceable is the string that precedes the encoded data in the ignition data.
    26  	// The data must be replaced before decoding the string, and the string must be
    27  	// prepended to the encoded data.
    28  	replaceable = "data:text/plain;charset=utf-8;base64,"
    29  )
    30  
    31  // EditIgnition attempts to edit the contents of the bootstrap ignition when the user has selected
    32  // a custom DNS configuration. Find the public and private load balancer addresses and fill in the
    33  // infrastructure file within the ignition struct.
    34  func EditIgnition(ctx context.Context, in clusterapi.IgnitionInput) ([]byte, error) {
    35  	ctx, cancel := context.WithTimeout(ctx, time.Minute*2)
    36  	defer cancel()
    37  
    38  	if in.InstallConfig.Config.GCP.UserProvisionedDNS == gcp.UserProvisionedDNSEnabled {
    39  		gcpCluster := &capg.GCPCluster{}
    40  		key := client.ObjectKey{
    41  			Name:      in.InfraID,
    42  			Namespace: capiutils.Namespace,
    43  		}
    44  		if err := in.Client.Get(ctx, key, gcpCluster); err != nil {
    45  			return nil, fmt.Errorf("failed to get GCP cluster: %w", err)
    46  		}
    47  
    48  		// public load balancer and health check are created by capi gcp provider.
    49  		// TODO: this is currently a global address
    50  		apiIPAddress := *gcpCluster.Status.Network.APIServerAddress
    51  
    52  		ignData := &igntypes.Config{}
    53  		err := json.Unmarshal(in.BootstrapIgnData, ignData)
    54  		if err != nil {
    55  			return nil, fmt.Errorf("failed to unmarshal bootstrap ignition: %w", err)
    56  		}
    57  
    58  		apiIntIPAddress, err := getInternalLBAddress(ctx, in.InstallConfig.Config.GCP.ProjectID, in.InstallConfig.Config.GCP.Region, getAPIAddressName(in.InfraID))
    59  		if err != nil {
    60  			return nil, fmt.Errorf("failed to create the internal load balancer address: %w", err)
    61  		}
    62  
    63  		err = addLoadBalancersToInfra(gcp.Name, ignData, []string{apiIPAddress}, []string{apiIntIPAddress})
    64  		if err != nil {
    65  			return nil, fmt.Errorf("failed to add load balancers to ignition config: %w", err)
    66  		}
    67  
    68  		editedIgnBytes, err := json.Marshal(ignData)
    69  		if err != nil {
    70  			return nil, fmt.Errorf("failed to convert ignition data to json: %w", err)
    71  		}
    72  
    73  		return editedIgnBytes, nil
    74  	}
    75  
    76  	return nil, nil
    77  }
    78  
    79  // addLoadBalancersToInfra will load the public and private load balancer information into
    80  // the infrastructure CR. This will occur after the data has already been inserted into the
    81  // ignition file.
    82  func addLoadBalancersToInfra(platform string, config *igntypes.Config, publicLBs []string, privateLBs []string) error {
    83  	for i, fileData := range config.Storage.Files {
    84  		// update the contents of this file
    85  		if fileData.Path == infrastructureFilepath {
    86  			contents := config.Storage.Files[i].Contents.Source
    87  			replaced := strings.Replace(*contents, replaceable, "", 1)
    88  
    89  			rawDecodedText, err := base64.StdEncoding.DecodeString(replaced)
    90  			if err != nil {
    91  				return fmt.Errorf("failed to decode contents of ignition file: %w", err)
    92  			}
    93  
    94  			infra := &configv1.Infrastructure{}
    95  			if err := yaml.Unmarshal(rawDecodedText, infra); err != nil {
    96  				return fmt.Errorf("failed to unmarshal infrastructure: %w", err)
    97  			}
    98  
    99  			// convert the list of strings to a list of IPs
   100  			apiIntLbs := []configv1.IP{}
   101  			for _, ip := range privateLBs {
   102  				apiIntLbs = append(apiIntLbs, configv1.IP(ip))
   103  			}
   104  			apiLbs := []configv1.IP{}
   105  			for _, ip := range publicLBs {
   106  				apiLbs = append(apiLbs, configv1.IP(ip))
   107  			}
   108  			cloudLBInfo := configv1.CloudLoadBalancerIPs{
   109  				APIIntLoadBalancerIPs: apiIntLbs,
   110  				APILoadBalancerIPs:    apiLbs,
   111  			}
   112  
   113  			if infra.Status.PlatformStatus.GCP.CloudLoadBalancerConfig.DNSType == configv1.ClusterHostedDNSType {
   114  				infra.Status.PlatformStatus.GCP.CloudLoadBalancerConfig.ClusterHosted = &cloudLBInfo
   115  			}
   116  
   117  			// convert the infrastructure back to an encoded string
   118  			infraContents, err := yaml.Marshal(infra)
   119  			if err != nil {
   120  				return fmt.Errorf("failed to marshal infrastructure: %w", err)
   121  			}
   122  
   123  			encoded := fmt.Sprintf("%s%s", replaceable, base64.StdEncoding.EncodeToString(infraContents))
   124  			// replace the contents with the edited information
   125  			config.Storage.Files[i].Contents.Source = &encoded
   126  
   127  			break
   128  		}
   129  	}
   130  
   131  	return nil
   132  }