Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pkg/asset/rhcos/bootstrap_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ func (i *BootstrapImage) Generate(p asset.Parents) error {
defer cancel()
switch config.Platform.Name() {
case baremetal.Name:
// Check for RHCOS image URL override
if boi := config.Platform.BareMetal.BootstrapOSImage; boi != "" {
osimage = boi
break
}

// Baremetal IPI launches a local VM for the bootstrap node
// Hence requires the QEMU image to use the libvirt backend
osimage, err = rhcos.QEMU(ctx)
Expand Down
6 changes: 6 additions & 0 deletions pkg/asset/rhcos/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ func osImage(config *types.InstallConfig) (string, error) {
case azure.Name:
osimage, err = rhcos.VHD(ctx)
case baremetal.Name:
// Check for RHCOS image URL override
if oi := config.Platform.BareMetal.ClusterOSImage; oi != "" {
osimage = oi
break
}

// Note that baremetal IPI currently uses the OpenStack image
// because this contains the necessary ironic config drive
// ignition support, which isn't enabled in the UPI BM images
Expand Down
12 changes: 12 additions & 0 deletions pkg/types/baremetal/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,16 @@ type Platform struct {

// DNSVIP is the VIP to use for internal DNS communication
DNSVIP string `json:"dnsVIP"`

// BootstrapOSImage is a URL to override the default OS image
// for the bootstrap node. The URL must contain a sha256 hash of the image
// e.g https://mirror.example.com/images/qemu.qcow2.gz?sha256=a07bd...
// +optional
BootstrapOSImage string `json:"bootstrapOSImage,omitempty"`

// ClusterOSImage is a URL to override the default OS image
// for cluster nodes. The URL must contain a sha256 hash of the image
// e.g https://mirror.example.com/images/metal.qcow2.gz?sha256=3b5a8...
// +optional
ClusterOSImage string `json:"clusterOSImage,omitempty"`
}
34 changes: 34 additions & 0 deletions pkg/types/baremetal/validation/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package validation
import (
"fmt"
"net"
"net/url"

"github.com/openshift/installer/pkg/types"
"github.com/openshift/installer/pkg/types/baremetal"
Expand Down Expand Up @@ -32,6 +33,29 @@ func validateIPNotinMachineCIDR(ip string, n *types.Networking) error {
return nil
}

func validateOSImageURI(uri string) error {
// Check for valid URI and sha256 checksum part of the URL
parsedURL, err := url.ParseRequestURI(uri)
if err != nil {
return fmt.Errorf("the URI provided: %s is invalid", uri)
}
if parsedURL.Scheme == "http" || parsedURL.Scheme == "https" {
var sha256Checksum string
if sha256Checksums, ok := parsedURL.Query()["sha256"]; ok {
sha256Checksum = sha256Checksums[0]
}
if sha256Checksum == "" {
return fmt.Errorf("the sha256 parameter in the %s URI is missing", uri)
}
if len(sha256Checksum) != 64 {
return fmt.Errorf("the sha256 parameter in the %s URI is invalid", uri)
}
} else {
return fmt.Errorf("the URI provided: %s must begin with http/https", uri)
}
return nil
}

// ValidatePlatform checks that the specified platform is valid.
func ValidatePlatform(p *baremetal.Platform, n *types.Networking, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
Expand Down Expand Up @@ -84,6 +108,16 @@ func ValidatePlatform(p *baremetal.Platform, n *types.Networking, fldPath *field
if err := validateIPNotinMachineCIDR(p.BootstrapProvisioningIP, n); err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("bootstrapHostIP"), p.BootstrapProvisioningIP, err.Error()))
}
if p.BootstrapOSImage != "" {
if err := validateOSImageURI(p.BootstrapOSImage); err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("bootstrapOSImage"), p.BootstrapOSImage, err.Error()))
}
}
if p.ClusterOSImage != "" {
if err := validateOSImageURI(p.ClusterOSImage); err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("clusterOSImage"), p.ClusterOSImage, err.Error()))
}
}

