Skip to content

Commit

Permalink
Add new platform baremetal config to override rhcos images
Browse files Browse the repository at this point in the history
Use the new config to override rhcos image for bootstrap node
Add validation and unit tests for OS Image overload
  • Loading branch information
kirankt committed Dec 16, 2019
1 parent 2d85529 commit be61e07
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 0 deletions.
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",
},
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

0 comments on commit be61e07

Please sign in to comment.