1
0
mirror of https://github.com/openshift/installer.git synced 2026-02-05 06:46:36 +01:00

AGENT-1357: Remove AgentWorkflowTypeInstallInteractiveDisconnected workflow

Remove the interactive disconnected workflow type. The appliance
  embeds registries.conf and CA certificates directly in the system ignition
  for bootstrap, eliminating the need for a separate workflow type.

  Key changes:

  Command-line interface:
  - Remove --interactive flag from unconfigured-ignition command
  - Remove ContextWrapper and workflow context management

  Workflow handling:
  - Simplify to always use AgentWorkflowTypeInstall for unconfigured ignition
  - Remove AgentWorkflowTypeInstallInteractiveDisconnected constant
  - Remove workflow type switching logic in UnconfiguredIgnition
  - Remove workflow dependency from UnconfiguredIgnition asset

  Mirror configuration:
  - Remove RegistriesConf and CaBundle dependencies from UnconfiguredIgnition
  - Remove addMirrorData() call (appliance provides this)
  - Remove early returns for interactive workflow in mirror assets

  Testing:
  - Remove interactive-disconnected-workflow test case
  - Remove with-mirror-configs test case from unconfigured ignition tests
  - Update default dependencies in test helpers

  Rationale:
  The OVE appliance provides a more robust solution for disconnected
  installations by embedding all necessary configuration (registries,
  certificates, UI) directly in the appliance image. This approach:
  - Eliminates workflow type complexity
  - Decouples installer and appliance repositories
  - Simplifies the codebase by removing conditional logic
  - Aligns with the architecture where MCO manages post-bootstrap config

  After first node reboot, the Machine Config Operator manages registry
  configuration and trust bundles via IDMS/IDMT resources.

  Commit message text generated by: Claude AI <noreply@anthropic.com>
This commit is contained in:
Pawan Pinjarkar
2025-10-15 20:45:00 -04:00
parent 8f88b34924
commit da87462421
12 changed files with 17 additions and 263 deletions

View File

