Skip to content

Commit

Permalink
Merge pull request #1285 from jlebon/pr/apply
Browse files Browse the repository at this point in the history
  • Loading branch information
jlebon committed Feb 25, 2022
2 parents d3c09e3 + 3401692 commit d554570
Show file tree
Hide file tree
Showing 57 changed files with 6,362 additions and 12 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/google/uuid v1.1.1
github.com/pin/tftp v2.1.0+incompatible
github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa // indirect
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace
github.com/stretchr/testify v1.7.0
github.com/vincent-petithory/dataurl v1.0.0
github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa h1:E+gaaifzi2xF65PbDmuKI3PhLWY6G5opMLniFq8vmXA=
github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU=
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace h1:9PNP1jnUjRhfmGMlkXHjYPishpcw4jpSt/V/xYY3FMA=
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
Expand Down
120 changes: 120 additions & 0 deletions internal/apply/apply.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright 2021 Red Hat, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apply

import (
"errors"
"fmt"
"os"
"path/filepath"

"github.com/coreos/ignition/v2/internal/exec"
"github.com/coreos/ignition/v2/internal/exec/stages"
_ "github.com/coreos/ignition/v2/internal/exec/stages/disks"
_ "github.com/coreos/ignition/v2/internal/exec/stages/fetch"
_ "github.com/coreos/ignition/v2/internal/exec/stages/fetch_offline"
_ "github.com/coreos/ignition/v2/internal/exec/stages/files"
_ "github.com/coreos/ignition/v2/internal/exec/stages/kargs"
_ "github.com/coreos/ignition/v2/internal/exec/stages/mount"
_ "github.com/coreos/ignition/v2/internal/exec/stages/umount"
"github.com/coreos/ignition/v2/internal/log"
"github.com/coreos/ignition/v2/internal/resource"
"github.com/coreos/ignition/v2/internal/state"
"github.com/coreos/ignition/v2/internal/util"

"github.com/coreos/ignition/v2/config/v3_4_experimental/types"
)

type Flags struct {
Root string
IgnoreUnsupported bool
Offline bool
}

func inContainer() bool {
// this is set by various container tools like systemd-nspawn, podman,
// docker, etc...
if val, _ := os.LookupEnv("container"); val != "" {
return true
}

// check for the podman or docker container files
paths := []string{"/run/.containerenv", "/.dockerenv"}
for _, path := range paths {
if _, err := os.Stat(path); err == nil {
return true
}
}

return false
}

func Run(cfg types.Config, flags Flags, logger *log.Logger) error {
if !inContainer() {
return errors.New("this tool is not designed to run on a host system; reprovision the machine instead")
}

// make absolute because our code assumes that
var err error
if flags.Root, err = filepath.Abs(flags.Root); err != nil {
return err
}

fetcher := resource.Fetcher{
Logger: logger,
Offline: flags.Offline,
}

state := state.State{}
cfgFetcher := exec.ConfigFetcher{
Logger: logger,
Fetcher: &fetcher,
State: &state,
}

finalCfg, err := cfgFetcher.RenderConfig(cfg)
if err != nil {
return err
}

// verify upfront if we'll need networking but we're not allowed
if flags.Offline {
stage := stages.Get("fetch-offline").Create(logger, flags.Root, fetcher, &state)
if err := stage.Run(finalCfg); err != nil {
return err
}
}

// Order in which to apply live. This is overkill since effectively only
// `files` supports it right now, but let's be extensible. Also ensures that
// all stages are accounted for.
stagesOrder := []string{"fetch-offline", "fetch", "kargs", "disks", "mount", "files", "umount"}
allStages := stages.Names()
if len(stagesOrder) != len(allStages) {
panic(fmt.Sprintf("%v != %v", stagesOrder, allStages))
}

for _, stageName := range stagesOrder {
if !util.StrSliceContains(allStages, stageName) {
panic(fmt.Sprintf("stage '%s' invalid", stageName))
}
stage := stages.Get(stageName).Create(logger, flags.Root, fetcher, &state)
if err := stage.Apply(finalCfg, flags.IgnoreUnsupported); err != nil {
return fmt.Errorf("running stage '%s': %w", stageName, err)
}
}

return nil
}
9 changes: 9 additions & 0 deletions internal/exec/stages/disks/disks.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package disks

