Skip to content

Commit

Permalink
*: provide a default OS image for libvirt
Browse files Browse the repository at this point in the history
Instead of requiring the user to download RHCOS and unpack it, this
allows them to be lazy and just let the installer download the image
from a default location. Note that there is no caching, so it is
recommended that users still manually download and unpack RHCOS.
  • Loading branch information
crawford committed Sep 18, 2018
1 parent 00cb2f1 commit 9ee45fb
Show file tree
Hide file tree
Showing 14 changed files with 46 additions and 143 deletions.
18 changes: 10 additions & 8 deletions Documentation/dev/libvirt-howto.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ git clone https://github.com/openshift/installer.git
cd installer
```

#### 1.3 Download and prepare the operating system image
#### 1.3 (Optional) Download and prepare the operating system image

*By default, the installer will download the latest RHCOS image every time it is invoked. This may be problematic for users who create a large number of clusters or who have limited network bandwidth. The installer allows a local image to be used instead.*

Download the latest RHCOS image (you will need access to the Red Hat internal build systems):

Expand Down Expand Up @@ -85,14 +87,14 @@ iptables -I INPUT -p tcp -s 192.168.124.0/24 -d 192.168.124.1 --dport 16509 \

#### 1.7 Prepare the configuration file
1. `cp examples/libvirt.yaml ./`
1. Edit the configuration file:
2. Edit the configuration file:
1. Set an email and password in the `admin` section
1. Set a `baseDomain` (to `tt.testing`)
1. Set the `sshKey` in the `admin` section to the **contents** of an ssh key (e.g. `ssh-rsa AAAA...`)
1. Set the `imagePath` to the **absolute** path of the operating system image you downloaded
1. Set the `name` (e.g. test1)
1. Look at the `podCIDR` and `serviceCIDR` fields in the `networking` section. Make sure they don't conflict with anything important.
1. Set the `pullSecret` to your JSON pull secret.
2. Set a `baseDomain` (to `tt.testing`)
3. Set the `sshKey` in the `admin` section to the **contents** of an ssh key (e.g. `ssh-rsa AAAA...`)
4. Set the `name` (e.g. test1)
5. Look at the `podCIDR` and `serviceCIDR` fields in the `networking` section. Make sure they don't conflict with anything important.
6. Set the `pullSecret` to your JSON pull secret.
7. (Optional) Change the `image` to the file URL of the operating system image you downloaded (e.g. `file:///home/user/Downloads/rhcos.qcow`). This will allow the installer to re-use that image instead of having to download it every time.

#### 1.8 Set up NetworkManager DNS overlay
This step is optional, but useful for being able to resolve cluster-internal hostnames from your host.
Expand Down
2 changes: 1 addition & 1 deletion examples/libvirt.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ libvirt:
name: tectonic
ifName: tt0
ipRange: 192.168.124.0/24
imagePath: /path/to/image
image: http://aos-ostree.rhev-ci-vms.eng.rdu2.redhat.com/rhcos/images/cloud/latest/rhcos-qemu.qcow2.gz

