From 66042815ae5d2ddb26562cd03351b79352ab9f4e Mon Sep 17 00:00:00 2001 From: Sunny Date: Mon, 10 Aug 2020 20:18:15 +0530 Subject: [PATCH 1/3] Add ignite configuration file support to ignited Adds a new package `pkg/config` to have common configuration handling code. --- cmd/ignite/cmd/root.go | 69 ++--------------------------------- cmd/ignited/cmd/root.go | 13 ++++++- pkg/config/config.go | 81 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 68 deletions(-) create mode 100644 pkg/config/config.go diff --git a/cmd/ignite/cmd/root.go b/cmd/ignite/cmd/root.go index 30e92c71c..a3acd273a 100644 --- a/cmd/ignite/cmd/root.go +++ b/cmd/ignite/cmd/root.go @@ -12,16 +12,12 @@ import ( "github.com/weaveworks/ignite/cmd/ignite/cmd/imgcmd" "github.com/weaveworks/ignite/cmd/ignite/cmd/kerncmd" "github.com/weaveworks/ignite/cmd/ignite/cmd/vmcmd" - api "github.com/weaveworks/ignite/pkg/apis/ignite" - "github.com/weaveworks/ignite/pkg/apis/ignite/scheme" - "github.com/weaveworks/ignite/pkg/constants" + "github.com/weaveworks/ignite/pkg/config" "github.com/weaveworks/ignite/pkg/logs" logflag "github.com/weaveworks/ignite/pkg/logs/flag" - "github.com/weaveworks/ignite/pkg/network" networkflag "github.com/weaveworks/ignite/pkg/network/flag" "github.com/weaveworks/ignite/pkg/providers" "github.com/weaveworks/ignite/pkg/providers/ignite" - "github.com/weaveworks/ignite/pkg/runtime" runtimeflag "github.com/weaveworks/ignite/pkg/runtime/flag" "github.com/weaveworks/ignite/pkg/util" versioncmd "github.com/weaveworks/ignite/pkg/version/cmd" @@ -59,48 +55,8 @@ func NewIgniteCommand(in io.Reader, out, err io.Writer) *cobra.Command { // Create the directories needed for running util.GenericCheckErr(util.CreateDirectories()) - var configFilePath string - - // If an ignite config flag is set, use it as the config file, else check - // if the global config file exists. - // If a config file path is passed, configure ignite using it. - if configPath != "" { - configFilePath = configPath - } else { - // Check the default config location. - if _, err := os.Stat(constants.IGNITE_CONFIG_FILE); !os.IsNotExist(err) { - log.Debugf("Found default ignite configuration file %s", constants.IGNITE_CONFIG_FILE) - configFilePath = constants.IGNITE_CONFIG_FILE - } - } - - if configFilePath != "" { - log.Debugf("Using ignite configuration file %s", configFilePath) - var err error - providers.ComponentConfig, err = getConfigFromFile(configFilePath) - if err != nil { - log.Fatal(err) - } - - // Set providers runtime and network plugin if found in config - // and not set explicitly via flags. - if providers.ComponentConfig.Spec.Runtime != "" && providers.RuntimeName == "" { - providers.RuntimeName = providers.ComponentConfig.Spec.Runtime - } - if providers.ComponentConfig.Spec.NetworkPlugin != "" && providers.NetworkPluginName == "" { - providers.NetworkPluginName = providers.ComponentConfig.Spec.NetworkPlugin - } - } else { - log.Debugln("Using ignite default configurations") - } - - // Set the default runtime and network-plugin if it's not set by - // now. - if providers.RuntimeName == "" { - providers.RuntimeName = runtime.RuntimeContainerd - } - if providers.NetworkPluginName == "" { - providers.NetworkPluginName = network.PluginCNI + if err := config.ApplyConfiguration(configPath); err != nil { + log.Fatal(err) } // Populate the providers after flags have been parsed @@ -173,22 +129,3 @@ func addGlobalFlags(fs *pflag.FlagSet) { func AddQuietFlag(fs *pflag.FlagSet) { fs.BoolVarP(&logs.Quiet, "quiet", "q", logs.Quiet, "The quiet mode allows for machine-parsable output by printing only IDs") } - -// getConfigFromFile reads a config file and returns ignite configuration. -func getConfigFromFile(configPath string) (*api.Configuration, error) { - componentConfig := &api.Configuration{} - - // TODO: Fix libgitops DecodeFileInto to not allow empty files. - if err := scheme.Serializer.DecodeFileInto(configPath, componentConfig); err != nil { - return nil, err - } - - // Ensure the read configuration is valid. If a file contains Kind and - // APIVersion, it's a valid config file. Empty file is invalid. - // NOTE: This is a workaround for libgitops allowing decode of empty file. - if componentConfig.Kind == "" || componentConfig.APIVersion == "" { - return nil, fmt.Errorf("invalid config file, Kind and APIVersion must be set") - } - - return componentConfig, nil -} diff --git a/cmd/ignited/cmd/root.go b/cmd/ignited/cmd/root.go index 9482ee2a8..326637b2c 100644 --- a/cmd/ignited/cmd/root.go +++ b/cmd/ignited/cmd/root.go @@ -5,10 +5,11 @@ import ( "os" "github.com/lithammer/dedent" - "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" + + "github.com/weaveworks/ignite/pkg/config" "github.com/weaveworks/ignite/pkg/logs" logflag "github.com/weaveworks/ignite/pkg/logs/flag" networkflag "github.com/weaveworks/ignite/pkg/network/flag" @@ -18,7 +19,10 @@ import ( versioncmd "github.com/weaveworks/ignite/pkg/version/cmd" ) -var logLevel = logrus.InfoLevel +var logLevel = log.InfoLevel + +// Ignite config file path flag variable. +var configPath string // NewIgnitedCommand returns the root command for ignited func NewIgnitedCommand(in io.Reader, out, err io.Writer) *cobra.Command { @@ -29,6 +33,10 @@ func NewIgnitedCommand(in io.Reader, out, err io.Writer) *cobra.Command { // Set the desired logging level, now that the flags are parsed logs.Logger.SetLevel(logLevel) + if err := config.ApplyConfiguration(configPath); err != nil { + log.Fatal(err) + } + // Populate the providers after flags have been parsed if err := providers.Populate(ignite.Providers); err != nil { log.Fatal(err) @@ -55,4 +63,5 @@ func addGlobalFlags(fs *pflag.FlagSet) { logflag.LogLevelFlagVar(fs, &logLevel) runtimeflag.RuntimeVar(fs, &providers.RuntimeName) networkflag.NetworkPluginVar(fs, &providers.NetworkPluginName) + fs.StringVar(&configPath, "ignite-config", "", "Ignite configuration path; refer to the 'Ignite Configuration' docs for more details") } diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 000000000..b66f593dc --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,81 @@ +package config + +import ( + "fmt" + "os" + + log "github.com/sirupsen/logrus" + + api "github.com/weaveworks/ignite/pkg/apis/ignite" + "github.com/weaveworks/ignite/pkg/apis/ignite/scheme" + "github.com/weaveworks/ignite/pkg/constants" + "github.com/weaveworks/ignite/pkg/network" + "github.com/weaveworks/ignite/pkg/providers" + "github.com/weaveworks/ignite/pkg/runtime" +) + +// ApplyConfiguration merges the given configurations with the default ignite +// configurations. +func ApplyConfiguration(configPath string) error { + var configFilePath string + + if configPath != "" { + configFilePath = configPath + } else { + // Check the default config location. + if _, err := os.Stat(constants.IGNITE_CONFIG_FILE); !os.IsNotExist(err) { + log.Debugf("Found default ignite configuration file %s", constants.IGNITE_CONFIG_FILE) + configFilePath = constants.IGNITE_CONFIG_FILE + } + } + + if configFilePath != "" { + log.Debugf("Using ignite configuration file %s", configFilePath) + var err error + providers.ComponentConfig, err = getConfigFromFile(configFilePath) + if err != nil { + return err + } + + // Set providers runtime and network plugin if found in config + // and not set explicitly via flags. + if providers.ComponentConfig.Spec.Runtime != "" && providers.RuntimeName == "" { + providers.RuntimeName = providers.ComponentConfig.Spec.Runtime + } + if providers.ComponentConfig.Spec.NetworkPlugin != "" && providers.NetworkPluginName == "" { + providers.NetworkPluginName = providers.ComponentConfig.Spec.NetworkPlugin + } + } else { + log.Debugln("Using ignite default configurations") + } + + // Set the default runtime and network-plugin if it's not set by + // now. + if providers.RuntimeName == "" { + providers.RuntimeName = runtime.RuntimeContainerd + } + if providers.NetworkPluginName == "" { + providers.NetworkPluginName = network.PluginCNI + } + + return nil +} + +// getConfigFromFile reads a config file and returns ignite configuration. +func getConfigFromFile(configPath string) (*api.Configuration, error) { + componentConfig := &api.Configuration{} + + // TODO: Fix libgitops DecodeFileInto to not allow empty files. + if err := scheme.Serializer.DecodeFileInto(configPath, componentConfig); err != nil { + return nil, err + } + + // Ensure the read configuration is valid. If a file contains Kind and + // APIVersion, it's a valid config file. Empty file is invalid. + // NOTE: This is a workaround for libgitops allowing decode of empty file. + if componentConfig.Kind == "" || componentConfig.APIVersion == "" { + return nil, fmt.Errorf("invalid config file, Kind and APIVersion must be set") + } + + return componentConfig, nil +} From 08e78d553dc43c985dacff7bb9ccff52dc059438 Mon Sep 17 00:00:00 2001 From: Sunny Date: Mon, 10 Aug 2020 20:20:18 +0530 Subject: [PATCH 2/3] gitops docs: Using a local git repo for testing --- docs/gitops.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/docs/gitops.md b/docs/gitops.md index 24728c972..0c3ba64cd 100644 --- a/docs/gitops.md +++ b/docs/gitops.md @@ -47,6 +47,8 @@ status: running: true ``` +**NOTE:** `uid` must be set. VM configurations without uid are ignored. + For a more complete example repository configuration, see [luxas/ignite-gitops](https://github.com/luxas/ignite-gitops) After you have [installed Ignite](installation.md), you can do the following: @@ -61,4 +63,55 @@ you need to set up SSH authentication and use the SSH clone URL for now. Ignite will now search that repo for suitable JSON/YAML files, and apply their state locally. You should see `my-vm` starting up in `ignite ps`. To enter the VM, run `ignite ssh my-vm`. +### Using a local git repo for testing + +Create a new directory and initialize it as a bare git repo. For ignited to +push updates to the git repo, the repo must be created as a bare git repo. + +```console +$ mkdir ~/ignite-gitops +$ cd ~/ignite-gitops +$ git init --bare +$ ls +HEAD branches config description hooks info objects refs +``` + +Clone this git repo and add the sample VM configuration: + +```console +$ git clone file:///home/user/ignite-gitops ignite-gitops-clone +Cloning into 'ignite-gitops-clone'... +warning: You appear to have cloned an empty repository. +$ cd ignite-gitops-clone +$ git remote -v +origin file:///home/user/ignite-gitops (fetch) +origin file:///home/user/ignite-gitops (push) +$ # Create my-vm.yaml, commit and push. +``` + +Run ignited against the bare git repo: + +```console +$ sudo ignited gitops file:///home/user/ignite-gitops +INFO[0000] Starting GitOps loop for repo at "file:///home/user/ignite-gitops" +INFO[0000] Whenever changes are pushed to the target branch, Ignite will apply the desired state locally +INFO[0000] Initializing the Git repo... +INFO[0000] Running in read-write mode, will commit back current status to the repo +INFO[0000] Starting the commit loop... +INFO[0000] Starting the checkout loop... +INFO[0000] Starting to clone the repository file:///home/user/ignite-gitops with timeout 1m0s +INFO[0000] New commit observed on branch "master": 7095d3603649792f44110cc5cf7deb0ded897e4b. User initiated: true +INFO[0003] Creating VM "599615df99804ae8" with name "my-vm"... +INFO[0004] Starting VM "599615df99804ae8" with name "my-vm"... +INFO[0004] Networking is handled by "cni" +INFO[0004] Started Firecracker VM "599615df99804ae8" in a container with ID "ignite-599615df99804ae8" +... +INFO[0030] A new commit with the actual state has been created and pushed to the origin: "186a8eb6eced5843480d4a10872db071ecc76439" +INFO[0030] New commit observed on branch "master": 186a8eb6eced5843480d4a10872db071ecc76439. User initiated: false +... +``` + +**NOTE:** Root permission may be required to push updates to the main repo +because ignited pushes git updates as root. + Please refer to [docs/declarative-config.md](declarative-config.md) for the full API reference. From 5403d535b9417a9b095a38efc7ad6c03fbf6ca8b Mon Sep 17 00:00:00 2001 From: Sunny Date: Mon, 10 Aug 2020 20:39:02 +0530 Subject: [PATCH 3/3] Update docs and deps --- docs/cli/ignited/ignited.md | 1 + docs/cli/ignited/ignited_completion.md | 1 + docs/cli/ignited/ignited_daemon.md | 1 + docs/cli/ignited/ignited_gitops.md | 1 + docs/cli/ignited/ignited_version.md | 1 + 5 files changed, 5 insertions(+) diff --git a/docs/cli/ignited/ignited.md b/docs/cli/ignited/ignited.md index 9d4641632..d6294cefd 100644 --- a/docs/cli/ignited/ignited.md +++ b/docs/cli/ignited/ignited.md @@ -15,6 +15,7 @@ TODO: ignited documentation ``` -h, --help help for ignited + --ignite-config string Ignite configuration path; refer to the 'Ignite Configuration' docs for more details --log-level loglevel Specify the loglevel for the program (default info) --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) diff --git a/docs/cli/ignited/ignited_completion.md b/docs/cli/ignited/ignited_completion.md index b7aa6df87..6eb2cd968 100644 --- a/docs/cli/ignited/ignited_completion.md +++ b/docs/cli/ignited/ignited_completion.md @@ -27,6 +27,7 @@ ignited completion [flags] ### Options inherited from parent commands ``` + --ignite-config string Ignite configuration path; refer to the 'Ignite Configuration' docs for more details --log-level loglevel Specify the loglevel for the program (default info) --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) diff --git a/docs/cli/ignited/ignited_daemon.md b/docs/cli/ignited/ignited_daemon.md index 4bce981ff..652d6d395 100644 --- a/docs/cli/ignited/ignited_daemon.md +++ b/docs/cli/ignited/ignited_daemon.md @@ -19,6 +19,7 @@ ignited daemon [flags] ### Options inherited from parent commands ``` + --ignite-config string Ignite configuration path; refer to the 'Ignite Configuration' docs for more details --log-level loglevel Specify the loglevel for the program (default info) --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) diff --git a/docs/cli/ignited/ignited_gitops.md b/docs/cli/ignited/ignited_gitops.md index 6f4886699..901c5f038 100644 --- a/docs/cli/ignited/ignited_gitops.md +++ b/docs/cli/ignited/ignited_gitops.md @@ -34,6 +34,7 @@ ignited gitops [flags] ### Options inherited from parent commands ``` + --ignite-config string Ignite configuration path; refer to the 'Ignite Configuration' docs for more details --log-level loglevel Specify the loglevel for the program (default info) --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) diff --git a/docs/cli/ignited/ignited_version.md b/docs/cli/ignited/ignited_version.md index fd4a73bb4..23a6ef41a 100644 --- a/docs/cli/ignited/ignited_version.md +++ b/docs/cli/ignited/ignited_version.md @@ -20,6 +20,7 @@ ignited version [flags] ### Options inherited from parent commands ``` + --ignite-config string Ignite configuration path; refer to the 'Ignite Configuration' docs for more details --log-level loglevel Specify the loglevel for the program (default info) --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd)