Skip to content

Commit

Permalink
pkg/rhcos/release: Extract RHCOS build from release image
Browse files Browse the repository at this point in the history
Since e2b31b2 (bootkube: Supply machine-os-content to MCO,
2019-01-29, openshift#1149), we have been using the machine-os-content image to
seed the machine-config operator.  With this commit, use the RHCOS
build ID from that image's annotations to calculate our AMI, etc. as
well.  This gives one less degree of freedom for breaking things ;).
Users who want to test clusters based on a different RHCOS build
should bump the value in their release image, just like users testing
operator updates and other changes.

The new pkg/asset/release subpackage allows users to continue using
pkg/rhcos without pulling in all of containers/image as a dependency.

The pull-secret handling is a bit of a hack, leaning on the fact that
users are likely providing clean secrets from [1].  Hopefully soon
containers/image will grow an API for injecting in-memory bytes into
their more-robust Docker-auth-config parser, but my attempt at that
[2] is unlikely to land in the next few days, so I've cludged together
a minimal implementation here.

[1]: https://cloud.openshift.com/clusters/install#pull-secret
[2]: containers/image#588
  • Loading branch information
wking committed Mar 11, 2019
1 parent 5455c4a commit 6887262
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 88 deletions.
6 changes: 1 addition & 5 deletions hack/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@ release)
TAGS="${TAGS} release"
if test -n "${RELEASE_IMAGE}"
then
LDFLAGS="${LDFLAGS} -X github.com/openshift/installer/pkg/asset/ignition/bootstrap.defaultReleaseImage=${RELEASE_IMAGE}"
fi
if test -n "${RHCOS_BUILD_NAME}"
then
LDFLAGS="${LDFLAGS} -X github.com/openshift/installer/pkg/rhcos.buildName=${RHCOS_BUILD_NAME}"
LDFLAGS="${LDFLAGS} -X github.com/openshift/installer/pkg/asset/release.defaultImage=${RELEASE_IMAGE}"
fi
if test "${SKIP_GENERATION}" != y
then
Expand Down
19 changes: 6 additions & 13 deletions pkg/asset/ignition/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/openshift/installer/pkg/asset/kubeconfig"
"github.com/openshift/installer/pkg/asset/machines"
"github.com/openshift/installer/pkg/asset/manifests"
"github.com/openshift/installer/pkg/asset/release"
"github.com/openshift/installer/pkg/asset/tls"
"github.com/openshift/installer/pkg/types"
)
Expand All @@ -35,10 +36,6 @@ const (
ignitionUser = "core"
)

var (
defaultReleaseImage = "registry.svc.ci.openshift.org/openshift/origin-release:v4.0"
)

