Skip to content
Closed
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions internal/exec/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"os"
"time"

"github.com/coreos/ignition/v2/internal/providers/file"

"github.com/coreos/ignition/v2/config"
"github.com/coreos/ignition/v2/config/shared/errors"
latest "github.com/coreos/ignition/v2/config/v3_1_experimental"
Expand Down Expand Up @@ -54,6 +56,8 @@ type Engine struct {
Root string
PlatformConfig platform.Config
Fetcher *resource.Fetcher

DetectOfflineConfig string
}

// Run executes the stage of the given name. It returns true if the stage
Expand All @@ -67,6 +71,26 @@ func (e Engine) Run(stageName string) error {
Ignition: types.Ignition{Version: types.MaxVersion.String()},
}

if stageName == "fetch" && e.DetectOfflineConfig != "" {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We should fix the Ignition code so that the stages can have their own CLI args.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

👍 I can't agree more.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WDYT about making this a helper func? func (e *Engine) HaveConfig() (bool, err)

var haveConfig bool
// If we already have a config cache, we're done
if _, err := os.Stat(e.ConfigCache); err != nil {
haveConfig = true
} else {
haveConfig, err = e.detectOfflineConfig()
if err != nil {
return err
}
}
if haveConfig {
if err := ioutil.WriteFile(e.DetectOfflineConfig, []byte{}, 0644); err != nil {
return err
}
}
// Note early return
return nil
}

systemBaseConfig, r, err := system.FetchBaseConfig(e.Logger)
e.logReport(r)
if err != nil && err != providers.ErrNoProvider {
Expand Down Expand Up @@ -175,6 +199,26 @@ func (e *Engine) acquireConfig() (cfg types.Config, err error) {
return
}

// acquireConfigOffline only looks at config providers which do not
// require networking.
func (e *Engine) detectOfflineConfig() (bool, error) {
offlineFetchers := []providers.FuncDetectConfig{
cmdline.DetectConfig,
file.DetectConfig,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The cmdline provider very much does require networking. The file provider does not, but only runs on the file platform, which is only used for testing.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, this is confusing. The goal here isn't to detect "offline" so much as whether a config was provided at all. We know that "statically" with those two by just looking at the filesystem basically.

For other providers we may need to wait for a driver or even do a network request. Ideally we detect the "no config provided" case with those too. But...that's a higher level thing I think.

}

for _, fetcher := range offlineFetchers {
exists, err := fetcher(e.Fetcher)
if err != nil {
return false, err
}
if exists {
return true, nil
}
}
return false, nil
}

// fetchProviderConfig returns the externally-provided configuration. It first
// checks to see if the command-line option is present. If so, it uses that
// source for the configuration. If the command-line option is not present, it
Expand Down
5 changes: 5 additions & 0 deletions internal/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ func main() {
stage stages.Name
version bool
logToStdout bool

detectOfflineConfig string
}{}

flag.BoolVar(&flags.clearCache, "clear-cache", false, "clear any cached config")
flag.StringVar(&flags.configCache, "config-cache", "/run/ignition.json", "where to cache the config")
flag.DurationVar(&flags.fetchTimeout, "fetch-timeout", exec.DefaultFetchTimeout, "initial duration for which to wait for config")
flag.StringVar(&flags.detectOfflineConfig, "detect-config-provided", "", "If a config is provided, create a file at this path")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The reason we write a stamp file is this is the best pattern I know of to signal the equivalent of Result<bool> as a subprocess. Keying off distinct exit codes feels clunky since it breaks up the flow of using set -e in shell.

flag.Var(&flags.platform, "platform", fmt.Sprintf("current platform. %v", platform.Names()))
flag.StringVar(&flags.root, "root", "/", "root of the filesystem")
flag.Var(&flags.stage, "stage", fmt.Sprintf("execution stage. %v", stages.Names()))
Expand Down Expand Up @@ -95,6 +98,8 @@ func main() {
ConfigCache: flags.configCache,
PlatformConfig: platformConfig,
Fetcher: &fetcher,

DetectOfflineConfig: flags.detectOfflineConfig,
}

err = engine.Run(flags.stage.String())
Expand Down
8 changes: 8 additions & 0 deletions internal/providers/cmdline/cmdline.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ const (
cmdlineUrlFlag = "ignition.config.url"
)

func DetectConfig(f *resource.Fetcher) (bool, error) {
url, err := readCmdline(f.Logger)
if err != nil {
return false, err
}
return url != nil, nil
}

func FetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) {
url, err := readCmdline(f.Logger)
if err != nil {
Expand Down
19 changes: 18 additions & 1 deletion internal/providers/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,29 @@ const (
defaultFilename = "config.ign"
)

func FetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) {
func getPath(f *resource.Fetcher) string {
filename := os.Getenv(cfgFilenameEnvVar)
if filename == "" {
filename = defaultFilename
f.Logger.Info("using default filename")
}
return filename
}

func DetectConfig(f *resource.Fetcher) (bool, error) {
filename := getPath(f)
_, err := os.Stat(filename)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
return true, nil
}

func FetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) {
filename := getPath(f)
f.Logger.Info("using config file at %q", filename)

rawConfig, err := ioutil.ReadFile(filename)
Expand Down
1 change: 1 addition & 0 deletions internal/providers/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var (
ErrNoProvider = errors.New("config provider was not online")
)

type FuncDetectConfig func(f *resource.Fetcher) (bool, error)
type FuncFetchConfig func(f *resource.Fetcher) (types.Config, report.Report, error)
type FuncNewFetcher func(logger *log.Logger) (resource.Fetcher, error)
type FuncPostStatus func(stageName string, f resource.Fetcher, e error) error