diff --git a/internal/exec/engine.go b/internal/exec/engine.go index 862831a2a4..637443981e 100644 --- a/internal/exec/engine.go +++ b/internal/exec/engine.go @@ -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" @@ -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 @@ -67,6 +71,26 @@ func (e Engine) Run(stageName string) error { Ignition: types.Ignition{Version: types.MaxVersion.String()}, } + if stageName == "fetch" && e.DetectOfflineConfig != "" { + var haveConfig bool + var err error + // 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 { @@ -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, + } + + 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 diff --git a/internal/main.go b/internal/main.go index f630b917b6..4a9a783f8c 100644 --- a/internal/main.go +++ b/internal/main.go @@ -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") 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())) @@ -60,7 +63,7 @@ func main() { return } - if flags.platform == "" { +if flags.platform == "" { fmt.Fprint(os.Stderr, "'--platform' must be provided\n") os.Exit(2) } @@ -95,6 +98,8 @@ func main() { ConfigCache: flags.configCache, PlatformConfig: platformConfig, Fetcher: &fetcher, + + DetectOfflineConfig: flags.detectOfflineConfig, } err = engine.Run(flags.stage.String()) diff --git a/internal/providers/cmdline/cmdline.go b/internal/providers/cmdline/cmdline.go index b92e8bc41b..0c12e02508 100644 --- a/internal/providers/cmdline/cmdline.go +++ b/internal/providers/cmdline/cmdline.go @@ -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 { diff --git a/internal/providers/file/file.go b/internal/providers/file/file.go index 0f62277999..b85dc372f9 100644 --- a/internal/providers/file/file.go +++ b/internal/providers/file/file.go @@ -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) diff --git a/internal/providers/providers.go b/internal/providers/providers.go index 1d6c08e311..ee9aa19236 100644 --- a/internal/providers/providers.go +++ b/internal/providers/providers.go @@ -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 diff --git a/internal/resource/url.go b/internal/resource/url.go index c5b8afed6d..8e7958d401 100644 --- a/internal/resource/url.go +++ b/internal/resource/url.go @@ -63,6 +63,9 @@ type Fetcher struct { // The logger object to use when logging information. Logger *log.Logger + // Offline is set when + Offline bool + // client is the http client that will be used when fetching http(s) // resources. If left nil, one will be created and used, but this means any // timeouts Ignition was configured to used will be ignored.