// bootstrapTemplateData is the data to use to replace values in bootstrap
// template files.
type bootstrapTemplateData struct {
Expand Down Expand Up @@ -66,6 +63,7 @@ func (a *Bootstrap) Dependencies() []asset.Asset {
&machines.Master{},
&manifests.Manifests{},
&manifests.Openshift{},
new(release.Image),
&tls.AdminKubeConfigCABundle{},
&tls.AggregatorCA{},
&tls.AggregatorCABundle{},
Expand Down Expand Up @@ -116,9 +114,10 @@ func (a *Bootstrap) Dependencies() []asset.Asset {
// Generate generates the ignition config for the Bootstrap asset.
func (a *Bootstrap) Generate(dependencies asset.Parents) error {
installConfig := &installconfig.InstallConfig{}
dependencies.Get(installConfig)
releaseImage := new(release.Image)
dependencies.Get(installConfig, releaseImage)

templateData, err := a.getTemplateData(installConfig.Config)
templateData, err := a.getTemplateData(installConfig.Config, string(*releaseImage))
if err != nil {
return errors.Wrap(err, "failed to get bootstrap templates")
}
Expand Down Expand Up @@ -170,18 +169,12 @@ func (a *Bootstrap) Files() []*asset.File {
}

// getTemplateData returns the data to use to execute bootstrap templates.
func (a *Bootstrap) getTemplateData(installConfig *types.InstallConfig) (*bootstrapTemplateData, error) {
func (a *Bootstrap) getTemplateData(installConfig *types.InstallConfig, releaseImage string) (*bootstrapTemplateData, error) {
etcdEndpoints := make([]string, *installConfig.ControlPlane.Replicas)
for i := range etcdEndpoints {
etcdEndpoints[i] = fmt.Sprintf("https://etcd-%d.%s:2379", i, installConfig.ClusterDomain())
}

releaseImage := defaultReleaseImage
if ri, ok := os.LookupEnv("OPENSHIFT_INSTALL_RELEASE_IMAGE_OVERRIDE"); ok && ri != "" {
logrus.Warn("Found override for ReleaseImage. Please be warned, this is not advised")
releaseImage = ri
}

return &bootstrapTemplateData{
EtcdCertSignerImage: etcdCertSignerImage,
PullSecret: installConfig.PullSecret,
Expand Down
41 changes: 41 additions & 0 deletions pkg/asset/release/release.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Package release contains assets for the release image (also known
// as the update payload).
package release

import (
"os"

"github.com/sirupsen/logrus"

"github.com/openshift/installer/pkg/asset"
)

var (
defaultImage = "registry.svc.ci.openshift.org/openshift/origin-release:v4.0"
)

// Image is the pull-spec for the release image.
type Image string

var _ asset.Asset = (*Image)(nil)

// Name returns the human-friendly name of the asset.
func (i *Image) Name() string {
return "Release Image"
}

// Dependencies returns no dependencies.
func (i *Image) Dependencies() []asset.Asset {
return nil
}

// Generate the release image.
func (i *Image) Generate(p asset.Parents) error {
releaseImage := defaultImage
if ri := os.Getenv("OPENSHIFT_INSTALL_RELEASE_IMAGE_OVERRIDE"); ri != "" {
logrus.Warn("Found override for Image. Please be warned, this is not advised")
releaseImage = ri
}
*i = Image(releaseImage)
return nil
}
29 changes: 15 additions & 14 deletions pkg/asset/rhcos/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ package rhcos

import (
"context"
"os"
"time"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"

"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/asset/installconfig"
releaseasset "github.com/openshift/installer/pkg/asset/release"
"github.com/openshift/installer/pkg/rhcos"
"github.com/openshift/installer/pkg/rhcos/release"
"github.com/openshift/installer/pkg/types/aws"
"github.com/openshift/installer/pkg/types/libvirt"
"github.com/openshift/installer/pkg/types/none"
Expand All @@ -30,34 +30,35 @@ func (i *Image) Name() string {
return "Image"
}

// Dependencies returns no dependencies.
// Dependencies returns the assets on which the Image asset depends.
func (i *Image) Dependencies() []asset.Asset {
return []asset.Asset{
&installconfig.InstallConfig{},
new(releaseasset.Image),
}
}

// Generate the RHCOS image location.
func (i *Image) Generate(p asset.Parents) error {
if oi, ok := os.LookupEnv("OPENSHIFT_INSTALL_OS_IMAGE_OVERRIDE"); ok && oi != "" {
logrus.Warn("Found override for OS Image. Please be warned, this is not advised")
*i = Image(oi)
return nil
}

ic := &installconfig.InstallConfig{}
p.Get(ic)
releaseImage := new(releaseasset.Image)
p.Get(ic, releaseImage)
config := ic.Config

ctx := context.TODO()
build, err := release.RHCOSBuild(ctx, string(*releaseImage), []byte(ic.Config.PullSecret))
if err != nil {
return err
}

var osimage string
var err error
ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
switch config.Platform.Name() {
case aws.Name:
osimage, err = rhcos.AMI(ctx, rhcos.DefaultChannel, config.Platform.AWS.Region)
osimage, err = rhcos.AMI(ctx, rhcos.DefaultChannel, build, config.Platform.AWS.Region)
case libvirt.Name:
osimage, err = rhcos.QEMU(ctx, rhcos.DefaultChannel)
osimage, err = rhcos.QEMU(ctx, rhcos.DefaultChannel, build)
case openstack.Name:
osimage = "rhcos"
case none.Name:
Expand Down
4 changes: 2 additions & 2 deletions pkg/rhcos/ami.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
)

// AMI fetches the HVM AMI ID of the latest Red Hat Enterprise Linux CoreOS release.
func AMI(ctx context.Context, channel, region string) (string, error) {
meta, err := fetchLatestMetadata(ctx, channel)
func AMI(ctx context.Context, channel, build, region string) (string, error) {
meta, err := fetchMetadata(ctx, channel, build)
if err != nil {
return "", errors.Wrap(err, "failed to fetch RHCOS metadata")
}
Expand Down
53 changes: 1 addition & 52 deletions pkg/rhcos/builds.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ var (
// DefaultChannel is the default RHCOS channel for the cluster.
DefaultChannel = "maipo"

// buildName is the name of the build in the channel that will be picked up
// empty string means the first one in the build list (latest) will be used
buildName = ""

baseURL = "https://releases-rhcos.svc.ci.openshift.org/storage/releases"
)

Expand All @@ -36,16 +32,7 @@ type metadata struct {
OSTreeVersion string `json:"ostree-version"`
}

func fetchLatestMetadata(ctx context.Context, channel string) (metadata, error) {
build := buildName
var err error
if build == "" {
build, err = fetchLatestBuild(ctx, channel)
if err != nil {
return metadata{}, errors.Wrap(err, "failed to fetch latest build")
}
}

func fetchMetadata(ctx context.Context, channel string, build string) (metadata, error) {
url := fmt.Sprintf("%s/%s/%s/meta.json", baseURL, channel, build)
logrus.Debugf("Fetching RHCOS metadata from %q", url)
req, err := http.NewRequest("GET", url, nil)
Expand Down Expand Up @@ -76,41 +63,3 @@ func fetchLatestMetadata(ctx context.Context, channel string) (metadata, error)

return meta, nil
}

func fetchLatestBuild(ctx context.Context, channel string) (string, error) {
url := fmt.Sprintf("%s/%s/builds.json", baseURL, channel)
logrus.Debugf("Fetching RHCOS builds from %q", url)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return "", errors.Wrap(err, "failed to build request")
}

client := &http.Client{}
resp, err := client.Do(req.WithContext(ctx))
if err != nil {
return "", errors.Wrap(err, "failed to fetch builds")
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return "", errors.Errorf("incorrect HTTP response (%s)", resp.Status)
}

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", errors.Wrap(err, "failed to read HTTP response")
}

var builds struct {
Builds []string `json:"builds"`
}
if err := json.Unmarshal(body, &builds); err != nil {
return "", errors.Wrap(err, "failed to parse HTTP response")
}

if len(builds.Builds) == 0 {
return "", errors.Errorf("no builds found")
}

return builds.Builds[0], nil
}
4 changes: 2 additions & 2 deletions pkg/rhcos/qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
)

// QEMU fetches the URL of the latest Red Hat Enterprise Linux CoreOS release.
func QEMU(ctx context.Context, channel string) (string, error) {
meta, err := fetchLatestMetadata(ctx, channel)
func QEMU(ctx context.Context, channel string, build string) (string, error) {
meta, err := fetchMetadata(ctx, channel, build)
if err != nil {
return "", errors.Wrap(err, "failed to fetch RHCOS metadata")
}
Expand Down
48 changes: 48 additions & 0 deletions pkg/rhcos/release/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package release

import (
"encoding/base64"
"encoding/json"
"strings"

"github.com/containers/image/types"
"github.com/docker/distribution/reference"
"github.com/pkg/errors"
)

type auth struct {
Auth string `json:"auth"`
}

type config struct {
Auths map[string]auth `json:"auths"`
}

func addPullSecret(sys *types.SystemContext, pullSecret []byte, named reference.Named) error {
var auths config
err := json.Unmarshal(pullSecret, &auths)
if err != nil {
return err
}

authority := reference.Domain(named)
auth, ok := auths.Auths[authority] // hack: skipping normalization
if ok {
decoded, err := base64.StdEncoding.DecodeString(auth.Auth)
if err != nil {
return err
}

parts := strings.SplitN(string(decoded), ":", 2)
if len(parts) != 2 {
return errors.Errorf("invalid pull-secret entry for %q", authority)
}

sys.DockerAuthConfig = &types.DockerAuthConfig{
Username: parts[0],
Password: parts[1],
}
}

return nil
}
Loading

0 comments on commit 6887262

Please sign in to comment.