ca:
# (optional) The content of the PEM-encoded CA certificate, used to generate Tectonic Console's server certificate.
Expand Down
6 changes: 4 additions & 2 deletions installer/pkg/config-generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,12 @@ func (c *ConfigGenerator) installConfig() (*types.InstallConfig, error) {
},
}
masterPlatform.Libvirt = &types.LibvirtMachinePoolPlatform{
QCOWImagePath: c.Libvirt.QCOWImagePath,
ImagePool: "default",
ImageVolume: "coreos_base",
}
workerPlatform.Libvirt = &types.LibvirtMachinePoolPlatform{
QCOWImagePath: c.Libvirt.QCOWImagePath,
ImagePool: "default",
ImageVolume: "coreos_base",
}
default:
return nil, fmt.Errorf("installconfig: invalid platform %s", c.Platform)
Expand Down
12 changes: 6 additions & 6 deletions installer/pkg/config/libvirt/libvirt.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ const (

// Libvirt encompasses configuration specific to libvirt.
type Libvirt struct {
URI string `json:"tectonic_libvirt_uri,omitempty" yaml:"uri"`
QCOWImagePath string `json:"tectonic_coreos_qcow_path,omitempty" yaml:"imagePath"`
Network `json:",inline" yaml:"network"`
MasterIPs []string `json:"tectonic_libvirt_master_ips,omitempty" yaml:"masterIPs"`
WorkerIPs []string `json:"tectonic_libvirt_worker_ips,omitempty" yaml:"workerIPs"`
BootstrapIP string `json:"tectonic_libvirt_bootstrap_ip,omitempty" yaml:"bootstrapIP"`
URI string `json:"tectonic_libvirt_uri,omitempty" yaml:"uri"`
Image string `json:"tectonic_os_image,omitempty" yaml:"image"`
Network `json:",inline" yaml:"network"`
MasterIPs []string `json:"tectonic_libvirt_master_ips,omitempty" yaml:"masterIPs"`
WorkerIPs []string `json:"tectonic_libvirt_worker_ips,omitempty" yaml:"workerIPs"`
BootstrapIP string `json:"tectonic_libvirt_bootstrap_ip,omitempty" yaml:"bootstrapIP"`
}

// Network describes a libvirt network configuration.
Expand Down
7 changes: 0 additions & 7 deletions installer/pkg/config/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ const (
maxS3BucketNameLength = 63
)

var (
qcowMagic = []byte{'Q', 'F', 'I', 0xfb}
)

// ErrUnmatchedNodePool is returned when a nodePool was specified but not found in the nodePools list.
type ErrUnmatchedNodePool struct {
name string
Expand Down Expand Up @@ -160,9 +156,6 @@ func (c *Cluster) validateLibvirt() []error {
if err := validate.PrefixError("libvirt uri", validate.NonEmpty(c.Libvirt.URI)); err != nil {
errs = append(errs, err)
}
if err := validate.PrefixError("libvirt imagePath is not a valid QCOW image", validate.FileHeader(c.Libvirt.QCOWImagePath, qcowMagic)); err != nil {
errs = append(errs, err)
}
if err := validate.PrefixError("libvirt network name", validate.NonEmpty(c.Libvirt.Network.Name)); err != nil {
errs = append(errs, err)
}
Expand Down
55 changes: 12 additions & 43 deletions installer/pkg/config/validate_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package config

import (
"io/ioutil"
"os"
"testing"

Expand Down Expand Up @@ -444,21 +443,6 @@ func TestValidateIgnitionFiles(t *testing.T) {
}

func TestValidateLibvirt(t *testing.T) {
fValid, err := ioutil.TempFile("", "qcow")
if err != nil {
t.Fatalf("failed to create temporary file: %v", err)
}
if _, err := fValid.Write(qcowMagic); err != nil {
t.Fatalf("failed to write to temporary file: %v", err)
}
fValid.Close()
defer os.Remove(fValid.Name())
fInvalid, err := ioutil.TempFile("", "qcow")
if err != nil {
t.Fatalf("failed to create temporary file: %v", err)
}
fInvalid.Close()
defer os.Remove(fInvalid.Name())
cases := []struct {
cluster Cluster
err bool
Expand All @@ -474,24 +458,9 @@ func TestValidateLibvirt(t *testing.T) {
{
cluster: Cluster{
Libvirt: libvirt.Libvirt{
Network: libvirt.Network{},
QCOWImagePath: "",
URI: "",
},
Networking: defaultCluster.Networking,
},
err: true,
},
{
cluster: Cluster{
Libvirt: libvirt.Libvirt{
Network: libvirt.Network{
Name: "tectonic",
IfName: libvirt.DefaultIfName,
IPRange: "10.0.1.0/24",
},
QCOWImagePath: fInvalid.Name(),
URI: "baz",
Network: libvirt.Network{},
Image: "",
URI: "",
},
Networking: defaultCluster.Networking,
},
Expand All @@ -505,8 +474,8 @@ func TestValidateLibvirt(t *testing.T) {
IfName: libvirt.DefaultIfName,
IPRange: "10.0.1.0/24",
},
QCOWImagePath: fValid.Name(),
URI: "baz",
Image: "file:///foo",
URI: "baz",
},
Networking: defaultCluster.Networking,
},
Expand All @@ -520,8 +489,8 @@ func TestValidateLibvirt(t *testing.T) {
IfName: libvirt.DefaultIfName,
IPRange: "10.2.1.0/24",
},
QCOWImagePath: fValid.Name(),
URI: "baz",
Image: "file:///foo",
URI: "baz",
},
Networking: defaultCluster.Networking,
},
Expand All @@ -535,8 +504,8 @@ func TestValidateLibvirt(t *testing.T) {
IfName: libvirt.DefaultIfName,
IPRange: "x",
},
QCOWImagePath: "foo",
URI: "baz",
Image: "file:///foo",
URI: "baz",
},
Networking: defaultCluster.Networking,
},
Expand All @@ -550,12 +519,12 @@ func TestValidateLibvirt(t *testing.T) {
IfName: libvirt.DefaultIfName,
IPRange: "192.168.0.1/24",
},
QCOWImagePath: "foo",
URI: "baz",
Image: "file:///foo",
URI: "baz",
},
Networking: defaultCluster.Networking,
},
err: true,
err: false,
},
}