for _, validator := range dynamicValidators {
allErrs = append(allErrs, validator(p, fldPath)...)
Expand Down
107 changes: 107 additions & 0 deletions pkg/types/baremetal/validation/platform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,23 @@ func TestValidatePlatform(t *testing.T) {
},
network: network,
},
{
name: "valid_with_os_image_overrides",
platform: &baremetal.Platform{
APIVIP: "192.168.111.2",
DNSVIP: "192.168.111.3",
IngressVIP: "192.168.111.4",
Hosts: []*baremetal.Host{},
LibvirtURI: "qemu://system",
ClusterProvisioningIP: "172.22.0.3",
BootstrapProvisioningIP: "172.22.0.2",
ExternalBridge: "br0",
ProvisioningBridge: "br1",
BootstrapOSImage: "http://192.168.111.1/images/qemu.x86_64.qcow2.gz?sha256=3b5a882c2af3e19d515b961855d144f293cab30190c2bdedd661af31a1fc4e2f",
ClusterOSImage: "http://192.168.111.1/images/metal.x86_64.qcow2.gz?sha256=340dfa4d92450f2eee852ed1e2d02e3138cc68d824827ef9cf0a40a7ea2f93da",
Comment on lines +65 to +66
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these fields are optional, so i would expect validation to be valid even when these are empty.

so 2 different test-cases no provided, provided valid.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks for the suggestion.

},
network: network,
},
{
name: "invalid_apivip",
platform: &baremetal.Platform{
Expand Down Expand Up @@ -195,6 +212,96 @@ func TestValidatePlatform(t *testing.T) {
network: network,
expected: "Invalid value: \"192.168.111.5\": the IP must not be in 192.168.111.0/24 subnet",
},
{
name: "invalid_bootstraposimage",
platform: &baremetal.Platform{
APIVIP: "192.168.111.2",
DNSVIP: "192.168.111.3",
IngressVIP: "192.168.111.4",
Hosts: []*baremetal.Host{},
LibvirtURI: "qemu://system",
ClusterProvisioningIP: "172.22.0.3",
BootstrapProvisioningIP: "172.22.0.2",
ExternalBridge: "br0",
ProvisioningBridge: "br1",
BootstrapOSImage: "192.168.111.1/images/qemu.x86_64.qcow2.gz?sha256=3b5a882c2af3e19d515b961855d144f293cab30190c2bdedd661af31a1fc4e2f",
ClusterOSImage: "http://192.168.111.1/images/metal.x86_64.qcow2.gz?sha256=340dfa4d92450f2eee852ed1e2d02e3138cc68d824827ef9cf0a40a7ea2f93da",
},
network: network,
expected: "the URI provided.*is invalid",
},
{
name: "invalid_clusterosimage",
platform: &baremetal.Platform{
APIVIP: "192.168.111.2",
DNSVIP: "192.168.111.3",
IngressVIP: "192.168.111.4",
Hosts: []*baremetal.Host{},
LibvirtURI: "qemu://system",
ClusterProvisioningIP: "172.22.0.3",
BootstrapProvisioningIP: "172.22.0.2",
ExternalBridge: "br0",
ProvisioningBridge: "br1",
BootstrapOSImage: "http://192.168.111.1/images/qemu.x86_64.qcow2.gz?sha256=3b5a882c2af3e19d515b961855d144f293cab30190c2bdedd661af31a1fc4e2f",
ClusterOSImage: "http//192.168.111.1/images/metal.x86_64.qcow2.gz?sha256=340dfa4d92450f2eee852ed1e2d02e3138cc68d824827ef9cf0a40a7ea2f93da",
},
network: network,
expected: "the URI provided.*is invalid",
},
{
name: "invalid_bootstraposimage_checksum",
platform: &baremetal.Platform{
APIVIP: "192.168.111.2",
DNSVIP: "192.168.111.3",
IngressVIP: "192.168.111.4",
Hosts: []*baremetal.Host{},
LibvirtURI: "qemu://system",
ClusterProvisioningIP: "172.22.0.3",
BootstrapProvisioningIP: "172.22.0.2",
ExternalBridge: "br0",
ProvisioningBridge: "br1",
BootstrapOSImage: "http://192.168.111.1/images/qemu.x86_64.qcow2.gz?md5sum=3b5a882c2af3e19d515b961855d144f293cab30190c2bdedd661af31a1fc4e2f",
ClusterOSImage: "http://192.168.111.1/images/metal.x86_64.qcow2.gz?sha256=340dfa4d92450f2eee852ed1e2d02e3138cc68d824827ef9cf0a40a7ea2f93da",
},
network: network,
expected: "the sha256 parameter in the.*URI is missing",
},
{
name: "invalid_clusterosimage_checksum",
platform: &baremetal.Platform{
APIVIP: "192.168.111.2",
DNSVIP: "192.168.111.3",
IngressVIP: "192.168.111.4",
Hosts: []*baremetal.Host{},
LibvirtURI: "qemu://system",
ClusterProvisioningIP: "172.22.0.3",
BootstrapProvisioningIP: "172.22.0.2",
ExternalBridge: "br0",
ProvisioningBridge: "br1",
BootstrapOSImage: "http://192.168.111.1/images/qemu.x86_64.qcow2.gz?sha256=3b5a882c2af3e19d515b961855d144f293cab30190c2bdedd661af31a1fc4e2f",
ClusterOSImage: "http://192.168.111.1/images/metal.x86_64.qcow2.gz?sha256=3ee852ed1e2d02e3138cc68d824827ef9cf0a40a7ea2f93da",
},
network: network,
expected: "the sha256 parameter in the.*URI is invalid",
},
{
name: "invalid_bootstraposimage_uri_scheme",
platform: &baremetal.Platform{
APIVIP: "192.168.111.2",
DNSVIP: "192.168.111.3",
IngressVIP: "192.168.111.4",
Hosts: []*baremetal.Host{},
LibvirtURI: "qemu://system",
ClusterProvisioningIP: "172.22.0.3",
BootstrapProvisioningIP: "172.22.0.2",
ExternalBridge: "br0",
ProvisioningBridge: "br1",
BootstrapOSImage: "xttp://192.168.111.1/images/qemu.x86_64.qcow2.gz?sha256=3b5a882c2af3e19d515b961855d144f293cab30190c2bdedd661af31a1fc4e2f",
ClusterOSImage: "http://192.168.111.1/images/metal.x86_64.qcow2.gz?sha256=340dfa4d92450f2eee852ed1e2d02e3138cc68d824827ef9cf0a40a7ea2f93da",
},
network: network,
expected: "the URI provided.*must begin with http/https",
},
}

for _, tc := range cases {
Expand Down