import (
"errors"
"fmt"
"os/exec"

Expand Down Expand Up @@ -72,6 +73,14 @@ func isNoOp(config types.Config) bool {
len(config.Storage.Luks) == 0
}

func (s stage) Apply(config types.Config, ignoreUnsupported bool) error {
// in theory, we could support this, but for now we don't need it
if isNoOp(config) || ignoreUnsupported {
return nil
}
return errors.New("cannot apply disk modifications live")
}

func (s stage) Run(config types.Config) error {
// Interacting with disks/partitions/raids/filesystems in general can cause
// udev races. If we do not need to do anything, we also do not need to
Expand Down
4 changes: 4 additions & 0 deletions internal/exec/stages/fetch/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ func (stage) Name() string {
return name
}

func (s stage) Apply(_ types.Config, _ bool) error {
return nil
}

func (s stage) Run(_ types.Config) error {
// Nothing - all we do is fetch and allow anything else in the initramfs to run
s.Logger.Info("fetch complete")
Expand Down
4 changes: 4 additions & 0 deletions internal/exec/stages/fetch_offline/fetch-offline.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ func (stage) Name() string {
return name
}

func (s stage) Apply(_ types.Config, _ bool) error {
return nil
}

func (s stage) Run(cfg types.Config) error {
if needsNet, err := configNeedsNet(&cfg); err != nil {
return err
Expand Down
48 changes: 36 additions & 12 deletions internal/exec/stages/files/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,32 @@ func (stage) Name() string {
return name
}

func (s stage) Apply(config types.Config, ignoreUnsupported bool) error {
return s.runImpl(config, true, ignoreUnsupported)
}

func (s stage) Run(config types.Config) error {
if err := s.checkRelabeling(); err != nil {
return fmt.Errorf("failed to check if SELinux labeling required: %v", err)
return s.runImpl(config, false, false)
}

func (s stage) runImpl(config types.Config, isApply bool, applyIgnoreUnsupported bool) error {
if !isApply {
// !isApply: SELinux is handled differently in container flows
if err := s.checkRelabeling(); err != nil {
return fmt.Errorf("failed to check if SELinux labeling required: %v", err)
}
}

if err := s.createPasswd(config); err != nil {
return fmt.Errorf("failed to create users/groups: %v", err)
// theoretically could support this, but the main user (CoreOS layering)
// does not: https://github.com/coreos/rpm-ostree/issues/3435
if isApply {
if !applyIgnoreUnsupported && (len(config.Passwd.Users) > 0 || len(config.Passwd.Groups) > 0) {
return errors.New("cannot apply passwd live")
}
} else {
if err := s.createPasswd(config); err != nil {
return fmt.Errorf("failed to create users/groups: %v", err)
}
}

if err := s.createFilesystemsEntries(config); err != nil {
Expand All @@ -83,16 +102,21 @@ func (s stage) Run(config types.Config) error {
return fmt.Errorf("failed to create units: %v", err)
}

if err := s.createCrypttabEntries(config); err != nil {
return fmt.Errorf("creating crypttab entries: %v", err)
}
if !isApply {
// !isApply: we don't support LUKS, so this isn't necessary
if err := s.createCrypttabEntries(config); err != nil {
return fmt.Errorf("creating crypttab entries: %v", err)
}

if err := s.createResultFile(); err != nil {
return fmt.Errorf("creating result file: %v", err)
}
// !isApply: we support running Ignition multiple times
if err := s.createResultFile(); err != nil {
return fmt.Errorf("creating result file: %v", err)
}

if err := s.relabelFiles(); err != nil {
return fmt.Errorf("failed to handle relabeling: %v", err)
// !isApply: SELinux is handled differently in container flows
if err := s.relabelFiles(); err != nil {
return fmt.Errorf("failed to handle relabeling: %v", err)
}
}

return nil
Expand Down
8 changes: 8 additions & 0 deletions internal/exec/stages/kargs/kargs.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package kargs

import (
"errors"
"fmt"
"os/exec"

Expand Down Expand Up @@ -65,6 +66,13 @@ func isNoOp(config types.Config) bool {
len(config.KernelArguments.ShouldNotExist) == 0
}

func (s stage) Apply(config types.Config, ignoreUnsupported bool) error {
if isNoOp(config) || ignoreUnsupported {
return nil
}
return errors.New("cannot apply kargs modifications live")
}

func (s stage) Run(config types.Config) error {
if isNoOp(config) {
return nil
Expand Down
8 changes: 8 additions & 0 deletions internal/exec/stages/mount/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package mount

import (
"errors"
"fmt"
"os"
"os/exec"
Expand Down Expand Up @@ -68,6 +69,13 @@ func (stage) Name() string {
return name
}

func (s stage) Apply(config types.Config, ignoreUnsupported bool) error {
if len(config.Storage.Filesystems) == 0 || ignoreUnsupported {
return nil
}
return errors.New("cannot apply filesystems modifications live")
}

func (s stage) Run(config types.Config) error {
fss := []types.Filesystem{}
for _, fs := range config.Storage.Filesystems {
Expand Down
1 change: 1 addition & 0 deletions internal/exec/stages/stages.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
// Stage is responsible for actually executing a stage of the configuration.
type Stage interface {
Run(config types.Config) error
Apply(config types.Config, ignoreUnsupported bool) error
Name() string
}

Expand Down
8 changes: 8 additions & 0 deletions internal/exec/stages/umount/umount.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package umount

import (
"errors"
"sort"

cutil "github.com/coreos/ignition/v2/config/util"
Expand Down Expand Up @@ -64,6 +65,13 @@ func (stage) Name() string {
return name
}

func (s stage) Apply(config types.Config, ignoreUnsupported bool) error {
if len(config.Storage.Filesystems) == 0 || ignoreUnsupported {
return nil
}
return errors.New("cannot apply filesystems modifications live")
}

func (s stage) Run(config types.Config) error {
fss := []types.Filesystem{}
for _, fs := range config.Storage.Filesystems {
Expand Down
Loading

0 comments on commit d554570

Please sign in to comment.