diff --git a/pkg/plan/bootstrap.go b/pkg/plan/bootstrap.go index d3264c1..1c9f6c1 100644 --- a/pkg/plan/bootstrap.go +++ b/pkg/plan/bootstrap.go @@ -139,17 +139,29 @@ func (p *plan) addInstructions(cfg *config.Config, dataDir string) error { } } - if err := p.addInstruction(resources.ToInstruction(cfg.RancherInstallerImage, cfg.SystemDefaultRegistry, k8sVersion, dataDir)); err != nil { - return err - } - - // currently instruction is needed for version above v2.8.x + // Patch local provisioning cluster status before installing bootstrap resources, + // so bundles can be created first and managed charts can be installed smoothly. if semver.Compare(cfg.RancherVersion, "v2.8.0") >= 0 { if err := p.addInstruction(rancher.PatchLocalProvisioningClusterStatus(cfg.RancherInstallerImage, cfg.SystemDefaultRegistry, k8sVersion)); err != nil { return err } } + // If clusterrepo check fails, it waits 5 minutes and retries. + // Install harvester-cluster-repo deployment before clusterrepo, + // so we can avoid the 5 minutes waiting time. + if err := p.addInstruction(resources.ToHarvesterClusterRepoInstruction(cfg.RancherInstallerImage, cfg.SystemDefaultRegistry, k8sVersion, dataDir)); err != nil { + return err + } + + if err := p.addInstruction(resources.ToWaitHarvesterClusterRepoInstruction(k8sVersion)); err != nil { + return err + } + + if err := p.addInstruction(resources.ToInstruction(cfg.RancherInstallerImage, cfg.SystemDefaultRegistry, k8sVersion, dataDir)); err != nil { + return err + } + if semver.Compare(cfg.RancherVersion, "v2.9.0") >= 0 { if err := p.addInstruction(rancher.ToRestartRancherWebhookInstruction(k8sVersion)); err != nil { return err @@ -224,6 +236,11 @@ func (p *plan) addFiles(cfg *config.Config, dataDir string) error { return err } + // harvester-cluster-repo manifests + if err := p.addFile(resources.ToHarvesterClusterRepoFile(resources.GetHarvesterClusterRepoManifests(dataDir))); err != nil { + return err + } + // bootstrap manifests if err := p.addFile(resources.ToBootstrapFile(cfg, resources.GetBootstrapManifests(dataDir))); err != nil { return err diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go index 9faf48d..20d435d 100644 --- a/pkg/resources/resources.go +++ b/pkg/resources/resources.go @@ -9,8 +9,10 @@ import ( v1 "github.com/rancher/rancher/pkg/apis/rke.cattle.io/v1" "github.com/rancher/system-agent/pkg/applyinator" + "github.com/rancher/wrangler/pkg/data/convert" "github.com/rancher/wrangler/pkg/randomtoken" "github.com/rancher/wrangler/pkg/yaml" + "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -153,6 +155,32 @@ func ToBootstrapFile(config *config.Config, path string) (*applyinator.File, err }, }), path) } + +func ToHarvesterClusterRepoFile(path string) (*applyinator.File, error) { + file := "/usr/share/rancher/rancherd/config.yaml.d/91-harvester-bootstrap-repo.yaml" + bytes, err := os.ReadFile(file) + if err != nil && !os.IsNotExist(err) { + return nil, err + } + + logrus.Infof("Loading config file [%s]", file) + values := map[string]interface{}{} + if err := yaml.Unmarshal(bytes, &values); err != nil { + return nil, err + } + + result := config.Config{} + convert.ToObj(values, &result) + + resources := []v1.GenericMap{} + for _, resource := range result.Resources { + if resource.Data["kind"] == "Deployment" || resource.Data["kind"] == "Service" { + resources = append(resources, resource) + } + } + return ToFile(resources, path) +} + func ToFile(resources []v1.GenericMap, path string) (*applyinator.File, error) { if len(resources) == 0 { return nil, nil @@ -195,3 +223,37 @@ func ToInstruction(imageOverride, systemDefaultRegistry, k8sVersion, dataDir str Env: kubectl.Env(k8sVersion), }, nil } + +func GetHarvesterClusterRepoManifests(dataDir string) string { + return fmt.Sprintf("%s/bootstrapmanifests/harvester-cluster-repo.yaml", dataDir) +} + +func ToHarvesterClusterRepoInstruction(imageOverride, systemDefaultRegistry, k8sVersion, dataDir string) (*applyinator.Instruction, error) { + bootstrap := GetHarvesterClusterRepoManifests(dataDir) + cmd, err := self.Self() + if err != nil { + return nil, fmt.Errorf("resolving location of %s: %w", os.Args[0], err) + } + return &applyinator.Instruction{ + Name: "harvester-cluster-repo", + SaveOutput: true, + Image: images.GetInstallerImage(imageOverride, systemDefaultRegistry, k8sVersion), + Args: []string{"retry", kubectl.Command(k8sVersion), "apply", "--validate=false", "-f", bootstrap}, + Command: cmd, + Env: kubectl.Env(k8sVersion), + }, nil +} + +func ToWaitHarvesterClusterRepoInstruction(k8sVersion string) (*applyinator.Instruction, error) { + cmd, err := self.Self() + if err != nil { + return nil, fmt.Errorf("resolving location of %s: %w", os.Args[0], err) + } + return &applyinator.Instruction{ + Name: "wait-harvester-cluster-repo", + SaveOutput: true, + Args: []string{"retry", kubectl.Command(k8sVersion), "-n", "cattle-system", "rollout", "status", "-w", "deploy/harvester-cluster-repo"}, + Env: kubectl.Env(k8sVersion), + Command: cmd, + }, nil +}