Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support installing extensions and rework OS update #1941

Merged
merged 11 commits into from
Aug 1, 2020
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 5 additions & 1 deletion cmd/machine-config-daemon/pivot.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ func run(_ *cobra.Command, args []string) (retErr error) {

client := daemon.NewNodeUpdaterClient()

_, changed, err := client.PullAndRebase(container, keep)
osImageContentDir, err := daemon.ExtractOSImage(container)
if err != nil {
return err
}
Copy link
Member

Choose a reason for hiding this comment

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

any reason not to do:

osImageContentDir, err = daemon.ExtractOSImage(container)
if err != nil {
    return err
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not really, missed to refactor at the end in proper way. fixed

Copy link
Member

Choose a reason for hiding this comment

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

This breaks OKD as it doesn't have oc in FCOS image. From release-image.log on bootstrap node:

Jul 29 08:25:43 ip-10-0-7-95 release-image-download.sh[1450]: I0729 08:25:43.638761    1450 run.go:18] Running: oc image extract --path /:/run/mco-machine-os-content/os-content-779087097 registry.svc.ci.openshift.org/ci-op-lx6rlf8w/stable@sha256:f5283b939e47e2158602d2954f9f1ce1e18ffafb2e711d4187f5954cacc050a6
Jul 29 08:25:43 ip-10-0-7-95 release-image-download.sh[1450]: W0729 08:25:43.638849    1450 run.go:44] oc failed: running oc image extract --path /:/run/mco-machine-os-content/os-content-779087097 registry.svc.ci.openshift.org/ci-op-lx6rlf8w/stable@sha256:f5283b939e47e2158602d2954f9f1ce1e18ffafb2e711d4187f5954cacc050a6 failed: : exec: "oc": executable file not found in $PATH; retrying...

Copy link
Member

Choose a reason for hiding this comment

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

decided to follow up thanks Vadim 🙏 #1941 (comment)