@@ -2,7 +2,6 @@ package main
import (
"context"
"log"
"github.com/spf13/cobra"
@@ -13,7 +12,6 @@ import (
"github.com/openshift/installer/pkg/asset/agent/image"
"github.com/openshift/installer/pkg/asset/agent/manifests"
"github.com/openshift/installer/pkg/asset/agent/mirror"
"github.com/openshift/installer/pkg/asset/agent/workflow"
"github.com/openshift/installer/pkg/asset/kubeconfig"
"github.com/openshift/installer/pkg/asset/password"
"github.com/openshift/installer/pkg/asset/tls"
@@ -109,7 +107,7 @@ var (
command: &cobra.Command{
Use: "unconfigured-ignition",
Short: "Generates an agent ignition that excludes cluster configuration",
Args: cobra.MaximumNArgs(1),
Args: cobra.ExactArgs(0),
Hidden: true,
},
assets: []asset.WritableAsset{
@@ -146,31 +144,15 @@ func newAgentCreateCmd(ctx context.Context) *cobra.Command {
},
}
agentCtx := agent.NewContextWrapper(ctx)
for _, t := range agentTargets {
t.command.Args = cobra.ExactArgs(0)
t.command.Run = runTargetCmd(agentCtx, t.assets...)
t.command.Run = runTargetCmd(ctx, t.assets...)
cmd.AddCommand(t.command)
}
setUnconfiguredIgnitionFlag(agentCtx)
return cmd
}
func setUnconfiguredIgnitionFlag(ctx *agent.ContextWrapper) {
agentUnconfiguredIgnitionTarget.command.PersistentFlags().Bool("interactive", false, "Enable the interactive disconnected workflow support")
agentUnconfiguredIgnitionTarget.command.PreRun = func(cmd *cobra.Command, args []string) {
isInteractive, err := cmd.Flags().GetBool("interactive")
if err != nil {
log.Fatal(err)
}
if isInteractive {
ctx.AddValue(workflow.WorkflowTypeKey, string(workflow.AgentWorkflowTypeInstallInteractiveDisconnected))
}
}
}
func newAgentGraphCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "graph",

View File

@@ -1,43 +0,0 @@
package agent
import (
"context"
"time"
)
// ContextWrapper is a context decorator.
type ContextWrapper struct {
ctx context.Context //nolint:containedctx
}
// NewContextWrapper creates a new ContextWrapper instance.
func NewContextWrapper(ctx context.Context) *ContextWrapper {
return &ContextWrapper{
ctx: ctx,
}
}
// Deadline returns the context Deadline.
func (a *ContextWrapper) Deadline() (deadline time.Time, ok bool) {
return a.ctx.Deadline()
}
// Done returns the context Done channel.
func (a *ContextWrapper) Done() <-chan struct{} {
return a.ctx.Done()
}
// Err returns the context current error.
func (a *ContextWrapper) Err() error {
return a.ctx.Err()
}
// Value returns the context Value.
func (a *ContextWrapper) Value(key any) any {
return a.ctx.Value(key)
}
// AddValue allows to add a new value in the context.
func (a *ContextWrapper) AddValue(key, value interface{}) {
a.ctx = context.WithValue(a.ctx, key, value)
}

View File

@@ -1,100 +0,0 @@
# Verify that the --interactive flag generates the expected resources required for the interactive workflow.
expandFile cluster-manifests/cluster-image-set.yaml
exec openshift-install agent create unconfigured-ignition --interactive --dir $WORK
exists $WORK/unconfigured-agent.ign
! exists $WORK/auth/kubeconfig
! exists $WORK/auth/kubeadmin-password
unconfiguredIgnContains /etc/assisted/manifests/pull-secret.yaml
! unconfiguredIgnContains /etc/assisted/manifests/agent-cluster-install.yaml
! unconfiguredIgnContains /etc/assisted/manifests/cluster-deployment.yaml
unconfiguredIgnCmp /etc/assisted/manifests/cluster-image-set.yaml expected/cluster-image-set.yaml
unconfiguredIgnCmp /etc/assisted/manifests/infraenv.yaml expected/infraenv.yaml
unconfiguredIgnCmp /etc/assisted/rendezvous-host.env expected/rendezvous-host.env
unconfiguredIgnContains /etc/assisted/no-config-image
unconfiguredIgnContains /etc/assisted/interactive-ui
-- cluster-manifests/infraenv.yaml --
apiVersion: agent-install.openshift.io/v1beta1
kind: InfraEnv
metadata:
name: ostest
namespace: cluster0
spec:
cpuArchitecture: x86_64
pullSecretRef:
name: ostest-pull-secret
sshAuthorizedKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDK6UTEydcEKzuNdPaofn8Z2DwgHqdcionLZBiPf/zIRNco++etLsat7Avv7yt04DINQd5zjxIFgG8jblaUB5E5C9ClUcMwb52GO0ay2Y9v1uBv1a4WhI3peKktAzYNk0EBMQlJtXPjRMrC9ylBPh+DsBHMu+KmDnfk7PIwyN4efC8k5kSRuPWoNdme1rz2+umU8FSmaWTHIajrbspf4GQbsntA5kuKEtDbfoNCU97o2KrRnUbeg3a8hwSjfh3u6MhlnGcg5K2Ij+zivEsWGCLKYUtE1ErqwfIzwWmJ6jnV66XCQGHf4Q1iIxqF7s2a1q24cgG2Z/iDXfqXrCIfy4P7b/Ztak3bdT9jfAdVZtdO5/r7I+O5hYhF86ayFlDWzZWP/ByiSb+q4CQbfVgK3BMmiAv2MqLHdhesmD/SmIcoOWUF6rFmRKZVFFpKpt5ATNTgUJ3JRowoXrrDruVXClUGRiCS6Zabd1rZ3VmTchaPJwtzQMdfIWISXj+Ig+C4UK0=
-- cluster-manifests/pull-secret.yaml --
apiVersion: v1
kind: Secret
metadata:
name: ostest-pull-secret
namespace: cluster0
stringData:
.dockerconfigjson: |-
{
"auths": {
"quay.io": {
"auth": "c3VwZXItc2VjcmV0Cg=="
}
}
}
-- cluster-manifests/cluster-image-set.yaml --
apiVersion: hive.openshift.io/v1
kind: ClusterImageSet
metadata:
name: cluster0-image-set
namespace: cluster0
spec:
releaseImage: $RELEASE_IMAGE
-- expected/cluster-image-set.yaml --
apiVersion: hive.openshift.io/v1
kind: ClusterImageSet
metadata:
name: cluster0-image-set
namespace: cluster0
spec:
releaseImage: $RELEASE_IMAGE
-- expected/infraenv.yaml --
apiVersion: agent-install.openshift.io/v1beta1
kind: InfraEnv
metadata:
name: ostest
namespace: cluster0
spec:
cpuArchitecture: x86_64
ipxeScriptType: ""
nmStateConfigLabelSelector: {}
pullSecretRef:
name: ostest-pull-secret
sshAuthorizedKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDK6UTEydcEKzuNdPaofn8Z2DwgHqdcionLZBiPf/zIRNco++etLsat7Avv7yt04DINQd5zjxIFgG8jblaUB5E5C9ClUcMwb52GO0ay2Y9v1uBv1a4WhI3peKktAzYNk0EBMQlJtXPjRMrC9ylBPh+DsBHMu+KmDnfk7PIwyN4efC8k5kSRuPWoNdme1rz2+umU8FSmaWTHIajrbspf4GQbsntA5kuKEtDbfoNCU97o2KrRnUbeg3a8hwSjfh3u6MhlnGcg5K2Ij+zivEsWGCLKYUtE1ErqwfIzwWmJ6jnV66XCQGHf4Q1iIxqF7s2a1q24cgG2Z/iDXfqXrCIfy4P7b/Ztak3bdT9jfAdVZtdO5/r7I+O5hYhF86ayFlDWzZWP/ByiSb+q4CQbfVgK3BMmiAv2MqLHdhesmD/SmIcoOWUF6rFmRKZVFFpKpt5ATNTgUJ3JRowoXrrDruVXClUGRiCS6Zabd1rZ3VmTchaPJwtzQMdfIWISXj+Ig+C4UK0=
status:
agentLabelSelector: {}
bootArtifacts:
discoveryIgnitionURL: ""
initrd: ""
ipxeScript: ""
kernel: ""
rootfs: ""
debugInfo:
eventsURL: ""
-- expected/rendezvous-host.env --
#{{ $$isIPv6 := false }}{{ $$host := .RendezvousIP }}{{ range ( len .RendezvousIP ) }}{{if eq ( index ( slice $$host . ) 0 ) ':'}}{{ $$isIPv6 = true }}{{ end }}{{ end }}
NODE_ZERO_IP={{.RendezvousIP}}
SERVICE_BASE_URL=http://{{ if $$isIPv6 }}{{ printf "[%s]" .RendezvousIP }}{{ else }}{{ .RendezvousIP }}{{ end }}:8090/
IMAGE_SERVICE_BASE_URL=http://{{ if $$isIPv6 }}{{ printf "[%s]" .RendezvousIP }}{{ else }}{{ .RendezvousIP }}{{ end }}:8888/
PULL_SECRET_TOKEN=
USER_AUTH_TOKEN=
WORKFLOW_TYPE=install-interactive-disconnected
AIUI_APP_API_URL=http://{{ if $$isIPv6 }}{{ printf "[%s]" .RendezvousIP }}{{ else }}{{ .RendezvousIP }}{{ end }}:8090/
AIUI_URL=http://{{ if $$isIPv6 }}{{ printf "[%s]" .RendezvousIP }}{{ else }}{{ .RendezvousIP }}{{ end }}:3001/

View File

@@ -53,13 +53,13 @@ so no connectivity checks to the release image is needed.
----
## Interactive workflow (unconfigured ignition --interactive)
## Install workflow with OpenShift Virtualizaton Engine (OVE)
![Interactive workflow](./agent_installer_services-interactive.svg)
OpenShift Virtualization Engine (OVE) is a standalone OpenShift deployment for virtualization workloads, distributed as a pre-built appliance image. This allows the user to install a cluster by using the assisted UI running on the rendezvous node. The agent-tui is also used interactively to configure which node will be the rendezvous host, and to configure accordingly the other nodes.
The interactive workflow allows the user to install a cluster by using the assisted UI running on the rendezvous node. In this workflow
the agent-tui is also used interactively to configure which node will be the rendezvous host, and to configure accordingly the other nodes.
The installer detects the `/etc/assisted/interactive-ui` sentinel file embedded in the appliance's system ignition to enable interactive installation, which provides:
* agent-extract-tui - extracts agent-tui and nmstate libraries from the agent-installer-utils image during boot
* agent-ui - runs the assisted UI on the rendezvous node
The appliance embeds this sentinel file during image creation. After first reboot, MCO manages registry and certificate configuration.

View File

@@ -90,10 +90,6 @@ func (a *AgentHosts) Generate(_ context.Context, dependencies asset.Parents) err
}
}
case workflow.AgentWorkflowTypeInstallInteractiveDisconnected:
// Not required
return nil
case workflow.AgentWorkflowTypeAddNodes:
a.Hosts = append(a.Hosts, addNodesConfig.Config.Hosts...)

