github.com/openshift/installer@v1.4.17/pkg/asset/agent/manifests/clusterimageset.go (about)

     1  package manifests
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"github.com/pkg/errors"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"k8s.io/apimachinery/pkg/util/validation/field"
    12  	"sigs.k8s.io/yaml"
    13  
    14  	hivev1 "github.com/openshift/hive/apis/hive/v1"
    15  	"github.com/openshift/installer/pkg/asset"
    16  	"github.com/openshift/installer/pkg/asset/agent"
    17  	"github.com/openshift/installer/pkg/asset/agent/joiner"
    18  	"github.com/openshift/installer/pkg/asset/agent/workflow"
    19  	"github.com/openshift/installer/pkg/asset/releaseimage"
    20  	"github.com/openshift/installer/pkg/version"
    21  )
    22  
    23  var (
    24  	clusterImageSetFilename = filepath.Join(clusterManifestDir, "cluster-image-set.yaml")
    25  )
    26  
    27  // ClusterImageSet generates the cluster-image-set.yaml file.
    28  type ClusterImageSet struct {
    29  	File   *asset.File
    30  	Config *hivev1.ClusterImageSet
    31  }
    32  
    33  var _ asset.WritableAsset = (*ClusterImageSet)(nil)
    34  
    35  // Name returns a human friendly name for the asset.
    36  func (*ClusterImageSet) Name() string {
    37  	return "ClusterImageSet Config"
    38  }
    39  
    40  // Dependencies returns all of the dependencies directly needed to generate
    41  // the asset.
    42  func (*ClusterImageSet) Dependencies() []asset.Asset {
    43  	return []asset.Asset{
    44  		&workflow.AgentWorkflow{},
    45  		&joiner.ClusterInfo{},
    46  		&releaseimage.Image{},
    47  		&agent.OptionalInstallConfig{},
    48  	}
    49  }
    50  
    51  // Generate generates the ClusterImageSet manifest.
    52  func (a *ClusterImageSet) Generate(ctx context.Context, dependencies asset.Parents) error {
    53  	agentWorkflow := &workflow.AgentWorkflow{}
    54  	clusterInfo := &joiner.ClusterInfo{}
    55  	releaseImage := &releaseimage.Image{}
    56  	installConfig := &agent.OptionalInstallConfig{}
    57  	dependencies.Get(releaseImage, installConfig, agentWorkflow, clusterInfo)
    58  
    59  	switch agentWorkflow.Workflow {
    60  	case workflow.AgentWorkflowTypeInstall:
    61  		currentVersion, err := version.Version()
    62  		if err != nil {
    63  			return err
    64  		}
    65  
    66  		clusterNamespace := ""
    67  		if installConfig.Config != nil {
    68  			clusterNamespace = installConfig.ClusterNamespace()
    69  		}
    70  		err = a.generateManifest(currentVersion, releaseImage.PullSpec, clusterNamespace)
    71  		if err != nil {
    72  			return err
    73  		}
    74  	case workflow.AgentWorkflowTypeAddNodes:
    75  		return a.generateManifest(clusterInfo.Version, clusterInfo.ReleaseImage, clusterInfo.Namespace)
    76  
    77  	default:
    78  		return fmt.Errorf("AgentWorkflowType value not supported: %s", agentWorkflow.Workflow)
    79  	}
    80  
    81  	return a.finish(ctx, agentWorkflow.Workflow)
    82  }
    83  
    84  func (a *ClusterImageSet) generateManifest(version, releaseImage, clusterNamespace string) error {
    85  	clusterImageSet := &hivev1.ClusterImageSet{
    86  		TypeMeta: metav1.TypeMeta{
    87  			Kind:       "ClusterImageSet",
    88  			APIVersion: hivev1.SchemeGroupVersion.String(),
    89  		},
    90  		ObjectMeta: metav1.ObjectMeta{
    91  			Name:      fmt.Sprintf("openshift-%s", version),
    92  			Namespace: clusterNamespace,
    93  		},
    94  		Spec: hivev1.ClusterImageSetSpec{
    95  			ReleaseImage: releaseImage,
    96  		},
    97  	}
    98  
    99  	configData, err := yaml.Marshal(clusterImageSet)
   100  	if err != nil {
   101  		return errors.Wrap(err, "failed to marshal agent cluster image set")
   102  	}
   103  
   104  	a.Config = clusterImageSet
   105  	a.File = &asset.File{
   106  		Filename: clusterImageSetFilename,
   107  		Data:     configData,
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  // Files returns the files generated by the asset.
   114  func (a *ClusterImageSet) Files() []*asset.File {
   115  	if a.File != nil {
   116  		return []*asset.File{a.File}
   117  	}
   118  	return []*asset.File{}
   119  }
   120  
   121  // Load returns ClusterImageSet asset from the disk.
   122  func (a *ClusterImageSet) Load(f asset.FileFetcher) (bool, error) {
   123  	ctx := context.TODO()
   124  
   125  	agentWorkflow := &workflow.AgentWorkflow{}
   126  	_, err := agentWorkflow.Load(f)
   127  	if err != nil {
   128  		return false, err
   129  	}
   130  
   131  	clusterImageSetFile, err := f.FetchByName(clusterImageSetFilename)
   132  	if err != nil {
   133  		if os.IsNotExist(err) {
   134  			return false, nil
   135  		}
   136  		return false, errors.Wrap(err, fmt.Sprintf("failed to load %s file", clusterImageSetFilename))
   137  	}
   138  
   139  	a.File = clusterImageSetFile
   140  
   141  	clusterImageSet := &hivev1.ClusterImageSet{}
   142  	if err := yaml.UnmarshalStrict(clusterImageSetFile.Data, clusterImageSet); err != nil {
   143  		err = errors.Wrapf(err, "failed to unmarshal %s", clusterImageSetFilename)
   144  		return false, err
   145  	}
   146  	a.Config = clusterImageSet
   147  
   148  	if err = a.finish(ctx, agentWorkflow.Workflow); err != nil {
   149  		return false, err
   150  	}
   151  	return true, nil
   152  }
   153  
   154  func (a *ClusterImageSet) finish(ctx context.Context, workflowType workflow.AgentWorkflowType) error {
   155  	if a.Config == nil {
   156  		return errors.New("missing configuration or manifest file")
   157  	}
   158  
   159  	if err := a.validateClusterImageSet(ctx, workflowType).ToAggregate(); err != nil {
   160  		return errors.Wrapf(err, "invalid ClusterImageSet configuration")
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  func (a *ClusterImageSet) validateClusterImageSet(ctx context.Context, workflowType workflow.AgentWorkflowType) field.ErrorList {
   167  	allErrs := field.ErrorList{}
   168  
   169  	if err := a.validateReleaseVersion(ctx, workflowType); err != nil {
   170  		allErrs = append(allErrs, err...)
   171  	}
   172  
   173  	return allErrs
   174  }
   175  
   176  func (a *ClusterImageSet) validateReleaseVersion(ctx context.Context, workflowType workflow.AgentWorkflowType) field.ErrorList {
   177  	// skip release version check in case of add nodes workflow
   178  	if workflowType == workflow.AgentWorkflowTypeAddNodes {
   179  		return nil
   180  	}
   181  
   182  	var allErrs field.ErrorList
   183  
   184  	fieldPath := field.NewPath("Spec", "ReleaseImage")
   185  
   186  	releaseImage := &releaseimage.Image{}
   187  	if err := releaseImage.Generate(ctx, asset.Parents{}); err != nil {
   188  		return append(allErrs, field.InternalError(fieldPath, fmt.Errorf("failed to generate the release image asset: %w", err)))
   189  	}
   190  
   191  	if a.Config.Spec.ReleaseImage != releaseImage.PullSpec {
   192  		allErrs = append(allErrs, field.Forbidden(fieldPath, fmt.Sprintf("value must be equal to %s", releaseImage.PullSpec)))
   193  	}
   194  
   195  	return allErrs
   196  }