github.com/openshift/installer@v1.4.17/pkg/asset/agent/agentconfig/agent_config.go (about) 1 package agentconfig 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path/filepath" 8 9 "github.com/pkg/errors" 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/types/agent" 15 "github.com/openshift/installer/pkg/types/agent/conversion" 16 "github.com/openshift/installer/pkg/validate" 17 ) 18 19 var ( 20 agentConfigFilename = "agent-config.yaml" 21 ) 22 23 // AgentConfig reads the agent-config.yaml file. 24 type AgentConfig struct { 25 File *asset.File 26 Config *agent.Config 27 Template string 28 } 29 30 var _ asset.WritableAsset = (*AgentConfig)(nil) 31 32 // Name returns a human friendly name for the asset. 33 func (*AgentConfig) Name() string { 34 return "Agent Config" 35 } 36 37 // Dependencies returns all of the dependencies directly needed to generate 38 // the asset. 39 func (*AgentConfig) Dependencies() []asset.Asset { 40 return []asset.Asset{} 41 } 42 43 // Generate generates the Agent Config manifest. 44 func (a *AgentConfig) Generate(_ context.Context, dependencies asset.Parents) error { 45 // TODO: We are temporarily generating a template of the agent-config.yaml 46 // Change this when its interactive survey is implemented. 47 agentConfigTemplate := `# 48 # Note: This is a sample AgentConfig file showing 49 # which fields are available to aid you in creating your 50 # own agent-config.yaml file. 51 # 52 apiVersion: v1beta1 53 kind: AgentConfig 54 metadata: 55 name: example-agent-config 56 namespace: cluster0 57 # All fields are optional 58 rendezvousIP: your-node0-ip 59 bootArtifactsBaseURL: http://user-specified-infra.com 60 additionalNTPSources: 61 - 0.rhel.pool.ntp.org 62 - 1.rhel.pool.ntp.org 63 hosts: 64 # If a host is listed, then at least one interface 65 # needs to be specified. 66 - hostname: change-to-hostname 67 role: master 68 # For more information about rootDeviceHints: 69 # https://docs.openshift.com/container-platform/4.10/installing/installing_bare_metal_ipi/ipi-install-installation-workflow.html#root-device-hints_ipi-install-installation-workflow 70 rootDeviceHints: 71 deviceName: /dev/sda 72 # interfaces are used to identify the host to apply this configuration to 73 interfaces: 74 - macAddress: 00:00:00:00:00:00 75 name: host-network-interface-name 76 # networkConfig contains the network configuration for the host in NMState format. 77 # See https://nmstate.io/examples.html for examples. 78 networkConfig: 79 interfaces: 80 - name: eth0 81 type: ethernet 82 state: up 83 mac-address: 00:00:00:00:00:00 84 ipv4: 85 enabled: true 86 address: 87 - ip: 192.168.122.2 88 prefix-length: 23 89 dhcp: false 90 ` 91 92 a.Template = agentConfigTemplate 93 94 // Set the File field correctly with the generated agent config YAML content 95 a.File = &asset.File{ 96 Filename: agentConfigFilename, 97 Data: []byte(a.Template), 98 } 99 100 // TODO: template is not validated 101 return nil 102 } 103 104 // PersistToFile writes the agent-config.yaml file to the assets folder. 105 func (a *AgentConfig) PersistToFile(directory string) error { 106 templatePath := filepath.Join(directory, agentConfigFilename) 107 templateByte := []byte(a.Template) 108 109 err := os.WriteFile(templatePath, templateByte, 0600) 110 if err != nil { 111 return err 112 } 113 114 return nil 115 } 116 117 // Files returns the files generated by the asset. 118 func (a *AgentConfig) Files() []*asset.File { 119 if a.File != nil { 120 return []*asset.File{a.File} 121 } 122 return []*asset.File{} 123 } 124 125 // Load returns agent config asset from the disk. 126 func (a *AgentConfig) Load(f asset.FileFetcher) (bool, error) { 127 file, err := f.FetchByName(agentConfigFilename) 128 if err != nil { 129 if os.IsNotExist(err) { 130 return false, nil 131 } 132 return false, errors.Wrap(err, fmt.Sprintf("failed to load %s file", agentConfigFilename)) 133 } 134 135 config := &agent.Config{} 136 if err := yaml.UnmarshalStrict(file.Data, config); err != nil { 137 return false, errors.Wrapf(err, "failed to unmarshal %s", agentConfigFilename) 138 } 139 140 // Upconvert any deprecated fields 141 if err := conversion.ConvertAgentConfig(config); err != nil { 142 return false, err 143 } 144 145 a.File, a.Config = file, config 146 147 if err = a.finish(); err != nil { 148 return false, err 149 } 150 151 return true, nil 152 } 153 154 func (a *AgentConfig) finish() error { 155 if err := a.validateAgent().ToAggregate(); err != nil { 156 return errors.Wrapf(err, "invalid Agent Config configuration") 157 } 158 159 return nil 160 } 161 162 func (a *AgentConfig) validateAgent() field.ErrorList { 163 var allErrs field.ErrorList 164 165 if err := a.validateRendezvousIP(); err != nil { 166 allErrs = append(allErrs, err...) 167 } 168 169 if err := a.validateAdditionalNTPSources(field.NewPath("AdditionalNTPSources"), a.Config.AdditionalNTPSources); err != nil { 170 allErrs = append(allErrs, err...) 171 } 172 173 if err := a.validateBootArtifactsBaseURL(); err != nil { 174 allErrs = append(allErrs, err...) 175 } 176 177 return allErrs 178 } 179 180 func (a *AgentConfig) validateRendezvousIP() field.ErrorList { 181 var allErrs field.ErrorList 182 183 rendezvousIPPath := field.NewPath("rendezvousIP") 184 185 // empty rendezvous ip is fine 186 if a.Config.RendezvousIP == "" { 187 return nil 188 } 189 190 if err := validate.IP(a.Config.RendezvousIP); err != nil { 191 allErrs = append(allErrs, field.Invalid(rendezvousIPPath, a.Config.RendezvousIP, err.Error())) 192 } 193 194 return allErrs 195 } 196 197 func (a *AgentConfig) validateAdditionalNTPSources(additionalNTPSourcesPath *field.Path, sources []string) field.ErrorList { 198 var allErrs field.ErrorList 199 200 for i, source := range sources { 201 domainNameErr := validate.DomainName(source, true) 202 if domainNameErr != nil { 203 ipErr := validate.IP(source) 204 if ipErr != nil { 205 allErrs = append(allErrs, field.Invalid(additionalNTPSourcesPath.Index(i), source, "NTP source is not a valid domain name nor a valid IP")) 206 } 207 } 208 } 209 210 return allErrs 211 } 212 213 func (a *AgentConfig) validateBootArtifactsBaseURL() field.ErrorList { 214 var allErrs field.ErrorList 215 216 bootArtifactsBaseURL := field.NewPath("bootArtifactsBaseURL") 217 218 // empty bootArtifactsBaseURL is fine 219 if a.Config.BootArtifactsBaseURL == "" { 220 return nil 221 } 222 223 if err := validate.URI(a.Config.BootArtifactsBaseURL); err != nil { 224 allErrs = append(allErrs, field.Invalid(bootArtifactsBaseURL, a.Config.BootArtifactsBaseURL, err.Error())) 225 } 226 227 return allErrs 228 } 229 230 func unmarshalJSON(b []byte) []byte { 231 output, _ := yaml.JSONToYAML(b) 232 return output 233 }