changed, err := client.Rebase(container, osImageContentDir)
if err != nil {
return err
}
Expand Down
23 changes: 6 additions & 17 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,20 @@ require (
github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/semver v1.4.2 // indirect
github.com/Masterminds/sprig v2.20.0+incompatible
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/OpenPeeDeeP/depguard v1.0.1 // indirect
github.com/apparentlymart/go-cidr v1.0.0
github.com/ashcrow/osrelease v0.0.0-20180626175927-9b292693c55c
github.com/clarketm/json v1.14.1
github.com/containers/image v3.0.2+incompatible
Copy link
Contributor

Choose a reason for hiding this comment

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

Hum, container-runtime-config (and openshift/runtime-utils) still use the old version. That should probably be updated to decrease duplication… but it’s also rather out of scope of this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, we can clean this up separately.

github.com/containers/storage v1.13.5
github.com/containers/image/v5 v5.5.1
github.com/containers/storage v1.20.2
github.com/coreos/fcct v0.5.0
github.com/coreos/go-semver v0.3.0
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
github.com/coreos/ign-converter v0.0.0-20200629171308-e40a44f244c5
github.com/coreos/ignition v0.35.0
github.com/coreos/ignition/v2 v2.3.0
github.com/davecgh/go-spew v1.1.1
github.com/docker/docker v1.4.2-0.20190927142053-ada3c14355ce // indirect
github.com/docker/docker-credential-helpers v0.6.3 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 // indirect
github.com/elazarl/goproxy/ext v0.0.0-20190911111923-ecfe977594f1 // indirect
Expand All @@ -41,31 +38,23 @@ require (
github.com/gostaticanalysis/analysisutil v0.0.3 // indirect
github.com/hashicorp/golang-lru v0.5.3 // indirect
github.com/huandu/xstrings v1.2.0 // indirect
github.com/imdario/mergo v0.3.7
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/imdario/mergo v0.3.9
github.com/magiconair/properties v1.8.1 // indirect
github.com/mattn/go-isatty v0.0.9 // indirect
github.com/mtrmac/gpgme v0.1.2 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 // indirect
github.com/opencontainers/runc v1.0.0-rc8.0.20190827142921-dd075602f158 // indirect
github.com/openshift/api v3.9.1-0.20191111211345-a27ff30ebf09+incompatible
github.com/openshift/client-go v0.0.0-20200320150128-a906f3d8e723
github.com/openshift/library-go v0.0.0-20200320155611-2a351bebf158
github.com/openshift/runtime-utils v0.0.0-20191011150825-9169de69ebf6
github.com/pkg/errors v0.8.1
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.1.0
github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d
github.com/spf13/cobra v0.0.5
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.4.0
github.com/stretchr/testify v1.6.1
github.com/ultraware/funlen v0.0.2 // indirect
github.com/vincent-petithory/dataurl v0.0.0-20160330182126-9a301d65acbb
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.1.0 // indirect
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
google.golang.org/appengine v1.6.1 // indirect
k8s.io/api v0.18.3
k8s.io/apiextensions-apiserver v0.18.0
Expand Down
202 changes: 109 additions & 93 deletions go.sum

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,12 @@ spec:
description: Name is the name of the unit. This must be suffixed with a
valid unit type (e.g. 'thing.service')
type: string
extensions:
description: List of additional features that can be enabled on host
type: array
items:
type: string
nullable: true
fips:
description: FIPS controls FIPS mode
type: boolean
Expand Down
4 changes: 4 additions & 0 deletions lib/resourcemerge/machineconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ func ensureMachineConfigSpec(modified *bool, existing *mcfgv1.MachineConfigSpec,
*modified = true
(*existing).FIPS = required.FIPS
}
if !equality.Semantic.DeepEqual(existing.Extensions, required.Extensions) {
*modified = true
(*existing).Extensions = required.Extensions
}
}

func ensureControllerConfigSpec(modified *bool, existing *mcfgv1.ControllerConfigSpec, required mcfgv1.ControllerConfigSpec) {
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/machineconfiguration.openshift.io/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ type MachineConfigSpec struct {

// +nullable
KernelArguments []string `json:"kernelArguments"`
Extensions []string `json:"extensions"`

FIPS bool `json:"fips"`
KernelType string `json:"kernelType"`
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions pkg/controller/common/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ func MergeMachineConfigs(configs []*mcfgv1.MachineConfig, osImageURL string) (*m
kargs = append(kargs, cfg.Spec.KernelArguments...)
}

extensions := []string{}
for _, cfg := range configs {
extensions = append(extensions, cfg.Spec.Extensions...)
}

// Ensure that kernel-devel extension is applied only with default kernel.
if kernelType != KernelTypeDefault {
if InSlice("kernel-devel", extensions) {
return nil, fmt.Errorf("installing kernel-devel extension is not supported with kernelType: %s", kernelType)
}
}

return &mcfgv1.MachineConfig{
Spec: mcfgv1.MachineConfigSpec{
OSImageURL: osImageURL,
Expand All @@ -112,6 +124,7 @@ func MergeMachineConfigs(configs []*mcfgv1.MachineConfig, osImageURL string) (*m
},
FIPS: fips,
KernelType: kernelType,
Extensions: extensions,
},
}, nil
}
Expand Down Expand Up @@ -285,12 +298,41 @@ func ValidateIgnition(ignconfig interface{}) error {
}
}

// InSlice search for an element in slice and return true if found, otherwise return false
func InSlice(elem string, slice []string) bool {
Copy link
Member

Choose a reason for hiding this comment

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

can be unexported inSlice till we find a use for it (if any)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks for catching this, this was carried from work done in #1850. I missed updating inArray() call in update.go to InSlice(). Fixed https://github.com/openshift/machine-config-operator/pull/1941/files#diff-06961b075f1753956d802ba954d2cfb5R694

for _, k := range slice {
if k == elem {
return true
}
}
return false
}

func validateExtensions(exts []string) error {
supportedExtensions := []string{"usbguard", "kernel-devel"}
Copy link
Contributor

@ericavonb ericavonb Jul 27, 2020

Choose a reason for hiding this comment

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

Could these be constants exported alongside the MC api types? If we could even add them to the CRD's openapi schema that'd be even better, so that users get immediate feedback on invalid extensions.

Or maybe even have a type OSExtensions string?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not very sure. The list of supported extensions is going to change whenever we add more extensions support in upcoming releases. In future, we are also exploring to add metadata for supported extension list in machine-os-content openshift/os#409 which MCO can read from and validate before rendering.

invalidExts := []string{}
for _, ext := range exts {
if !InSlice(ext, supportedExtensions) {
invalidExts = append(invalidExts, ext)
}
}
if len(invalidExts) != 0 {
return fmt.Errorf("invalid extensions found: %v", invalidExts)
}
return nil

}

// ValidateMachineConfig validates that given MachineConfig Spec is valid.
func ValidateMachineConfig(cfg mcfgv1.MachineConfigSpec) error {
if !(cfg.KernelType == "" || cfg.KernelType == KernelTypeDefault || cfg.KernelType == KernelTypeRealtime) {
return errors.Errorf("kernelType=%s is invalid", cfg.KernelType)
}

if err := validateExtensions(cfg.Extensions); err != nil {
return err
}

if cfg.Config.Raw != nil {
ignCfg, err := IgnParseWrapper(cfg.Config.Raw)
if err != nil {
Expand Down
12 changes: 11 additions & 1 deletion pkg/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,17 @@ func (dn *Daemon) checkStateOnFirstRun() error {
if !osMatch {
glog.Infof("Bootstrap pivot required to: %s", targetOSImageURL)
// This only returns on error
Copy link
Member

Choose a reason for hiding this comment

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

Should move this comment down to the dn.finalizeAndReboot now I suppose?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

umm, we will return back here also when updateOS() fails?

return dn.updateOSAndReboot(state.currentConfig)
osImageContentDir, err := ExtractOSImage(targetOSImageURL)
if err != nil {
return err
}
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

if err := dn.updateOS(state.currentConfig, osImageContentDir); err != nil {
return err
}
if err := os.RemoveAll(osImageContentDir); err != nil {
return err
}
return dn.finalizeAndReboot(state.currentConfig)
}
glog.Info("No bootstrap pivot required; unlinking bootstrap node annotations")

Expand Down
83 changes: 83 additions & 0 deletions pkg/daemon/image-inspect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package daemon

import (
"context"
"fmt"
"math"
"strings"
"time"

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

func retryIfNecessary(ctx context.Context, operation func() error) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason not to use k8s.io/client-go/util/retry?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

err := operation()
for attempt := 0; err != nil && attempt < numRetriesNetCommands; attempt++ {
delay := time.Duration(int(math.Pow(2, float64(attempt)))) * time.Second
fmt.Printf("Warning: failed, retrying in %s ... (%d/%d)", delay, attempt+1, numRetriesNetCommands)
select {
case <-time.After(delay):
break
case <-ctx.Done():
return err
}
err = operation()
}
return err
}

// newDockerImageSource creates an image source for an image reference.
// The caller must call .Close() on the returned ImageSource.
func newDockerImageSource(ctx context.Context, sys *types.SystemContext, name string) (types.ImageSource, error) {
var imageName string
if !strings.HasPrefix(name, "//") {
imageName = "//" + name
} else {
imageName = name
}
ref, err := docker.ParseReference(imageName)
if err != nil {
return nil, err
}

return ref.NewImageSource(ctx, sys)
}

// This function has been inspired from upstream skopeo inspect, see https://github.com/containers/skopeo/blob/master/cmd/skopeo/inspect.go
// We can use skopeo inspect directly once fetching RepoTags becomes optional in skopeo.
func imageInspect(imageName string) (*types.ImageInspectInfo, error) {
var (
src types.ImageSource
imgInspect *types.ImageInspectInfo
err error
)

ctx := context.Background()
sys := &types.SystemContext{AuthFilePath: kubeletAuthFile}

if err := retryIfNecessary(ctx, func() error {
src, err = newDockerImageSource(ctx, sys, imageName)
return err
}); err != nil {
return nil, errors.Wrapf(err, "Error parsing image name %q", imageName)
}

defer src.Close()

img, err := image.FromUnparsedImage(ctx, sys, image.UnparsedInstance(src, nil))
if err != nil {
return nil, fmt.Errorf("Error parsing manifest for image: %v", err)
}

if err := retryIfNecessary(ctx, func() error {
imgInspect, err = img.Inspect(ctx)
return err
}); err != nil {
return nil, err
}

return imgInspect, nil
}
Loading