View File

@@ -18,7 +18,6 @@ import (
"github.com/openshift/installer/pkg/asset/agent/agentconfig"
"github.com/openshift/installer/pkg/asset/agent/common"
"github.com/openshift/installer/pkg/asset/agent/manifests"
"github.com/openshift/installer/pkg/asset/agent/mirror"
"github.com/openshift/installer/pkg/asset/agent/workflow"
"github.com/openshift/installer/pkg/asset/ignition"
"github.com/openshift/installer/pkg/asset/ignition/bootstrap"
@@ -84,13 +83,13 @@ func (a *UnconfiguredIgnition) Dependencies() []asset.Asset {
&manifests.AgentPullSecret{},
&manifests.ClusterImageSet{},
&manifests.NMStateConfig{},
&mirror.RegistriesConf{},
&mirror.CaBundle{},
&common.InfraEnvID{},
}
}
// Generate generates the agent installer unconfigured ignition.
// The appliance embeds both registries.conf and CA certificates in the image's
// system ignition for the bootstrap phase. After first reboot, MCO manages these.
func (a *UnconfiguredIgnition) Generate(_ context.Context, dependencies asset.Parents) error {
agentWorkflow := &workflow.AgentWorkflow{}
infraEnvAsset := &manifests.InfraEnvFile{}
@@ -101,6 +100,10 @@ func (a *UnconfiguredIgnition) Generate(_ context.Context, dependencies asset.Pa
agentConfig := &agentconfig.AgentConfig{}
dependencies.Get(agentWorkflow, infraEnvAsset, clusterImageSetAsset, pullSecretAsset, nmStateConfigs, infraEnvIDAsset, agentConfig)
if agentWorkflow.Workflow != workflow.AgentWorkflowTypeInstall {
return fmt.Errorf("AgentWorkflowType value not supported: %s", agentWorkflow.Workflow)
}
infraEnv := infraEnvAsset.Config
clusterImageSet := clusterImageSetAsset.Config
@@ -133,10 +136,6 @@ func (a *UnconfiguredIgnition) Generate(_ context.Context, dependencies asset.Pa
return err
}
registriesConfig := &mirror.RegistriesConf{}
registryCABundle := &mirror.CaBundle{}
dependencies.Get(registriesConfig, registryCABundle)
infraEnvID := infraEnvIDAsset.ID
logrus.Debug("Generated random infra-env id ", infraEnvID)
@@ -155,9 +154,9 @@ func (a *UnconfiguredIgnition) Generate(_ context.Context, dependencies asset.Pa
PullSecret: pullSecretAsset.GetPullSecretData(),
ReleaseImages: releaseImageList,
ReleaseImage: clusterImageSet.Spec.ReleaseImage,
ReleaseImageMirror: mirror.GetMirrorFromRelease(clusterImageSet.Spec.ReleaseImage, registriesConfig),
HaveMirrorConfig: len(registriesConfig.MirrorConfig) > 0,
PublicContainerRegistries: getPublicContainerRegistries(registriesConfig),
ReleaseImageMirror: "",
HaveMirrorConfig: true,
PublicContainerRegistries: "",
InfraEnvID: infraEnvID,
OSImage: osImage,
Proxy: infraEnv.Spec.Proxy,
@@ -180,27 +179,7 @@ func (a *UnconfiguredIgnition) Generate(_ context.Context, dependencies asset.Pa
config.Storage.Files = append(config.Storage.Files, rendezvousHostFile)
}
switch agentWorkflow.Workflow {
case workflow.AgentWorkflowTypeInstall:
agentTemplateData.ConfigImageFiles = strings.Join(GetConfigImageFiles(), ",")
case workflow.AgentWorkflowTypeInstallInteractiveDisconnected:
// Add the rendezvous host file. Agent TUI will interact with that file in case
// the rendezvous IP wasn't previously configured, by managing it as a template file.
if rendezvousIP == "" {
rendezvousHostFile := ignition.FileFromString(rendezvousHostEnvPath, "root", 0644, rendezvousHostTemplateData)
config.Storage.Files = append(config.Storage.Files, rendezvousHostFile)
}
// Explicitly disable the load-config-iso service, not required in the current flow
// (even though disabled by default, the udev rule may require it).
config.Storage.Files = append(config.Storage.Files, ignition.FileFromString("/etc/assisted/no-config-image", "root", 0644, ""))
// Enable the UI service.
interactiveUIFile := ignition.FileFromString("/etc/assisted/interactive-ui", "root", 0644, "")
config.Storage.Files = append(config.Storage.Files, interactiveUIFile)
}
agentTemplateData.ConfigImageFiles = strings.Join(GetConfigImageFiles(), ",")
// Required by assisted-service.
a.ignAddFolders(&config, "/opt/agent/tls")
@@ -249,8 +228,6 @@ func (a *UnconfiguredIgnition) Generate(_ context.Context, dependencies asset.Pa
return err
}
addMirrorData(&config, registriesConfig, registryCABundle)
a.Config = &config
if err := a.generateFile(unconfiguredIgnitionFilename); err != nil {

View File

@@ -15,7 +15,6 @@ import (
"github.com/openshift/installer/pkg/asset/agent/agentconfig"
"github.com/openshift/installer/pkg/asset/agent/common"
"github.com/openshift/installer/pkg/asset/agent/manifests"
"github.com/openshift/installer/pkg/asset/agent/mirror"
"github.com/openshift/installer/pkg/asset/agent/workflow"
)
@@ -42,43 +41,6 @@ func TestUnconfiguredIgnition_Generate(t *testing.T) {
"agent-check-config-image.service": true,
"agent-extract-tui.service": true},
},
{
name: "interactive-disconnected-workflow-should-have-agent-extract-tui-service-enabled",
overrideDeps: []asset.Asset{
&workflow.AgentWorkflow{Workflow: workflow.AgentWorkflowTypeInstallInteractiveDisconnected},
},
serviceEnabledMap: map[string]bool{
"agent-extract-tui.service": true},
},
{
name: "with-mirror-configs",
overrideDeps: []asset.Asset{
&mirror.RegistriesConf{
File: &asset.File{
Filename: mirror.RegistriesConfFilename,
Data: []byte(""),
},
MirrorConfig: []mirror.RegistriesConfig{
{
Location: "some.registry.org/release",
Mirrors: []string{"some.mirror.org"},
},
},
},
&mirror.CaBundle{
File: &asset.File{
Filename: "my.crt",
Data: []byte("my-certificate"),
},
},
},
expectedFiles: generatedFilesUnconfiguredIgnition(registriesConfPath,
registryCABundlePath, "/usr/local/bin/pre-network-manager-config.sh", "/usr/local/bin/oci-eval-user-data.sh"),
serviceEnabledMap: map[string]bool{
"pre-network-manager-config.service": false,
"oci-eval-user-data.service": true,
"agent-check-config-image.service": true},
},
{
name: "with-nmstateconfigs",
overrideDeps: []asset.Asset{
@@ -132,8 +94,6 @@ func buildUnconfiguredIgnitionAssetDefaultDependencies(t *testing.T) []asset.Ass
&agentPullSecret,
&clusterImageSet,
&manifests.NMStateConfig{},
&mirror.RegistriesConf{},
&mirror.CaBundle{},
&common.InfraEnvID{},
&agentconfig.AgentConfig{},
}

View File

@@ -57,7 +57,7 @@ func (a *ClusterImageSet) Generate(ctx context.Context, dependencies asset.Paren
dependencies.Get(releaseImage, installConfig, agentWorkflow, clusterInfo)
switch agentWorkflow.Workflow {
case workflow.AgentWorkflowTypeInstall, workflow.AgentWorkflowTypeInstallInteractiveDisconnected:
case workflow.AgentWorkflowTypeInstall:
currentVersion, err := version.Version()
if err != nil {
return err

View File

@@ -58,10 +58,6 @@ func (i *CaBundle) Generate(_ context.Context, dependencies asset.Parents) error
}
additionalTrustBundle = installConfig.Config.AdditionalTrustBundle
case workflow.AgentWorkflowTypeInstallInteractiveDisconnected:
// Not required
return nil
case workflow.AgentWorkflowTypeAddNodes:
additionalTrustBundle = clusterInfo.UserCaBundle

View File

@@ -164,10 +164,6 @@ func (i *RegistriesConf) Generate(_ context.Context, dependencies asset.Parents)
deprecatedImageContentSources = clusterInfo.DeprecatedImageContentSources
image = clusterInfo.ReleaseImage
case workflow.AgentWorkflowTypeInstallInteractiveDisconnected:
// Not required
return nil
default:
return fmt.Errorf("AgentWorkflowType value not supported: %s", agentWorkflow.Workflow)
}

View File

@@ -33,11 +33,6 @@ func (a *AgentWorkflow) Generate(ctx context.Context, dependencies asset.Parents
// Set install workflow as a default
a.Workflow = AgentWorkflowTypeInstall
// Extract the workflow type from the context, if available.
if w, ok := ctx.Value(WorkflowTypeKey).(string); ok {
a.Workflow = AgentWorkflowType(w)
}
a.File = &asset.File{
Filename: agentWorkflowFilename,
Data: []byte(a.Workflow),

View File

@@ -13,11 +13,6 @@ const (
AgentWorkflowTypeInstall AgentWorkflowType = "install"
// AgentWorkflowTypeAddNodes identifies the add nodes workflow.
AgentWorkflowTypeAddNodes AgentWorkflowType = "addnodes"
// AgentWorkflowTypeInstallInteractiveDisconnected identifies a specific kind of
// disconnected install workflow. The installation details will be provided through
// a dedicated UI running on the rendezvous node, and in addition no external registry
// will be required for an air-gapped deployment.
AgentWorkflowTypeInstallInteractiveDisconnected AgentWorkflowType = "install-interactive-disconnected"
agentWorkflowFilename = ".agentworkflow"
)