Expand Down
17 changes: 0 additions & 17 deletions installer/pkg/validate/validate.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package validate

import (
"bytes"
"crypto/x509"
"encoding/json"
"encoding/pem"
Expand Down Expand Up @@ -469,19 +468,3 @@ func lastIP(cidr *net.IPNet) net.IP {
}
return last
}

// FileHeader validates that the file at the specified path begins with the given string.
func FileHeader(path string, header []byte) error {
f, err := os.Open(path)
if err != nil {
return err
}
buf := make([]byte, len(header))
if _, err := f.Read(buf); err != nil {
return err
}
if !bytes.Equal(buf, header) {
return fmt.Errorf("file %q does not begin with %q", path, string(header))
}
return nil
}
49 changes: 0 additions & 49 deletions installer/pkg/validate/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -610,52 +610,3 @@ func TestFileExists(t *testing.T) {
}
}
}

func TestFileHeader(t *testing.T) {
cases := []struct {
actual []byte
expected []byte
err bool
}{
{
actual: []byte{},
expected: []byte("foo"),
err: true,
},
{
actual: []byte("foo"),
expected: []byte("bar"),
err: true,
},
{
actual: []byte("fooooo"),
expected: []byte("foo"),
err: false,
},
{
actual: []byte("fooooo"),
expected: []byte{},
err: false,
},
}

for i, c := range cases {
f, err := ioutil.TempFile("", "fileheader")
if err != nil {
t.Errorf("test case %d: failed to create temporary file: %v", i, err)
continue
}
if _, err := f.Write(c.actual); err != nil {
t.Errorf("test case %d: failed to write to temporary file: %v", i, err)
}
f.Close()
if err := validate.FileHeader(f.Name(), c.expected); (err != nil) != c.err {
no := "no"
if c.err {
no = "an"
}
t.Errorf("test case %d: expected %s error, got %v", i, no, err)
}
os.Remove(f.Name())
}
}
3 changes: 1 addition & 2 deletions modules/libvirt/volume/main.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Create a QCOW volume from the downloaded path
resource "libvirt_volume" "coreos_base" {
name = "coreos_base"
source = "file://${var.coreos_qcow_path}"
source = "file://${var.image}"
}
4 changes: 2 additions & 2 deletions modules/libvirt/volume/variables.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
variable "coreos_qcow_path" {
description = "The path on disk to the coreos disk image"
variable "image" {
description = "The URL of the OS disk image"
type = "string"
}
8 changes: 6 additions & 2 deletions pkg/types/machinepools.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ type EC2RootVolume struct {
// LibvirtMachinePoolPlatform stores the configuration for a machine pool
// installed on libvirt.
type LibvirtMachinePoolPlatform struct {
// QCOWImagePath
QCOWImagePath string `json:"qcowImagePath"`
// ImagePool is the name of the libvirt storage pool to which the storage
// volume containing the OS image belongs.
ImagePool string `json:"imagePool"`
// ImageVolume is the name of the libvirt storage volume containing the OS
// image.
ImageVolume string `json:"imageVolume"`
}
2 changes: 1 addition & 1 deletion steps/infra/libvirt/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ provider "libvirt" {
module "libvirt_base_volume" {
source = "../../../modules/libvirt/volume"

coreos_qcow_path = "${var.tectonic_coreos_qcow_path}"
image = "${var.tectonic_os_image}"
}

resource "libvirt_volume" "master" {
Expand Down
4 changes: 2 additions & 2 deletions steps/variables-libvirt.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ variable "tectonic_libvirt_ip_range" {
description = "IP range for the libvirt machines"
}

variable "tectonic_coreos_qcow_path" {
variable "tectonic_os_image" {
type = "string"
description = "path to a Red Hat CoreOS qcow image"
description = "The URL of the OS disk image"
}

variable "tectonic_libvirt_bootstrap_ip" {
Expand Down
2 changes: 1 addition & 1 deletion tests/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ python <<-EOF >"${CLUSTER_NAME}.yaml"
config['aws']['master']['iamRoleName'] = 'tf-tectonic-master-node'
config['aws']['worker']['iamRoleName'] = 'tf-tectonic-worker-node'
elif '${BACKEND}' == 'libvirt':
config['libvirt']['imagePath'] = '${IMAGE_PATH}'
config['libvirt']['image'] = 'file://${IMAGE_PATH}'
yaml.safe_dump(config, sys.stdout)
EOF

Expand Down

0 comments on commit 9ee45fb

Please sign in to comment.