github.com/openshift/installer@v1.4.17/pkg/asset/imagebased/configimage/imagebased_config.go (about)

     1  package configimage
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"github.com/sirupsen/logrus"
    10  	"k8s.io/apimachinery/pkg/util/validation/field"
    11  	"sigs.k8s.io/yaml"
    12  
    13  	"github.com/openshift/installer/pkg/asset"
    14  	"github.com/openshift/installer/pkg/asset/agent/manifests/staticnetworkconfig"
    15  	"github.com/openshift/installer/pkg/types/imagebased"
    16  	"github.com/openshift/installer/pkg/validate"
    17  )
    18  
    19  const (
    20  	imageBasedConfigFilename = "image-based-config.yaml"
    21  )
    22  
    23  // ImageBasedConfig reads the image-based-config.yaml file.
    24  type ImageBasedConfig struct {
    25  	File     *asset.File
    26  	Config   *imagebased.Config
    27  	Template string
    28  }
    29  
    30  var _ asset.WritableAsset = (*ImageBasedConfig)(nil)
    31  
    32  // Name returns a human friendly name for the asset.
    33  func (*ImageBasedConfig) Name() string {
    34  	return "Image-based Config ISO configuration"
    35  }
    36  
    37  // Dependencies returns all of the dependencies directly needed to generate
    38  // the asset.
    39  func (*ImageBasedConfig) Dependencies() []asset.Asset {
    40  	return []asset.Asset{}
    41  }
    42  
    43  // Generate generates the Image-based Config manifest.
    44  func (i *ImageBasedConfig) Generate(_ context.Context, dependencies asset.Parents) error {
    45  	imageBasedConfigTemplate := `#
    46  # Note: This is a sample ImageBasedConfig file showing
    47  # which fields are available to aid you in creating your
    48  # own image-based-config.yaml file.
    49  #
    50  apiVersion: v1beta1
    51  kind: ImageBasedConfig
    52  metadata:
    53    name: example-image-based-config
    54  additionalNTPSources:
    55    - 0.rhel.pool.ntp.org
    56    - 1.rhel.pool.ntp.org
    57  hostname: change-to-hostname
    58  releaseRegistry: quay.io
    59  # networkConfig contains the network configuration for the host in NMState format.
    60  # See https://nmstate.io/examples.html for examples.
    61  networkConfig:
    62    interfaces:
    63      - name: eth0
    64        type: ethernet
    65        state: up
    66        mac-address: 00:00:00:00:00:00
    67        ipv4:
    68          enabled: true
    69          address:
    70            - ip: 192.168.122.2
    71              prefix-length: 23
    72          dhcp: false
    73  `
    74  
    75  	i.Template = imageBasedConfigTemplate
    76  
    77  	// Set the File field correctly with the generated image-based install config YAML content
    78  	i.File = &asset.File{
    79  		Filename: imageBasedConfigFilename,
    80  		Data:     []byte(i.Template),
    81  	}
    82  
    83  	return nil
    84  }
    85  
    86  // PersistToFile writes the image-based-config.yaml file to the assets folder.
    87  func (i *ImageBasedConfig) PersistToFile(directory string) error {
    88  	if i.File == nil {
    89  		return nil
    90  	}
    91  
    92  	configPath := filepath.Join(directory, imageBasedConfigFilename)
    93  	err := os.WriteFile(configPath, i.File.Data, 0o600)
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  // Files returns the files generated by the asset.
   102  func (i *ImageBasedConfig) Files() []*asset.File {
   103  	if i.File != nil {
   104  		return []*asset.File{i.File}
   105  	}
   106  	return []*asset.File{}
   107  }
   108  
   109  // Load returns the image-based config asset from the disk.
   110  func (i *ImageBasedConfig) Load(f asset.FileFetcher) (bool, error) {
   111  	file, err := f.FetchByName(imageBasedConfigFilename)
   112  	if err != nil {
   113  		if os.IsNotExist(err) {
   114  			return false, nil
   115  		}
   116  		return false, fmt.Errorf("failed to load %s file: %w", imageBasedConfigFilename, err)
   117  	}
   118  
   119  	config := &imagebased.Config{}
   120  	if err := yaml.UnmarshalStrict(file.Data, config); err != nil {
   121  		return false, fmt.Errorf("failed to unmarshal %s: %w", imageBasedConfigFilename, err)
   122  	}
   123  
   124  	i.File, i.Config = file, config
   125  
   126  	if err = i.finish(); err != nil {
   127  		return false, err
   128  	}
   129  
   130  	return true, nil
   131  }
   132  
   133  func (i *ImageBasedConfig) finish() error {
   134  	if err := i.validateImageBasedConfig().ToAggregate(); err != nil {
   135  		return fmt.Errorf("invalid Image-based Config configuration: %w", err)
   136  	}
   137  	return nil
   138  }
   139  
   140  func (i *ImageBasedConfig) validateImageBasedConfig() field.ErrorList {
   141  	var allErrs field.ErrorList
   142  
   143  	if err := i.validateAdditionalNTPSources(); err != nil {
   144  		allErrs = append(allErrs, err...)
   145  	}
   146  
   147  	if err := i.validateNetworkConfig(); err != nil {
   148  		allErrs = append(allErrs, err...)
   149  	}
   150  
   151  	return allErrs
   152  }
   153  
   154  func (i *ImageBasedConfig) validateNetworkConfig() field.ErrorList {
   155  	var allErrs field.ErrorList
   156  
   157  	// empty NetworkConfig is fine
   158  	if i.Config.NetworkConfig.String() == "" {
   159  		return nil
   160  	}
   161  
   162  	networkConfig := field.NewPath("networkConfig")
   163  
   164  	staticNetworkConfigGenerator := staticnetworkconfig.New(logrus.StandardLogger(), staticnetworkconfig.Config{MaxConcurrentGenerations: 2})
   165  
   166  	// Validate the network config using nmstatectl
   167  	if err := staticNetworkConfigGenerator.ValidateNMStateYaml(context.Background(), i.Config.NetworkConfig.String()); err != nil {
   168  		allErrs = append(allErrs, field.Invalid(networkConfig, i.Config.NetworkConfig, err.Error()))
   169  	}
   170  
   171  	return allErrs
   172  }
   173  
   174  func (i *ImageBasedConfig) validateAdditionalNTPSources() field.ErrorList {
   175  	var allErrs field.ErrorList
   176  
   177  	additionalNTPSourcesPath := field.NewPath("additionalNTPSources")
   178  
   179  	for i, source := range i.Config.AdditionalNTPSources {
   180  		if domainNameErr := validate.DomainName(source, true); domainNameErr != nil {
   181  			if ipErr := validate.IP(source); ipErr != nil {
   182  				allErrs = append(allErrs, field.Invalid(additionalNTPSourcesPath.Index(i), source, "NTP source is not a valid domain name nor a valid IP"))
   183  			}
   184  		}
   185  	}
   186  
   187  	return allErrs
   188  }