From 8612708f516e62e4e52bd2d8aee6fc5d815546b3 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 23 May 2021 05:42:57 +0530 Subject: [PATCH 1/8] Add client-config-dir configuration - Adds a --client-config-dir flag in VM create, image import and kernel import subcommands. - Adds field clientConfigDir in ignite configuration. - Update v1alpha4 API and add custom conversion function for v1alpha3. --- cmd/ignite/cmd/cmdutil/flags.go | 4 ++++ cmd/ignite/cmd/cmdutil/providers.go | 22 +++++++++++++++++++ cmd/ignite/cmd/imgcmd/import.go | 1 + cmd/ignite/cmd/kerncmd/import.go | 1 + cmd/ignite/cmd/vmcmd/create.go | 1 + cmd/ignite/run/create.go | 4 ++++ cmd/ignite/run/import.go | 5 +++++ pkg/apis/ignite/types.go | 9 ++++---- pkg/apis/ignite/v1alpha3/conversion.go | 11 ++++++++++ .../v1alpha3/zz_generated.conversion.go | 16 +++++--------- pkg/apis/ignite/v1alpha4/types.go | 9 ++++---- .../v1alpha4/zz_generated.conversion.go | 2 ++ pkg/openapi/openapi_generated.go | 6 +++++ pkg/providers/providers.go | 5 +++++ pkg/runtime/auth/auth.go | 6 +++-- pkg/runtime/containerd/client.go | 7 +++--- pkg/runtime/docker/client.go | 3 ++- 17 files changed, 88 insertions(+), 24 deletions(-) create mode 100644 cmd/ignite/cmd/cmdutil/providers.go create mode 100644 pkg/apis/ignite/v1alpha3/conversion.go diff --git a/cmd/ignite/cmd/cmdutil/flags.go b/cmd/ignite/cmd/cmdutil/flags.go index d31712cc8..6b093d782 100644 --- a/cmd/ignite/cmd/cmdutil/flags.go +++ b/cmd/ignite/cmd/cmdutil/flags.go @@ -35,3 +35,7 @@ func AddSSHFlags(fs *pflag.FlagSet, identityFile *string, timeout *uint32) { fs.StringVarP(identityFile, "identity", "i", "", "Override the vm's default identity file") fs.Uint32Var(timeout, "timeout", constants.SSH_DEFAULT_TIMEOUT_SECONDS, "Timeout waiting for connection in seconds") } + +func AddClientConfigDirFlag(fs *pflag.FlagSet, dir *string) { + fs.StringVar(dir, "client-config-dir", "", "Directory containing the client configuration (default ~/.docker/)") +} diff --git a/cmd/ignite/cmd/cmdutil/providers.go b/cmd/ignite/cmd/cmdutil/providers.go new file mode 100644 index 000000000..180f3a7aa --- /dev/null +++ b/cmd/ignite/cmd/cmdutil/providers.go @@ -0,0 +1,22 @@ +package cmdutil + +import ( + log "github.com/sirupsen/logrus" + + "github.com/weaveworks/ignite/pkg/providers" +) + +// ResolveClientConfigDir reads various configuration to resolve the client +// configuration directory. +func ResolveClientConfigDir() { + if providers.ComponentConfig != nil { + // Set the providers client config dir from ignite configuration if + // it's empty. When it's set in the providers and in the ignite + // configuration, log about the override. + if providers.ClientConfigDir == "" { + providers.ClientConfigDir = providers.ComponentConfig.Spec.ClientConfigDir + } else if providers.ComponentConfig.Spec.ClientConfigDir != "" { + log.Debug("client-config-dir flag overriding the ignite configuration") + } + } +} diff --git a/cmd/ignite/cmd/imgcmd/import.go b/cmd/ignite/cmd/imgcmd/import.go index 66edbcf42..0c631e910 100644 --- a/cmd/ignite/cmd/imgcmd/import.go +++ b/cmd/ignite/cmd/imgcmd/import.go @@ -38,4 +38,5 @@ func NewCmdImport(out io.Writer) *cobra.Command { func addImportFlags(fs *pflag.FlagSet) { runtimeflag.RuntimeVar(fs, &providers.RuntimeName) + cmdutil.AddClientConfigDirFlag(fs, &providers.ClientConfigDir) } diff --git a/cmd/ignite/cmd/kerncmd/import.go b/cmd/ignite/cmd/kerncmd/import.go index 2ad3af111..36a05fe01 100644 --- a/cmd/ignite/cmd/kerncmd/import.go +++ b/cmd/ignite/cmd/kerncmd/import.go @@ -38,4 +38,5 @@ func NewCmdImport(out io.Writer) *cobra.Command { func addImportFlags(fs *pflag.FlagSet) { runtimeflag.RuntimeVar(fs, &providers.RuntimeName) + cmdutil.AddClientConfigDirFlag(fs, &providers.ClientConfigDir) } diff --git a/cmd/ignite/cmd/vmcmd/create.go b/cmd/ignite/cmd/vmcmd/create.go index 18146424f..ed088a8b6 100644 --- a/cmd/ignite/cmd/vmcmd/create.go +++ b/cmd/ignite/cmd/vmcmd/create.go @@ -88,4 +88,5 @@ func addCreateFlags(fs *pflag.FlagSet, cf *run.CreateFlags) { runtimeflag.RuntimeVar(fs, &providers.RuntimeName) networkflag.NetworkPluginVar(fs, &providers.NetworkPluginName) cmdutil.AddIDPrefixFlag(fs, &providers.IDPrefix) + cmdutil.AddClientConfigDirFlag(fs, &providers.ClientConfigDir) } diff --git a/cmd/ignite/run/create.go b/cmd/ignite/run/create.go index 9e450ed99..723b5b486 100644 --- a/cmd/ignite/run/create.go +++ b/cmd/ignite/run/create.go @@ -6,6 +6,7 @@ import ( "path" "strings" + "github.com/weaveworks/ignite/cmd/ignite/cmd/cmdutil" api "github.com/weaveworks/ignite/pkg/apis/ignite" "github.com/weaveworks/ignite/pkg/apis/ignite/scheme" "github.com/weaveworks/ignite/pkg/apis/ignite/validation" @@ -58,6 +59,9 @@ func (cf *CreateFlags) NewCreateOptions(args []string, fs *flag.FlagSet) (*Creat baseVM.Spec = providers.ComponentConfig.Spec.VMDefaults } + // Resolve client configuration used for pulling image if required. + cmdutil.ResolveClientConfigDir() + // Initialize the VM's Prefixer baseVM.Status.IDPrefix = providers.IDPrefix // Set the runtime and network-plugin on the VM, then override the global config. diff --git a/cmd/ignite/run/import.go b/cmd/ignite/run/import.go index b9e415606..9d88dfb56 100644 --- a/cmd/ignite/run/import.go +++ b/cmd/ignite/run/import.go @@ -1,6 +1,7 @@ package run import ( + "github.com/weaveworks/ignite/cmd/ignite/cmd/cmdutil" api "github.com/weaveworks/ignite/pkg/apis/ignite" meta "github.com/weaveworks/ignite/pkg/apis/meta/v1alpha1" "github.com/weaveworks/ignite/pkg/config" @@ -16,6 +17,8 @@ func ImportImage(source string) (image *api.Image, err error) { return nil, err } + cmdutil.ResolveClientConfigDir() + ociRef, err := meta.NewOCIImageRef(source) if err != nil { return @@ -38,6 +41,8 @@ func ImportKernel(source string) (kernel *api.Kernel, err error) { return nil, err } + cmdutil.ResolveClientConfigDir() + ociRef, err := meta.NewOCIImageRef(source) if err != nil { return diff --git a/pkg/apis/ignite/types.go b/pkg/apis/ignite/types.go index d595ce14b..9f898eeac 100644 --- a/pkg/apis/ignite/types.go +++ b/pkg/apis/ignite/types.go @@ -259,8 +259,9 @@ type Configuration struct { // ConfigurationSpec defines the ignite configuration. type ConfigurationSpec struct { - Runtime igniteRuntime.Name `json:"runtime,omitempty"` - NetworkPlugin igniteNetwork.PluginName `json:"networkPlugin,omitempty"` - VMDefaults VMSpec `json:"vmDefaults,omitempty"` - IDPrefix string `json:"idPrefix,omitempty"` + Runtime igniteRuntime.Name `json:"runtime,omitempty"` + NetworkPlugin igniteNetwork.PluginName `json:"networkPlugin,omitempty"` + VMDefaults VMSpec `json:"vmDefaults,omitempty"` + IDPrefix string `json:"idPrefix,omitempty"` + ClientConfigDir string `json:"clientConfigDir,omitempty"` } diff --git a/pkg/apis/ignite/v1alpha3/conversion.go b/pkg/apis/ignite/v1alpha3/conversion.go new file mode 100644 index 000000000..1843142c8 --- /dev/null +++ b/pkg/apis/ignite/v1alpha3/conversion.go @@ -0,0 +1,11 @@ +package v1alpha3 + +import ( + "github.com/weaveworks/ignite/pkg/apis/ignite" + "k8s.io/apimachinery/pkg/conversion" +) + +// Convert_ignite_ConfigurationSpec_To_v1alpha3_ConfigurationSpec calls the autogenerated conversion function along with custom conversion logic +func Convert_ignite_ConfigurationSpec_To_v1alpha3_ConfigurationSpec(in *ignite.ConfigurationSpec, out *ConfigurationSpec, s conversion.Scope) error { + return autoConvert_ignite_ConfigurationSpec_To_v1alpha3_ConfigurationSpec(in, out, s) +} diff --git a/pkg/apis/ignite/v1alpha3/zz_generated.conversion.go b/pkg/apis/ignite/v1alpha3/zz_generated.conversion.go index 552fce87b..d9398492e 100644 --- a/pkg/apis/ignite/v1alpha3/zz_generated.conversion.go +++ b/pkg/apis/ignite/v1alpha3/zz_generated.conversion.go @@ -48,11 +48,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*ignite.ConfigurationSpec)(nil), (*ConfigurationSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_ignite_ConfigurationSpec_To_v1alpha3_ConfigurationSpec(a.(*ignite.ConfigurationSpec), b.(*ConfigurationSpec), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*FileMapping)(nil), (*ignite.FileMapping)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha3_FileMapping_To_ignite_FileMapping(a.(*FileMapping), b.(*ignite.FileMapping), scope) }); err != nil { @@ -303,6 +298,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*ignite.ConfigurationSpec)(nil), (*ConfigurationSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_ignite_ConfigurationSpec_To_v1alpha3_ConfigurationSpec(a.(*ignite.ConfigurationSpec), b.(*ConfigurationSpec), scope) + }); err != nil { + return err + } return nil } @@ -376,14 +376,10 @@ func autoConvert_ignite_ConfigurationSpec_To_v1alpha3_ConfigurationSpec(in *igni return err } out.IDPrefix = in.IDPrefix + // WARNING: in.ClientConfigDir requires manual conversion: does not exist in peer-type return nil } -// Convert_ignite_ConfigurationSpec_To_v1alpha3_ConfigurationSpec is an autogenerated conversion function. -func Convert_ignite_ConfigurationSpec_To_v1alpha3_ConfigurationSpec(in *ignite.ConfigurationSpec, out *ConfigurationSpec, s conversion.Scope) error { - return autoConvert_ignite_ConfigurationSpec_To_v1alpha3_ConfigurationSpec(in, out, s) -} - func autoConvert_v1alpha3_FileMapping_To_ignite_FileMapping(in *FileMapping, out *ignite.FileMapping, s conversion.Scope) error { out.HostPath = in.HostPath out.VMPath = in.VMPath diff --git a/pkg/apis/ignite/v1alpha4/types.go b/pkg/apis/ignite/v1alpha4/types.go index f625a3c46..2ced76c6f 100644 --- a/pkg/apis/ignite/v1alpha4/types.go +++ b/pkg/apis/ignite/v1alpha4/types.go @@ -259,8 +259,9 @@ type Configuration struct { // ConfigurationSpec defines the ignite configuration. type ConfigurationSpec struct { - Runtime igniteRuntime.Name `json:"runtime,omitempty"` - NetworkPlugin igniteNetwork.PluginName `json:"networkPlugin,omitempty"` - VMDefaults VMSpec `json:"vmDefaults,omitempty"` - IDPrefix string `json:"idPrefix,omitempty"` + Runtime igniteRuntime.Name `json:"runtime,omitempty"` + NetworkPlugin igniteNetwork.PluginName `json:"networkPlugin,omitempty"` + VMDefaults VMSpec `json:"vmDefaults,omitempty"` + IDPrefix string `json:"idPrefix,omitempty"` + ClientConfigDir string `json:"clientConfigDir,omitempty"` } diff --git a/pkg/apis/ignite/v1alpha4/zz_generated.conversion.go b/pkg/apis/ignite/v1alpha4/zz_generated.conversion.go index f463c204c..4adb5fff0 100644 --- a/pkg/apis/ignite/v1alpha4/zz_generated.conversion.go +++ b/pkg/apis/ignite/v1alpha4/zz_generated.conversion.go @@ -361,6 +361,7 @@ func autoConvert_v1alpha4_ConfigurationSpec_To_ignite_ConfigurationSpec(in *Conf return err } out.IDPrefix = in.IDPrefix + out.ClientConfigDir = in.ClientConfigDir return nil } @@ -376,6 +377,7 @@ func autoConvert_ignite_ConfigurationSpec_To_v1alpha4_ConfigurationSpec(in *igni return err } out.IDPrefix = in.IDPrefix + out.ClientConfigDir = in.ClientConfigDir return nil } diff --git a/pkg/openapi/openapi_generated.go b/pkg/openapi/openapi_generated.go index 8a376bbb5..fd6fed19d 100644 --- a/pkg/openapi/openapi_generated.go +++ b/pkg/openapi/openapi_generated.go @@ -1955,6 +1955,12 @@ func schema_pkg_apis_ignite_v1alpha4_ConfigurationSpec(ref common.ReferenceCallb Format: "", }, }, + "clientConfigDir": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, }, }, }, diff --git a/pkg/providers/providers.go b/pkg/providers/providers.go index afe61e71f..9bc2fbba8 100644 --- a/pkg/providers/providers.go +++ b/pkg/providers/providers.go @@ -37,6 +37,11 @@ var Storage storage.Storage var ComponentConfig *api.Configuration +// ClientConfigDir is the container runtime client configuration directory. +// This is used during operations like image import for loading registry +// configurations. +var ClientConfigDir string + type ProviderInitFunc func() error // Populate initializes all given providers diff --git a/pkg/runtime/auth/auth.go b/pkg/runtime/auth/auth.go index cca1a9f10..eef0e53e8 100644 --- a/pkg/runtime/auth/auth.go +++ b/pkg/runtime/auth/auth.go @@ -18,9 +18,11 @@ type AuthCreds func(string) (string, string, error) // NewAuthCreds returns an AuthCreds which loads the credentials from the // docker client config. -func NewAuthCreds(refHostname string) (AuthCreds, error) { +func NewAuthCreds(refHostname string, configPath string) (AuthCreds, error) { + log.Debugf("runtime.auth: client config dir path: %q", configPath) + // Load does not raise an error on ENOENT - dockerConfigFile, err := dockercliconfig.Load("") + dockerConfigFile, err := dockercliconfig.Load(configPath) if err != nil { return nil, err } diff --git a/pkg/runtime/containerd/client.go b/pkg/runtime/containerd/client.go index 4293578d9..1380e7ae7 100644 --- a/pkg/runtime/containerd/client.go +++ b/pkg/runtime/containerd/client.go @@ -40,6 +40,7 @@ import ( imagespec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/runtime-spec/specs-go" log "github.com/sirupsen/logrus" + "github.com/weaveworks/ignite/pkg/providers" "golang.org/x/sys/unix" ) @@ -144,9 +145,9 @@ func GetContainerdClient() (*ctdClient, error) { // newRemoteResolver returns a remote resolver with auth info for a given // host name. -func newRemoteResolver(refHostname string) (remotes.Resolver, error) { +func newRemoteResolver(refHostname string, configPath string) (remotes.Resolver, error) { var authzOpts []docker.AuthorizerOpt - if authCreds, err := auth.NewAuthCreds(refHostname); err != nil { + if authCreds, err := auth.NewAuthCreds(refHostname, configPath); err != nil { return nil, err } else { authzOpts = append(authzOpts, docker.WithAuthCreds(authCreds)) @@ -178,7 +179,7 @@ func (cc *ctdClient) PullImage(image meta.OCIImageRef) error { refDomain := refdocker.Domain(named) // Create a remote resolver for the domain. - resolver, err := newRemoteResolver(refDomain) + resolver, err := newRemoteResolver(refDomain, providers.ClientConfigDir) if err != nil { return err } diff --git a/pkg/runtime/docker/client.go b/pkg/runtime/docker/client.go index 5fc7fbee4..473d81a43 100644 --- a/pkg/runtime/docker/client.go +++ b/pkg/runtime/docker/client.go @@ -21,6 +21,7 @@ import ( meta "github.com/weaveworks/ignite/pkg/apis/meta/v1alpha1" "github.com/weaveworks/ignite/pkg/preflight" "github.com/weaveworks/ignite/pkg/preflight/checkers" + "github.com/weaveworks/ignite/pkg/providers" "github.com/weaveworks/ignite/pkg/runtime" "github.com/weaveworks/ignite/pkg/runtime/auth" "github.com/weaveworks/ignite/pkg/util" @@ -68,7 +69,7 @@ func (dc *dockerClient) PullImage(image meta.OCIImageRef) (err error) { } // Get available credentials from docker cli config. - authCreds, err := auth.NewAuthCreds(refDomain) + authCreds, err := auth.NewAuthCreds(refDomain, providers.ClientConfigDir) if err != nil { return err } From 45ee9197e01aad0556ef07b7160b633d49ad7b4c Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 23 May 2021 05:45:56 +0530 Subject: [PATCH 2/8] Update docs with client-config-dir --- docs/api/ignite_v1alpha3.md | 16 +++++++++++ docs/api/ignite_v1alpha4.md | 11 ++++---- docs/cli/ignite/ignite_create.md | 37 +++++++++++++------------ docs/cli/ignite/ignite_image_import.md | 5 ++-- docs/cli/ignite/ignite_kernel_import.md | 5 ++-- docs/cli/ignite/ignite_run.md | 1 + docs/cli/ignite/ignite_vm_create.md | 37 +++++++++++++------------ docs/cli/ignite/ignite_vm_run.md | 1 + 8 files changed, 68 insertions(+), 45 deletions(-) diff --git a/docs/api/ignite_v1alpha3.md b/docs/api/ignite_v1alpha3.md index 313351b03..d54ed22d4 100644 --- a/docs/api/ignite_v1alpha3.md +++ b/docs/api/ignite_v1alpha3.md @@ -15,6 +15,11 @@ - [Constants](#pkg-constants) - [Variables](#pkg-variables) + - [func + Convert\_ignite\_ConfigurationSpec\_To\_v1alpha3\_ConfigurationSpec(in + *ignite.ConfigurationSpec, out *ConfigurationSpec, s + conversion.Scope) + error](#Convert_ignite_ConfigurationSpec_To_v1alpha3_ConfigurationSpec) - [func SetDefaults\_ConfigurationSpec(obj \*ConfigurationSpec)](#SetDefaults_ConfigurationSpec) - [func SetDefaults\_PoolSpec(obj \*PoolSpec)](#SetDefaults_PoolSpec) @@ -60,6 +65,7 @@ #### Package files +[conversion.go](https://github.com/weaveworks/ignite/tree/main/pkg/apis/ignite/v1alpha3/conversion.go) [defaults.go](https://github.com/weaveworks/ignite/tree/main/pkg/apis/ignite/v1alpha3/defaults.go) [doc.go](https://github.com/weaveworks/ignite/tree/main/pkg/apis/ignite/v1alpha3/doc.go) [json.go](https://github.com/weaveworks/ignite/tree/main/pkg/apis/ignite/v1alpha3/json.go) @@ -106,6 +112,16 @@ var SchemeGroupVersion = schema.GroupVersion{ SchemeGroupVersion is group version used to register these objects +## func [Convert\_ignite\_ConfigurationSpec\_To\_v1alpha3\_ConfigurationSpec](https://github.com/weaveworks/ignite/tree/main/pkg/apis/ignite/v1alpha3/conversion.go?s=261:408#L9) + +``` go +func Convert_ignite_ConfigurationSpec_To_v1alpha3_ConfigurationSpec(in *ignite.ConfigurationSpec, out *ConfigurationSpec, s conversion.Scope) error +``` + +Convert\_ignite\_ConfigurationSpec\_To\_v1alpha3\_ConfigurationSpec +calls the autogenerated conversion function along with custom conversion +logic + ## func [SetDefaults\_ConfigurationSpec](https://github.com/weaveworks/ignite/tree/main/pkg/apis/ignite/v1alpha3/defaults.go?s=1785:1843#L71) ``` go diff --git a/docs/api/ignite_v1alpha4.md b/docs/api/ignite_v1alpha4.md index 936c35d5f..e75eb64d4 100644 --- a/docs/api/ignite_v1alpha4.md +++ b/docs/api/ignite_v1alpha4.md @@ -166,14 +166,15 @@ type Configuration struct { Configuration represents the ignite runtime configuration. +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -## type [ConfigurationSpec](https://github.com/weaveworks/ignite/tree/main/pkg/apis/ignite/v1alpha4/types.go?s=9475:9786#L261) +## type [ConfigurationSpec](https://github.com/weaveworks/ignite/tree/main/pkg/apis/ignite/v1alpha4/types.go?s=9475:9871#L261) ``` go type ConfigurationSpec struct { - Runtime igniteRuntime.Name `json:"runtime,omitempty"` - NetworkPlugin igniteNetwork.PluginName `json:"networkPlugin,omitempty"` - VMDefaults VMSpec `json:"vmDefaults,omitempty"` - IDPrefix string `json:"idPrefix,omitempty"` + Runtime igniteRuntime.Name `json:"runtime,omitempty"` + NetworkPlugin igniteNetwork.PluginName `json:"networkPlugin,omitempty"` + VMDefaults VMSpec `json:"vmDefaults,omitempty"` + IDPrefix string `json:"idPrefix,omitempty"` + ClientConfigDir string `json:"clientConfigDir,omitempty"` } ``` diff --git a/docs/cli/ignite/ignite_create.md b/docs/cli/ignite/ignite_create.md index 9e5f75d2a..ae5fd9e1d 100644 --- a/docs/cli/ignite/ignite_create.md +++ b/docs/cli/ignite/ignite_create.md @@ -34,24 +34,25 @@ ignite create [flags] ### Options ``` - --config string Specify a path to a file with the API resources you want to pass - -f, --copy-files strings Copy files/directories from the host to the created VM - --cpus uint VM vCPU count, 1 or even numbers between 1 and 32 (default 1) - -h, --help help for create - --id-prefix string Prefix string for system identifiers (default ignite) - --kernel-args string Set the command line for the kernel (default "console=ttyS0 reboot=k panic=1 pci=off ip=dhcp") - -k, --kernel-image oci-image Specify an OCI image containing the kernel at /boot/vmlinux and optionally, modules (default weaveworks/ignite-kernel:5.4.108) - -l, --label stringArray Set a label (foo=bar) - --memory size Amount of RAM to allocate for the VM (default 512.0 MB) - -n, --name string Specify the name - --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) - -p, --ports strings Map host ports to VM ports - --require-name Require VM name to be passed, no name generation - --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) - --sandbox-image oci-image Specify an OCI image for the VM sandbox (default weaveworks/ignite:dev) - -s, --size size VM filesystem size, for example 5GB or 2048MB (default 4.0 GB) - --ssh[=] Enable SSH for the VM. If is given, it will be imported as the public key. If just '--ssh' is specified, a new keypair will be generated. (default is unset, which disables SSH access to the VM) - -v, --volumes volume Expose block devices from the host inside the VM + --client-config-dir string Directory containing the client configuration (default ~/.docker/) + --config string Specify a path to a file with the API resources you want to pass + -f, --copy-files strings Copy files/directories from the host to the created VM + --cpus uint VM vCPU count, 1 or even numbers between 1 and 32 (default 1) + -h, --help help for create + --id-prefix string Prefix string for system identifiers (default ignite) + --kernel-args string Set the command line for the kernel (default "console=ttyS0 reboot=k panic=1 pci=off ip=dhcp") + -k, --kernel-image oci-image Specify an OCI image containing the kernel at /boot/vmlinux and optionally, modules (default weaveworks/ignite-kernel:5.4.108) + -l, --label stringArray Set a label (foo=bar) + --memory size Amount of RAM to allocate for the VM (default 512.0 MB) + -n, --name string Specify the name + --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) + -p, --ports strings Map host ports to VM ports + --require-name Require VM name to be passed, no name generation + --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) + --sandbox-image oci-image Specify an OCI image for the VM sandbox (default weaveworks/ignite:dev) + -s, --size size VM filesystem size, for example 5GB or 2048MB (default 4.0 GB) + --ssh[=] Enable SSH for the VM. If is given, it will be imported as the public key. If just '--ssh' is specified, a new keypair will be generated. (default is unset, which disables SSH access to the VM) + -v, --volumes volume Expose block devices from the host inside the VM ``` ### Options inherited from parent commands diff --git a/docs/cli/ignite/ignite_image_import.md b/docs/cli/ignite/ignite_image_import.md index 749ba4384..330a1192e 100644 --- a/docs/cli/ignite/ignite_image_import.md +++ b/docs/cli/ignite/ignite_image_import.md @@ -17,8 +17,9 @@ ignite image import [flags] ### Options ``` - -h, --help help for import - --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) + --client-config-dir string Directory containing the client configuration (default ~/.docker/) + -h, --help help for import + --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) ``` ### Options inherited from parent commands diff --git a/docs/cli/ignite/ignite_kernel_import.md b/docs/cli/ignite/ignite_kernel_import.md index 74b879cd4..aaf40b2c5 100644 --- a/docs/cli/ignite/ignite_kernel_import.md +++ b/docs/cli/ignite/ignite_kernel_import.md @@ -17,8 +17,9 @@ ignite kernel import [flags] ### Options ``` - -h, --help help for import - --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) + --client-config-dir string Directory containing the client configuration (default ~/.docker/) + -h, --help help for import + --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) ``` ### Options inherited from parent commands diff --git a/docs/cli/ignite/ignite_run.md b/docs/cli/ignite/ignite_run.md index fd3f8edff..de3930600 100644 --- a/docs/cli/ignite/ignite_run.md +++ b/docs/cli/ignite/ignite_run.md @@ -27,6 +27,7 @@ ignite run [flags] ### Options ``` + --client-config-dir string Directory containing the client configuration (default ~/.docker/) --config string Specify a path to a file with the API resources you want to pass -f, --copy-files strings Copy files/directories from the host to the created VM --cpus uint VM vCPU count, 1 or even numbers between 1 and 32 (default 1) diff --git a/docs/cli/ignite/ignite_vm_create.md b/docs/cli/ignite/ignite_vm_create.md index ff1359d77..9f7108666 100644 --- a/docs/cli/ignite/ignite_vm_create.md +++ b/docs/cli/ignite/ignite_vm_create.md @@ -34,24 +34,25 @@ ignite vm create [flags] ### Options ``` - --config string Specify a path to a file with the API resources you want to pass - -f, --copy-files strings Copy files/directories from the host to the created VM - --cpus uint VM vCPU count, 1 or even numbers between 1 and 32 (default 1) - -h, --help help for create - --id-prefix string Prefix string for system identifiers (default ignite) - --kernel-args string Set the command line for the kernel (default "console=ttyS0 reboot=k panic=1 pci=off ip=dhcp") - -k, --kernel-image oci-image Specify an OCI image containing the kernel at /boot/vmlinux and optionally, modules (default weaveworks/ignite-kernel:5.4.108) - -l, --label stringArray Set a label (foo=bar) - --memory size Amount of RAM to allocate for the VM (default 512.0 MB) - -n, --name string Specify the name - --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) - -p, --ports strings Map host ports to VM ports - --require-name Require VM name to be passed, no name generation - --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) - --sandbox-image oci-image Specify an OCI image for the VM sandbox (default weaveworks/ignite:dev) - -s, --size size VM filesystem size, for example 5GB or 2048MB (default 4.0 GB) - --ssh[=] Enable SSH for the VM. If is given, it will be imported as the public key. If just '--ssh' is specified, a new keypair will be generated. (default is unset, which disables SSH access to the VM) - -v, --volumes volume Expose block devices from the host inside the VM + --client-config-dir string Directory containing the client configuration (default ~/.docker/) + --config string Specify a path to a file with the API resources you want to pass + -f, --copy-files strings Copy files/directories from the host to the created VM + --cpus uint VM vCPU count, 1 or even numbers between 1 and 32 (default 1) + -h, --help help for create + --id-prefix string Prefix string for system identifiers (default ignite) + --kernel-args string Set the command line for the kernel (default "console=ttyS0 reboot=k panic=1 pci=off ip=dhcp") + -k, --kernel-image oci-image Specify an OCI image containing the kernel at /boot/vmlinux and optionally, modules (default weaveworks/ignite-kernel:5.4.108) + -l, --label stringArray Set a label (foo=bar) + --memory size Amount of RAM to allocate for the VM (default 512.0 MB) + -n, --name string Specify the name + --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) + -p, --ports strings Map host ports to VM ports + --require-name Require VM name to be passed, no name generation + --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) + --sandbox-image oci-image Specify an OCI image for the VM sandbox (default weaveworks/ignite:dev) + -s, --size size VM filesystem size, for example 5GB or 2048MB (default 4.0 GB) + --ssh[=] Enable SSH for the VM. If is given, it will be imported as the public key. If just '--ssh' is specified, a new keypair will be generated. (default is unset, which disables SSH access to the VM) + -v, --volumes volume Expose block devices from the host inside the VM ``` ### Options inherited from parent commands diff --git a/docs/cli/ignite/ignite_vm_run.md b/docs/cli/ignite/ignite_vm_run.md index a9bdc0d7e..0a66cb23a 100644 --- a/docs/cli/ignite/ignite_vm_run.md +++ b/docs/cli/ignite/ignite_vm_run.md @@ -27,6 +27,7 @@ ignite vm run [flags] ### Options ``` + --client-config-dir string Directory containing the client configuration (default ~/.docker/) --config string Specify a path to a file with the API resources you want to pass -f, --copy-files strings Copy files/directories from the host to the created VM --cpus uint VM vCPU count, 1 or even numbers between 1 and 32 (default 1) From 12e0919a3151bcca61935df684821fad90842dc9 Mon Sep 17 00:00:00 2001 From: Sunny Date: Mon, 24 May 2021 05:26:55 +0530 Subject: [PATCH 3/8] Add e2e tests for registry authentication - Adds setup-private-registry.sh script to setup a local docker registry with self-signed cert and basic auth. - Adds test util to delete image from ignite and the runtime content store. - Adds e2e tests for image import with configurations set via flag and ignite configuration. - Enable docker registry setup only in semaphore CI for e2e tests. - Update go version in semaphore to 1.16.3 to use os.WriteFile(). --- .semaphore/semaphore.yml | 3 +- e2e/registry_auth_test.go | 201 +++++++++++++++++++++++++++++ e2e/util/image.go | 40 ++++++ e2e/util/setup-private-registry.sh | 57 ++++++++ pkg/runtime/containerd/client.go | 13 ++ 5 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 e2e/registry_auth_test.go create mode 100644 e2e/util/image.go create mode 100644 e2e/util/setup-private-registry.sh diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index afc94b6b7..65d8512e1 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -11,7 +11,7 @@ blocks: task: env_vars: - name: GIMME_GO_VERSION - value: "1.14.2" + value: "1.16.3" jobs: - name: Tests commands: @@ -32,4 +32,5 @@ blocks: - make ignite ignite-spawn ignited bin/amd64/Dockerfile GO_MAKE_TARGET=local - make test - make root-test + - bash e2e/util/setup-private-registry.sh # This is required for registry auth e2e tests. - make e2e-nobuild # this depends on Semaphore CI's support for nested virtualization diff --git a/e2e/registry_auth_test.go b/e2e/registry_auth_test.go new file mode 100644 index 000000000..046e8ee6d --- /dev/null +++ b/e2e/registry_auth_test.go @@ -0,0 +1,201 @@ +package e2e + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "gotest.tools/assert" + + "github.com/weaveworks/ignite/e2e/util" + "github.com/weaveworks/ignite/pkg/runtime" +) + +const ( + testOSImage = "localhost:5000/weaveworks/ignite-ubuntu:test" + testKernelImage = "localhost:5000/weaveworks/ignite-kernel:test" +) + +// client config with auth info for the registry setup in +// e2e/util/setup-private-registry.sh. +// NOTE: Update the auth token if the credentials in setup-private-registry.sh +// is updated. +const clientConfigContent = ` +{ + "auths": { + "localhost:5000": { + "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" + } + } +} +` + +func TestPullFromAuthRegistry(t *testing.T) { + assert.Assert(t, e2eHome != "", "IGNITE_E2E_HOME should be set") + + // Create a client config directory to use in test. + ccDir, err := ioutil.TempDir("", "ignite-test") + assert.NilError(t, err) + defer os.RemoveAll(ccDir) + + templateConfig := `--- +apiVersion: ignite.weave.works/v1alpha4 +kind: Configuration +metadata: + name: test-config +spec: + clientConfigDir: %s +` + igniteConfigContent := fmt.Sprintf(templateConfig, ccDir) + + cases := []struct { + name string + runtime runtime.Name + configWithAuthPath string + clientConfigFlag string + igniteConfig string + wantErr bool + }{ + { + name: "no auth info - containerd", + runtime: runtime.RuntimeContainerd, + wantErr: true, + }, + { + name: "no auth info - docker", + runtime: runtime.RuntimeDocker, + wantErr: true, + }, + { + name: "client config flag - containerd", + runtime: runtime.RuntimeContainerd, + configWithAuthPath: ccDir, + clientConfigFlag: ccDir, + }, + { + name: "client config flag - docker", + runtime: runtime.RuntimeDocker, + configWithAuthPath: ccDir, + clientConfigFlag: ccDir, + }, + { + name: "client config in ignite config - containerd", + runtime: runtime.RuntimeContainerd, + configWithAuthPath: ccDir, + igniteConfig: igniteConfigContent, + }, + { + name: "client config in ignite config - docker", + runtime: runtime.RuntimeDocker, + configWithAuthPath: ccDir, + igniteConfig: igniteConfigContent, + }, + // Following sets the client config dir to a location without a valid + // client config file, although the client config dir in the ignite + // config is correct, the import fails due to bad configuration by the + // flag override. + { + name: "flag override client config - containerd", + runtime: runtime.RuntimeContainerd, + configWithAuthPath: ccDir, + clientConfigFlag: "/tmp", + igniteConfig: igniteConfigContent, + wantErr: true, + }, + { + name: "flag override client config - docker", + runtime: runtime.RuntimeDocker, + configWithAuthPath: ccDir, + clientConfigFlag: "/tmp", + igniteConfig: igniteConfigContent, + wantErr: true, + }, + // Following set the client config dir via flag without any actual + // client config. Import fails due to missing auth info in the given + // client config dir. + { + name: "invalid client config - containerd", + runtime: runtime.RuntimeContainerd, + configWithAuthPath: "", + clientConfigFlag: ccDir, + wantErr: true, + }, + { + name: "invalid client config - docker", + runtime: runtime.RuntimeDocker, + configWithAuthPath: "", + clientConfigFlag: ccDir, + wantErr: true, + }, + } + + for _, rt := range cases { + rt := rt + t.Run(rt.name, func(t *testing.T) { + igniteCmd := util.NewCommand(t, igniteBin) + + // Remove images from ignite store and runtime store. Remove + // individually because an error in deleting one image cancels the + // whole command. + // TODO: Improve image rm to not fail completely when there are + // multiple images and some are not found. + util.RmiCompletely(testOSImage, igniteCmd, rt.runtime) + util.RmiCompletely(testKernelImage, igniteCmd, rt.runtime) + + // Write client config if given. + if len(rt.configWithAuthPath) > 0 { + // Ensure the directory exists and create a config file in the + // directory. + assert.NilError(t, os.MkdirAll(rt.configWithAuthPath, 0755)) + configPath := filepath.Join(rt.configWithAuthPath, "config.json") + assert.NilError(t, os.WriteFile(configPath, []byte(clientConfigContent), 0600)) + defer os.Remove(configPath) + } + + // Write ignite config if provided. + var igniteConfigPath string + if len(rt.igniteConfig) > 0 { + igniteFile, err := ioutil.TempFile("", "ignite-config-file-test") + if err != nil { + t.Fatalf("failed to create a file: %v", err) + } + igniteConfigPath = igniteFile.Name() + + _, err = igniteFile.WriteString(rt.igniteConfig) + assert.NilError(t, err) + assert.NilError(t, igniteFile.Close()) + + defer os.Remove(igniteFile.Name()) + } + + // Construct the ignite image import command. + imageImportCmdArgs := []string{"--runtime", rt.runtime.String()} + if len(rt.clientConfigFlag) > 0 { + imageImportCmdArgs = append(imageImportCmdArgs, "--client-config-dir", rt.clientConfigFlag) + } + if len(igniteConfigPath) > 0 { + imageImportCmdArgs = append(imageImportCmdArgs, "--ignite-config", igniteConfigPath) + } + + // Run image import. + _, importErr := igniteCmd.New(). + With("image", "import", testOSImage). + With(imageImportCmdArgs...). + Cmd.CombinedOutput() + if (importErr != nil) != rt.wantErr { + t.Error("expected OS image import to fail") + } + + // Run kernel import. + _, importErr = igniteCmd.New(). + With("image", "import", testKernelImage). + With(imageImportCmdArgs...). + Cmd.CombinedOutput() + if (importErr != nil) != rt.wantErr { + t.Error("expected kernel image import to fail") + } + }) + } +} diff --git a/e2e/util/image.go b/e2e/util/image.go new file mode 100644 index 000000000..e48f409a3 --- /dev/null +++ b/e2e/util/image.go @@ -0,0 +1,40 @@ +package util + +import ( + "os/exec" + + "github.com/weaveworks/ignite/pkg/runtime" +) + +// RmiDocker removes an image from docker content store. +func RmiDocker(img string) { + _, _ = exec.Command( + "docker", + "rmi", img, + ).CombinedOutput() +} + +// RmiContainerd removes an image from containerd content store. +func RmiContainerd(img string) { + _, _ = exec.Command( + "ctr", "-n", "firecracker", + "image", "rm", img, + ).CombinedOutput() +} + +// rmiCompletely removes a given image completely, from ignite image store and +// runtime image store. +func RmiCompletely(img string, cmd *Command, rt runtime.Name) { + // Remote from ignite content store. + _, _ = cmd.New(). + With("image", "rm", img). + Cmd.CombinedOutput() + + // Remove from runtime content store. + switch rt { + case runtime.RuntimeContainerd: + RmiContainerd(img) + case runtime.RuntimeDocker: + RmiDocker(img) + } +} diff --git a/e2e/util/setup-private-registry.sh b/e2e/util/setup-private-registry.sh new file mode 100644 index 000000000..7d1fcf13c --- /dev/null +++ b/e2e/util/setup-private-registry.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +set -e + +# This script runs a local private docker registry with self-signed certificate +# and basic auth. + +REGISTRY_SECRET_PATH=/tmp/ignite-test-registry +PRIVATE_KEY=${REGISTRY_SECRET_PATH}/certs/domain.key +CERT=${REGISTRY_SECRET_PATH}/certs/domain.crt +HTPASSWD=${REGISTRY_SECRET_PATH}/auth/htpasswd +USERNAME=testuser +PASSWORD=testpassword +REGISTRY_ADDRESS=https://localhost:5000 +OS_IMG=weaveworks/ignite-ubuntu:latest +LOCAL_OS_IMG=localhost:5000/weaveworks/ignite-ubuntu:test +KERNEL_IMG=weaveworks/ignite-kernel:5.4.108 +LOCAL_KERNEL_IMG=localhost:5000/weaveworks/ignite-kernel:test + +# Clear any existing registry secret and create new directories. +rm -rf ${REGISTRY_SECRET_PATH} +mkdir -p ${REGISTRY_SECRET_PATH}/{certs,auth} + +# Generate key and cert. +openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \ + -subj "/C=US/ST=Foo/L=Bar/O=Weave" \ + -keyout ${PRIVATE_KEY} -out ${CERT} +chmod 400 ${PRIVATE_KEY} + +# Create htpasswd file. +docker run --rm \ + --entrypoint htpasswd \ + httpd:2 -Bbn ${USERNAME} ${PASSWORD} > ${HTPASSWD} + +# Run the registry. +docker run -d --rm \ + --name registry \ + -v ${REGISTRY_SECRET_PATH}/auth:/auth \ + -v ${REGISTRY_SECRET_PATH}/certs:/certs \ + -e REGISTRY_AUTH=htpasswd \ + -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \ + -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ + -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \ + -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \ + -p 5000:5000 \ + registry:2 + +# Login, push test images to download in tests and logout. +docker login -u ${USERNAME} -p ${PASSWORD} ${REGISTRY_ADDRESS} +docker pull ${OS_IMG} +docker pull ${KERNEL_IMG} +docker tag ${OS_IMG} ${LOCAL_OS_IMG} +docker tag ${KERNEL_IMG} ${LOCAL_KERNEL_IMG} +docker push ${LOCAL_OS_IMG} +docker push ${LOCAL_KERNEL_IMG} +docker rmi ${LOCAL_OS_IMG} ${LOCAL_KERNEL_IMG} +docker logout ${REGISTRY_ADDRESS} diff --git a/pkg/runtime/containerd/client.go b/pkg/runtime/containerd/client.go index 1380e7ae7..2c48377d7 100644 --- a/pkg/runtime/containerd/client.go +++ b/pkg/runtime/containerd/client.go @@ -2,9 +2,11 @@ package containerd import ( "context" + "crypto/tls" "fmt" "io" "io/ioutil" + "net/http" "os" "os/exec" "path/filepath" @@ -159,6 +161,17 @@ func newRemoteResolver(refHostname string, configPath string) (remotes.Resolver, docker.WithAuthorizer(authz), } + // TODO: Make this opt-in via a flag option. + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + client := &http.Client{ + Transport: tr, + } + regOpts = append(regOpts, docker.WithClient(client)) + // TODO: Add option to skip verifying HTTPS cert. resolverOpts := docker.ResolverOptions{ Hosts: docker.ConfigureDefaultRegistries(regOpts...), From db2150d132a94b2f8eb2869b3af335194ed733ad Mon Sep 17 00:00:00 2001 From: leigh capili Date: Mon, 24 May 2021 14:58:46 -0600 Subject: [PATCH 4/8] Implement insecure registry behavior for containerd client WRT HTTP and TLS-ignore-verify /w tests :) Co-Authored-By: Sunny --- docs/usage.md | 28 +++++- e2e/registry_auth_test.go | 155 +++++++++++++++-------------- e2e/util/setup-private-registry.sh | 110 +++++++++++++------- pkg/runtime/auth/auth.go | 12 ++- pkg/runtime/containerd/client.go | 49 ++++++--- pkg/runtime/docker/client.go | 2 +- 6 files changed, 222 insertions(+), 134 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 93b8049a2..40a220b61 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -64,14 +64,17 @@ Now the `weaveworks/ignite-ubuntu` image is imported and ready for VM use. ### Configuring image registries -Ignite's runtime configuration for image registry uses the docker client -configuration. To add a new registry to docker client configuration, run +Ignite's runtime configuration for image registry uses the docker registry +configuration. To add a new registry to docker registry configuration, run `docker login `. This will create `$HOME/.docker/config.json` in the user's home directory. When ignite runs, it'll check the user's home -directory for docker client configuration file, load the registry configuration +directory for docker registry configuration file, load the registry configuration if found and use it. -An example of a docker client configuration file: +!!! Note + On many systems, running `sudo ignite` will set the `$HOME` directory to `/root`. + +An example of a docker registry configuration file: ```json @@ -80,6 +83,9 @@ An example of a docker client configuration file: "https://index.docker.io/v1/": { "auth": "" }, + "http://localhost:5000": { + "auth": "" + }, "gcr.io": { "auth": "" } @@ -92,12 +98,24 @@ the token is a base64 encoded value of `:`. For `gcr.io`, it's a [json key][json-key] file. Using docker [credential helpers][credential-helpers] also works but please ensure that the required credential helper program is installed to handle the credentials. If -the docker client configuration contains `"credHelpers"` block, but the +the docker registry configuration contains `"credHelpers"` block, but the associated helper program isn't installed or not configured properly, ignite image pull will fail with errors related to the specific credential helper. In presence of both auth tokens and credential helpers in a configuration file, credential helper takes precedence. +The `--registry-config-dir` flag can be used to override the default directory(`$HOME/.docker/config.json`). +This can also be done from the ignite [Configuration](./ignite-configuration). + +When using the `containerd` runtime to pull images, TLS verification can be disabled, +and `http://` protocols can be specified by using the client-side `IGNITE_CONTAINERD_INSECURE_REGISTRIES` +environment variable as a comma separate list. +In this list, the protocol is completely ignored, because it's specified by the registry-configuration: + +```shell +IGNITE_CONTAINERD_INSECURE_REGISTRIES="localhost:5000,localhost:5001,example.com,http://example.com" +``` + [json-key]: https://cloud.google.com/container-registry/docs/advanced-authentication#json-key [credential-helpers]: https://docs.docker.com/engine/reference/commandline/login/#credential-helpers diff --git a/e2e/registry_auth_test.go b/e2e/registry_auth_test.go index 046e8ee6d..a9b8b18d2 100644 --- a/e2e/registry_auth_test.go +++ b/e2e/registry_auth_test.go @@ -14,8 +14,10 @@ import ( ) const ( - testOSImage = "localhost:5000/weaveworks/ignite-ubuntu:test" - testKernelImage = "localhost:5000/weaveworks/ignite-kernel:test" + httpTestOSImage = "127.5.0.1:5080/weaveworks/ignite-ubuntu:test" + httpTestKernelImage = "127.5.0.1:5080/weaveworks/ignite-kernel:test" + httpsTestOSImage = "127.5.0.1:5443/weaveworks/ignite-ubuntu:test" + httpsTestKernelImage = "127.5.0.1:5443/weaveworks/ignite-kernel:test" ) // client config with auth info for the registry setup in @@ -24,22 +26,40 @@ const ( // is updated. const clientConfigContent = ` { - "auths": { - "localhost:5000": { - "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" - } - } + "auths": { + "http://127.5.0.1:5080": { + "auth": "aHR0cF90ZXN0dXNlcjpodHRwX3Rlc3RwYXNzd29yZA==" + }, + "https://127.5.0.1:5443": { + "auth": "aHR0cHNfdGVzdHVzZXI6aHR0cHNfdGVzdHBhc3N3b3Jk" + } + } } ` func TestPullFromAuthRegistry(t *testing.T) { assert.Assert(t, e2eHome != "", "IGNITE_E2E_HOME should be set") + os.Setenv("IGNITE_CONTAINERD_INSECURE_REGISTRIES", "http://127.5.0.1:5080,https://127.5.0.1:5443") + defer os.Unsetenv("IGNITE_CONTAINERD_INSECURE_REGISTRIES") + + // Create a client config directory to use in test. + emptyDir, err := ioutil.TempDir("", "ignite-test") + assert.NilError(t, err) + defer os.RemoveAll(emptyDir) + // Create a client config directory to use in test. ccDir, err := ioutil.TempDir("", "ignite-test") assert.NilError(t, err) defer os.RemoveAll(ccDir) + // Ensure the directory exists and create a config file in the + // directory. + assert.NilError(t, os.MkdirAll(ccDir, 0755)) + configPath := filepath.Join(ccDir, "config.json") + assert.NilError(t, os.WriteFile(configPath, []byte(clientConfigContent), 0600)) + defer os.Remove(configPath) + templateConfig := `--- apiVersion: ignite.weave.works/v1alpha4 kind: Configuration @@ -50,14 +70,14 @@ spec: ` igniteConfigContent := fmt.Sprintf(templateConfig, ccDir) - cases := []struct { - name string - runtime runtime.Name - configWithAuthPath string - clientConfigFlag string - igniteConfig string - wantErr bool - }{ + type testCase struct { + name string + runtime runtime.Name + clientConfigFlag string + igniteConfig string + wantErr bool + } + cases := []testCase{ { name: "no auth info - containerd", runtime: runtime.RuntimeContainerd, @@ -69,71 +89,62 @@ spec: wantErr: true, }, { - name: "client config flag - containerd", - runtime: runtime.RuntimeContainerd, - configWithAuthPath: ccDir, - clientConfigFlag: ccDir, + name: "client config flag - containerd", + runtime: runtime.RuntimeContainerd, + clientConfigFlag: ccDir, }, { - name: "client config flag - docker", - runtime: runtime.RuntimeDocker, - configWithAuthPath: ccDir, - clientConfigFlag: ccDir, + name: "client config flag - docker", + runtime: runtime.RuntimeDocker, + clientConfigFlag: ccDir, }, { - name: "client config in ignite config - containerd", - runtime: runtime.RuntimeContainerd, - configWithAuthPath: ccDir, - igniteConfig: igniteConfigContent, + name: "client config in ignite config - containerd", + runtime: runtime.RuntimeContainerd, + igniteConfig: igniteConfigContent, }, { - name: "client config in ignite config - docker", - runtime: runtime.RuntimeDocker, - configWithAuthPath: ccDir, - igniteConfig: igniteConfigContent, + name: "client config in ignite config - docker", + runtime: runtime.RuntimeDocker, + igniteConfig: igniteConfigContent, }, // Following sets the client config dir to a location without a valid // client config file, although the client config dir in the ignite // config is correct, the import fails due to bad configuration by the // flag override. { - name: "flag override client config - containerd", - runtime: runtime.RuntimeContainerd, - configWithAuthPath: ccDir, - clientConfigFlag: "/tmp", - igniteConfig: igniteConfigContent, - wantErr: true, + name: "flag override client config - containerd", + runtime: runtime.RuntimeContainerd, + clientConfigFlag: emptyDir, + igniteConfig: igniteConfigContent, + wantErr: true, }, { - name: "flag override client config - docker", - runtime: runtime.RuntimeDocker, - configWithAuthPath: ccDir, - clientConfigFlag: "/tmp", - igniteConfig: igniteConfigContent, - wantErr: true, + name: "flag override client config - docker", + runtime: runtime.RuntimeDocker, + clientConfigFlag: emptyDir, + igniteConfig: igniteConfigContent, + wantErr: true, }, - // Following set the client config dir via flag without any actual + // Following sets the client config dir via flag without any actual // client config. Import fails due to missing auth info in the given // client config dir. { - name: "invalid client config - containerd", - runtime: runtime.RuntimeContainerd, - configWithAuthPath: "", - clientConfigFlag: ccDir, - wantErr: true, + name: "invalid client config - containerd", + runtime: runtime.RuntimeContainerd, + clientConfigFlag: emptyDir, + wantErr: true, }, { - name: "invalid client config - docker", - runtime: runtime.RuntimeDocker, - configWithAuthPath: "", - clientConfigFlag: ccDir, - wantErr: true, + name: "invalid client config - docker", + runtime: runtime.RuntimeDocker, + clientConfigFlag: emptyDir, + wantErr: true, }, } - for _, rt := range cases { - rt := rt - t.Run(rt.name, func(t *testing.T) { + testFunc := func(rt testCase, osImage, kernelImage string) func(t *testing.T) { + return func(t *testing.T) { igniteCmd := util.NewCommand(t, igniteBin) // Remove images from ignite store and runtime store. Remove @@ -141,18 +152,8 @@ spec: // whole command. // TODO: Improve image rm to not fail completely when there are // multiple images and some are not found. - util.RmiCompletely(testOSImage, igniteCmd, rt.runtime) - util.RmiCompletely(testKernelImage, igniteCmd, rt.runtime) - - // Write client config if given. - if len(rt.configWithAuthPath) > 0 { - // Ensure the directory exists and create a config file in the - // directory. - assert.NilError(t, os.MkdirAll(rt.configWithAuthPath, 0755)) - configPath := filepath.Join(rt.configWithAuthPath, "config.json") - assert.NilError(t, os.WriteFile(configPath, []byte(clientConfigContent), 0600)) - defer os.Remove(configPath) - } + util.RmiCompletely(osImage, igniteCmd, rt.runtime) + util.RmiCompletely(kernelImage, igniteCmd, rt.runtime) // Write ignite config if provided. var igniteConfigPath string @@ -181,7 +182,7 @@ spec: // Run image import. _, importErr := igniteCmd.New(). - With("image", "import", testOSImage). + With("image", "import", osImage). With(imageImportCmdArgs...). Cmd.CombinedOutput() if (importErr != nil) != rt.wantErr { @@ -190,12 +191,22 @@ spec: // Run kernel import. _, importErr = igniteCmd.New(). - With("image", "import", testKernelImage). + With("image", "import", kernelImage). With(imageImportCmdArgs...). Cmd.CombinedOutput() if (importErr != nil) != rt.wantErr { t.Error("expected kernel image import to fail") } - }) + } + } + + for _, rt := range cases { + rt := rt + t.Run("http_"+rt.name, testFunc(rt, httpTestOSImage, httpTestKernelImage)) + } + + for _, rt := range cases { + rt := rt + t.Run("https_"+rt.name, testFunc(rt, httpsTestOSImage, httpsTestKernelImage)) } } diff --git a/e2e/util/setup-private-registry.sh b/e2e/util/setup-private-registry.sh index 7d1fcf13c..3443fcab5 100644 --- a/e2e/util/setup-private-registry.sh +++ b/e2e/util/setup-private-registry.sh @@ -1,57 +1,95 @@ #!/usr/bin/env bash -set -e - -# This script runs a local private docker registry with self-signed certificate -# and basic auth. - -REGISTRY_SECRET_PATH=/tmp/ignite-test-registry -PRIVATE_KEY=${REGISTRY_SECRET_PATH}/certs/domain.key -CERT=${REGISTRY_SECRET_PATH}/certs/domain.crt -HTPASSWD=${REGISTRY_SECRET_PATH}/auth/htpasswd -USERNAME=testuser -PASSWORD=testpassword -REGISTRY_ADDRESS=https://localhost:5000 -OS_IMG=weaveworks/ignite-ubuntu:latest -LOCAL_OS_IMG=localhost:5000/weaveworks/ignite-ubuntu:test -KERNEL_IMG=weaveworks/ignite-kernel:5.4.108 -LOCAL_KERNEL_IMG=localhost:5000/weaveworks/ignite-kernel:test +set -eu + +# This script runs two local, private, docker registries, +# one with with self-signed certificates, TLS, and HTTP, +# the other with plain HTTP, both using basic auth. + +HTTP_REGISTRY_SECRET_PATH="$(mktemp -d)/ignite-test-registry-http" +HTTPS_REGISTRY_SECRET_PATH="$(mktemp -d)/ignite-test-registry-https" + +PRIVATE_KEY="${HTTPS_REGISTRY_SECRET_PATH}/certs/domain.key" +CERT="${HTTPS_REGISTRY_SECRET_PATH}/certs/domain.crt" + +HTTP_USERNAME="http_testuser" +HTTP_PASSWORD="http_testpassword" +HTTPS_USERNAME="https_testuser" +HTTPS_PASSWORD="https_testpassword" + +BIND_IP="127.5.0.1" +HTTP_ADDR="${BIND_IP}:5080" +HTTPS_ADDR="${BIND_IP}:5443" + +OS_IMG="weaveworks/ignite-ubuntu:latest" +KERNEL_IMG="weaveworks/ignite-kernel:5.4.108" +HTTP_LOCAL_OS_IMG="${HTTP_ADDR}/weaveworks/ignite-ubuntu:test" +HTTP_LOCAL_KERNEL_IMG="${HTTP_ADDR}/weaveworks/ignite-kernel:test" +HTTPS_LOCAL_OS_IMG="${HTTPS_ADDR}/weaveworks/ignite-ubuntu:test" +HTTPS_LOCAL_KERNEL_IMG="${HTTPS_ADDR}/weaveworks/ignite-kernel:test" # Clear any existing registry secret and create new directories. -rm -rf ${REGISTRY_SECRET_PATH} -mkdir -p ${REGISTRY_SECRET_PATH}/{certs,auth} +mkdir -p "${HTTP_REGISTRY_SECRET_PATH}/auth" +mkdir -p "${HTTPS_REGISTRY_SECRET_PATH}/"{certs,auth} # Generate key and cert. openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \ -subj "/C=US/ST=Foo/L=Bar/O=Weave" \ - -keyout ${PRIVATE_KEY} -out ${CERT} -chmod 400 ${PRIVATE_KEY} + -keyout "${PRIVATE_KEY}" -out "${CERT}" +chmod 400 "${PRIVATE_KEY}" -# Create htpasswd file. +# Create htpasswd files. docker run --rm \ --entrypoint htpasswd \ - httpd:2 -Bbn ${USERNAME} ${PASSWORD} > ${HTPASSWD} + httpd:2 -Bbn "${HTTP_USERNAME}" "${HTTP_PASSWORD}" > "${HTTP_REGISTRY_SECRET_PATH}/auth/htpasswd" + +docker run --rm \ + --entrypoint htpasswd \ + httpd:2 -Bbn "${HTTPS_USERNAME}" "${HTTPS_PASSWORD}" > "${HTTPS_REGISTRY_SECRET_PATH}/auth/htpasswd" + +# Run the registries +docker run -d --rm \ + --name ignite-test-http-registry \ + -v "${HTTP_REGISTRY_SECRET_PATH}/auth":/auth \ + -e REGISTRY_AUTH=htpasswd \ + -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \ + -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ + -p "${HTTP_ADDR}":5000 \ + registry:2 -# Run the registry. docker run -d --rm \ - --name registry \ - -v ${REGISTRY_SECRET_PATH}/auth:/auth \ - -v ${REGISTRY_SECRET_PATH}/certs:/certs \ + --name ignite-test-https-registry \ + -v "${HTTPS_REGISTRY_SECRET_PATH}/auth":/auth \ + -v "${HTTPS_REGISTRY_SECRET_PATH}/certs":/certs \ -e REGISTRY_AUTH=htpasswd \ -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \ -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \ - -p 5000:5000 \ + -p "${HTTPS_ADDR}":5000 \ registry:2 # Login, push test images to download in tests and logout. -docker login -u ${USERNAME} -p ${PASSWORD} ${REGISTRY_ADDRESS} -docker pull ${OS_IMG} -docker pull ${KERNEL_IMG} -docker tag ${OS_IMG} ${LOCAL_OS_IMG} -docker tag ${KERNEL_IMG} ${LOCAL_KERNEL_IMG} -docker push ${LOCAL_OS_IMG} -docker push ${LOCAL_KERNEL_IMG} -docker rmi ${LOCAL_OS_IMG} ${LOCAL_KERNEL_IMG} -docker logout ${REGISTRY_ADDRESS} +docker pull "${OS_IMG}" +docker pull "${KERNEL_IMG}" + +docker tag "${OS_IMG}" "${HTTP_LOCAL_OS_IMG}" +docker tag "${KERNEL_IMG}" "${HTTP_LOCAL_KERNEL_IMG}" +docker tag "${OS_IMG}" "${HTTPS_LOCAL_OS_IMG}" +docker tag "${KERNEL_IMG}" "${HTTPS_LOCAL_KERNEL_IMG}" + +docker login -u "${HTTP_USERNAME}" -p "${HTTP_PASSWORD}" "https://${HTTP_ADDR}" +docker login -u "${HTTPS_USERNAME}" -p "${HTTPS_PASSWORD}" "https://${HTTPS_ADDR}" + +# push in parallel, block until all finished +docker push "${HTTP_LOCAL_OS_IMG}" & +docker push "${HTTP_LOCAL_KERNEL_IMG}" & +docker push "${HTTPS_LOCAL_OS_IMG}" & +docker push "${HTTPS_LOCAL_KERNEL_IMG}" & +wait + +docker logout "http://${HTTP_ADDR}" +docker logout "https://${HTTPS_ADDR}" + +docker rmi "${HTTP_LOCAL_OS_IMG}" "${HTTP_LOCAL_KERNEL_IMG}" +docker rmi "${HTTPS_LOCAL_OS_IMG}" "${HTTPS_LOCAL_KERNEL_IMG}" diff --git a/pkg/runtime/auth/auth.go b/pkg/runtime/auth/auth.go index eef0e53e8..6f920d2b0 100644 --- a/pkg/runtime/auth/auth.go +++ b/pkg/runtime/auth/auth.go @@ -18,23 +18,24 @@ type AuthCreds func(string) (string, string, error) // NewAuthCreds returns an AuthCreds which loads the credentials from the // docker client config. -func NewAuthCreds(refHostname string, configPath string) (AuthCreds, error) { +func NewAuthCreds(refHostname string, configPath string) (AuthCreds, string, error) { log.Debugf("runtime.auth: client config dir path: %q", configPath) // Load does not raise an error on ENOENT dockerConfigFile, err := dockercliconfig.Load(configPath) if err != nil { - return nil, err + return nil, "", err } // DefaultHost converts "docker.io" to "registry-1.docker.io", // which is wanted by credFunc . credFuncExpectedHostname, err := docker.DefaultHost(refHostname) if err != nil { - return nil, err + return nil, "", err } var credFunc AuthCreds + var serverAddress string authConfigHostnames := []string{refHostname} if refHostname == "docker.io" || refHostname == "registry-1.docker.io" { @@ -62,7 +63,7 @@ func NewAuthCreds(refHostname string, configPath string) (AuthCreds, error) { } else { acsaHostname := credentials.ConvertToHostname(ac.ServerAddress) if acsaHostname != authConfigHostname { - return nil, fmt.Errorf("expected the hostname part of ac.ServerAddress (%q) to be authConfigHostname=%q, got %q", + return nil, "", fmt.Errorf("expected the hostname part of ac.ServerAddress (%q) to be authConfigHostname=%q, got %q", ac.ServerAddress, authConfigHostname, acsaHostname) } } @@ -83,12 +84,13 @@ func NewAuthCreds(refHostname string, configPath string) (AuthCreds, error) { } return ac.Username, ac.Password, nil } + serverAddress = ac.ServerAddress break } } } // credFunc can be nil here. - return credFunc, nil + return credFunc, serverAddress, nil } func isAuthConfigEmpty(ac dockercliconfigtypes.AuthConfig) bool { diff --git a/pkg/runtime/containerd/client.go b/pkg/runtime/containerd/client.go index 2c48377d7..7942764f2 100644 --- a/pkg/runtime/containerd/client.go +++ b/pkg/runtime/containerd/client.go @@ -11,9 +11,11 @@ import ( "os/exec" "path/filepath" "strconv" + "strings" "syscall" "time" + "github.com/docker/cli/cli/config/credentials" meta "github.com/weaveworks/ignite/pkg/apis/meta/v1alpha1" "github.com/weaveworks/ignite/pkg/constants" "github.com/weaveworks/ignite/pkg/preflight" @@ -149,27 +151,44 @@ func GetContainerdClient() (*ctdClient, error) { // host name. func newRemoteResolver(refHostname string, configPath string) (remotes.Resolver, error) { var authzOpts []docker.AuthorizerOpt - if authCreds, err := auth.NewAuthCreds(refHostname, configPath); err != nil { + regOpts := []docker.RegistryOpt{} + insecureAllowed := false + client := &http.Client{} + + // Allow setting insecure_registries through a client-side ENV variable. + // dockerconfig.json does not have a place to set this. + // We would have to override the parser to add a field otherwise. + for _, reg := range strings.Split(os.Getenv("IGNITE_CONTAINERD_INSECURE_REGISTRIES"), ",") { + // image hostnames don't have protocols, this is the most forgiving parsing logic. + if credentials.ConvertToHostname(reg) == refHostname { + insecureAllowed = true + } + } + + if authCreds, serverAddress, err := auth.NewAuthCreds(refHostname, configPath); err != nil { return nil, err } else { authzOpts = append(authzOpts, docker.WithAuthCreds(authCreds)) + // Allow the dockerconfig.json to specify HTTP as a specific protocol override, defaults to HTTPS + if strings.HasPrefix(serverAddress, "http://") { + if !insecureAllowed { + return nil, fmt.Errorf("Registry %q uses plain HTTP, but is not in the IGNITE_CONTAINERD_INSECURE_REGISTRIES env var", serverAddress) + } + regOpts = append(regOpts, docker.WithPlainHTTP(docker.MatchAllHosts)) + } else { + if insecureAllowed { + log.Warnf("Disabling TLS Verification for %q via IGNITE_CONTAINERD_INSECURE_REGISTRIES env var", serverAddress) + client.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + } + } } authz := docker.NewDockerAuthorizer(authzOpts...) - // TODO: Add plain http option. - regOpts := []docker.RegistryOpt{ - docker.WithAuthorizer(authz), - } - - // TODO: Make this opt-in via a flag option. - tr := &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - } - client := &http.Client{ - Transport: tr, - } + regOpts = append(regOpts, docker.WithAuthorizer(authz)) regOpts = append(regOpts, docker.WithClient(client)) // TODO: Add option to skip verifying HTTPS cert. diff --git a/pkg/runtime/docker/client.go b/pkg/runtime/docker/client.go index 473d81a43..f35fc7743 100644 --- a/pkg/runtime/docker/client.go +++ b/pkg/runtime/docker/client.go @@ -69,7 +69,7 @@ func (dc *dockerClient) PullImage(image meta.OCIImageRef) (err error) { } // Get available credentials from docker cli config. - authCreds, err := auth.NewAuthCreds(refDomain, providers.ClientConfigDir) + authCreds, _, err := auth.NewAuthCreds(refDomain, providers.ClientConfigDir) if err != nil { return err } From bcc598b63bc64405fa4489ea7c43236648af9f1c Mon Sep 17 00:00:00 2001 From: Sunny Date: Mon, 31 May 2021 19:35:34 +0530 Subject: [PATCH 5/8] Add TestNewRemoteResolver This mainly checks if the remote resolver enforces the usage of insecure registries when the registry server address is http. --- e2e/registry_auth_test.go | 9 +-- pkg/runtime/containerd/client.go | 10 ++-- pkg/runtime/containerd/client_test.go | 81 +++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/e2e/registry_auth_test.go b/e2e/registry_auth_test.go index a9b8b18d2..3a122478e 100644 --- a/e2e/registry_auth_test.go +++ b/e2e/registry_auth_test.go @@ -11,6 +11,7 @@ import ( "github.com/weaveworks/ignite/e2e/util" "github.com/weaveworks/ignite/pkg/runtime" + "github.com/weaveworks/ignite/pkg/runtime/containerd" ) const ( @@ -40,8 +41,8 @@ const clientConfigContent = ` func TestPullFromAuthRegistry(t *testing.T) { assert.Assert(t, e2eHome != "", "IGNITE_E2E_HOME should be set") - os.Setenv("IGNITE_CONTAINERD_INSECURE_REGISTRIES", "http://127.5.0.1:5080,https://127.5.0.1:5443") - defer os.Unsetenv("IGNITE_CONTAINERD_INSECURE_REGISTRIES") + os.Setenv(containerd.InsecureRegistriesEnvVar, "http://127.5.0.1:5080,https://127.5.0.1:5443") + defer os.Unsetenv(containerd.InsecureRegistriesEnvVar) // Create a client config directory to use in test. emptyDir, err := ioutil.TempDir("", "ignite-test") @@ -186,7 +187,7 @@ spec: With(imageImportCmdArgs...). Cmd.CombinedOutput() if (importErr != nil) != rt.wantErr { - t.Error("expected OS image import to fail") + t.Errorf("expected error %t, actual: %v", rt.wantErr, importErr) } // Run kernel import. @@ -195,7 +196,7 @@ spec: With(imageImportCmdArgs...). Cmd.CombinedOutput() if (importErr != nil) != rt.wantErr { - t.Error("expected kernel image import to fail") + t.Errorf("expected error %t, actual: %v", rt.wantErr, importErr) } } } diff --git a/pkg/runtime/containerd/client.go b/pkg/runtime/containerd/client.go index 7942764f2..2a164a6e1 100644 --- a/pkg/runtime/containerd/client.go +++ b/pkg/runtime/containerd/client.go @@ -53,6 +53,9 @@ const ( stopTimeoutLabel = "IgniteStopTimeout" logPathTemplate = "/tmp/%s.log" resolvConfName = "runtime.containerd.resolv.conf" + + // InsecureRegistriesEnvVar helps set insecure registries. + InsecureRegistriesEnvVar = "IGNITE_CONTAINERD_INSECURE_REGISTRIES" ) var ( @@ -158,7 +161,7 @@ func newRemoteResolver(refHostname string, configPath string) (remotes.Resolver, // Allow setting insecure_registries through a client-side ENV variable. // dockerconfig.json does not have a place to set this. // We would have to override the parser to add a field otherwise. - for _, reg := range strings.Split(os.Getenv("IGNITE_CONTAINERD_INSECURE_REGISTRIES"), ",") { + for _, reg := range strings.Split(os.Getenv(InsecureRegistriesEnvVar), ",") { // image hostnames don't have protocols, this is the most forgiving parsing logic. if credentials.ConvertToHostname(reg) == refHostname { insecureAllowed = true @@ -172,12 +175,12 @@ func newRemoteResolver(refHostname string, configPath string) (remotes.Resolver, // Allow the dockerconfig.json to specify HTTP as a specific protocol override, defaults to HTTPS if strings.HasPrefix(serverAddress, "http://") { if !insecureAllowed { - return nil, fmt.Errorf("Registry %q uses plain HTTP, but is not in the IGNITE_CONTAINERD_INSECURE_REGISTRIES env var", serverAddress) + return nil, fmt.Errorf("Registry %q uses plain HTTP, but is not in the %s env var", serverAddress, InsecureRegistriesEnvVar) } regOpts = append(regOpts, docker.WithPlainHTTP(docker.MatchAllHosts)) } else { if insecureAllowed { - log.Warnf("Disabling TLS Verification for %q via IGNITE_CONTAINERD_INSECURE_REGISTRIES env var", serverAddress) + log.Warnf("Disabling TLS Verification for %q via %s env var", serverAddress, InsecureRegistriesEnvVar) client.Transport = &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, @@ -191,7 +194,6 @@ func newRemoteResolver(refHostname string, configPath string) (remotes.Resolver, regOpts = append(regOpts, docker.WithAuthorizer(authz)) regOpts = append(regOpts, docker.WithClient(client)) - // TODO: Add option to skip verifying HTTPS cert. resolverOpts := docker.ResolverOptions{ Hosts: docker.ConfigureDefaultRegistries(regOpts...), } diff --git a/pkg/runtime/containerd/client_test.go b/pkg/runtime/containerd/client_test.go index d71bbc499..7f4cec9ef 100644 --- a/pkg/runtime/containerd/client_test.go +++ b/pkg/runtime/containerd/client_test.go @@ -1,6 +1,8 @@ package containerd import ( + "fmt" + "io/ioutil" "os" "path/filepath" "strings" @@ -168,3 +170,82 @@ func TestV2ShimRuntimesHaveBinaryNames(t *testing.T) { } } } + +func TestNewRemoteResolver(t *testing.T) { + // Use a template for the configuration and get a registry configuration + // with appropriate protocol. + templateConfig := ` +{ + "auths": { + "%s://127.5.0.1:5443": { + "auth": "aHR0cHNfdGVzdHVzZXI6aHR0cHNfdGVzdHBhc3N3b3Jk" + } + } +} +` + getRegistryConfigWithProtocol := func(protocol string) string { + return fmt.Sprintf(templateConfig, protocol) + } + + domainRef := "127.5.0.1:5443" + + cases := []struct { + name string + insecureRegistries []string + registryConfig string + wantErr bool + }{ + { + name: "invalid configuration", + registryConfig: ` +{ some invalid json } +`, + wantErr: true, + }, + { + name: "valid configuration", + registryConfig: getRegistryConfigWithProtocol("https"), + }, + { + name: "http server address without insecure registries", + registryConfig: getRegistryConfigWithProtocol("http"), + wantErr: true, + }, + { + name: "http server address with insecure registries", + insecureRegistries: []string{"127.5.0.1:5443"}, + registryConfig: getRegistryConfigWithProtocol("http"), + }, + } + + for _, rt := range cases { + t.Run(rt.name, func(t *testing.T) { + // Create directory for the registry configuration. + dir, err := ioutil.TempDir("", "ignite") + if err != nil { + t.Fatalf("failed to create storage for ignite: %v", err) + } + defer os.RemoveAll(dir) + + // If a registry configuration content is given, write it. + if len(rt.registryConfig) > 0 { + configPath := filepath.Join(dir, "config.json") + writeErr := os.WriteFile(configPath, []byte(rt.registryConfig), 0600) + assert.NilError(t, writeErr) + defer os.Remove(configPath) + } + + // If insecure registries are given, set env vars. + if len(rt.insecureRegistries) > 0 { + irValues := strings.Join(rt.insecureRegistries, ",") + os.Setenv(InsecureRegistriesEnvVar, irValues) + defer os.Unsetenv(InsecureRegistriesEnvVar) + } + + _, rrErr := newRemoteResolver(domainRef, dir) + if (rrErr != nil) != rt.wantErr { + t.Errorf("expected error %t, actual: %v", rt.wantErr, rrErr) + } + }) + } +} From dfcebe8a658369eb6c1511016356c837b230cf6a Mon Sep 17 00:00:00 2001 From: Sunny Date: Mon, 31 May 2021 19:54:52 +0530 Subject: [PATCH 6/8] TestPullFromAuthRe..: handle registry start/stop TestPullFromAuthRegistry executes the script to set up the registries at the beginning and stop the registries at the end of the test. Remove registry setup from semaphore --- .semaphore/semaphore.yml | 1 - e2e/registry_auth_test.go | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 65d8512e1..0093131b0 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -32,5 +32,4 @@ blocks: - make ignite ignite-spawn ignited bin/amd64/Dockerfile GO_MAKE_TARGET=local - make test - make root-test - - bash e2e/util/setup-private-registry.sh # This is required for registry auth e2e tests. - make e2e-nobuild # this depends on Semaphore CI's support for nested virtualization diff --git a/e2e/registry_auth_test.go b/e2e/registry_auth_test.go index 3a122478e..630ddb378 100644 --- a/e2e/registry_auth_test.go +++ b/e2e/registry_auth_test.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "os" + "os/exec" "path/filepath" "testing" @@ -41,6 +42,26 @@ const clientConfigContent = ` func TestPullFromAuthRegistry(t *testing.T) { assert.Assert(t, e2eHome != "", "IGNITE_E2E_HOME should be set") + // Set up the registries. + startRegistries := exec.Command( + "bash", "util/setup-private-registry.sh", + ) + startOutput, startErr := startRegistries.CombinedOutput() + if startErr != nil { + t.Fatalf("failed to set up registries: %v, %s", startErr, string(startOutput)) + } + + // Stop the registries at the end. + stopRegistries := exec.Command( + "docker", "stop", + "ignite-test-http-registry", "ignite-test-https-registry", + ) + defer func() { + if stopOutput, stopErr := stopRegistries.CombinedOutput(); stopErr != nil { + t.Fatalf("failed to stop registries: %v, %s", stopErr, string(stopOutput)) + } + }() + os.Setenv(containerd.InsecureRegistriesEnvVar, "http://127.5.0.1:5080,https://127.5.0.1:5443") defer os.Unsetenv(containerd.InsecureRegistriesEnvVar) From b569d9720fb1626839b1f87d47531611338c57a7 Mon Sep 17 00:00:00 2001 From: Sunny Date: Mon, 31 May 2021 20:36:37 +0530 Subject: [PATCH 7/8] Rename client-config-dir to registry-config-dir --- cmd/ignite/cmd/cmdutil/flags.go | 4 +- cmd/ignite/cmd/cmdutil/providers.go | 14 +-- cmd/ignite/cmd/imgcmd/import.go | 2 +- cmd/ignite/cmd/kerncmd/import.go | 2 +- cmd/ignite/cmd/vmcmd/create.go | 2 +- cmd/ignite/run/create.go | 4 +- cmd/ignite/run/import.go | 4 +- docs/api/ignite_v1alpha4.md | 12 +-- docs/cli/ignite/ignite_create.md | 38 +++---- docs/cli/ignite/ignite_image_import.md | 6 +- docs/cli/ignite/ignite_kernel_import.md | 6 +- docs/cli/ignite/ignite_run.md | 2 +- docs/cli/ignite/ignite_vm_create.md | 38 +++---- docs/cli/ignite/ignite_vm_run.md | 2 +- docs/usage.md | 2 +- e2e/registry_auth_test.go | 98 +++++++++---------- go.mod | 2 +- go.sum | 4 +- pkg/apis/ignite/types.go | 10 +- .../v1alpha3/zz_generated.conversion.go | 2 +- pkg/apis/ignite/v1alpha4/types.go | 10 +- .../v1alpha4/zz_generated.conversion.go | 4 +- pkg/openapi/openapi_generated.go | 2 +- pkg/providers/providers.go | 4 +- pkg/runtime/auth/auth.go | 2 +- pkg/runtime/containerd/client.go | 2 +- pkg/runtime/docker/client.go | 2 +- vendor/modules.txt | 2 +- 28 files changed, 141 insertions(+), 141 deletions(-) diff --git a/cmd/ignite/cmd/cmdutil/flags.go b/cmd/ignite/cmd/cmdutil/flags.go index 6b093d782..013f99ec7 100644 --- a/cmd/ignite/cmd/cmdutil/flags.go +++ b/cmd/ignite/cmd/cmdutil/flags.go @@ -36,6 +36,6 @@ func AddSSHFlags(fs *pflag.FlagSet, identityFile *string, timeout *uint32) { fs.Uint32Var(timeout, "timeout", constants.SSH_DEFAULT_TIMEOUT_SECONDS, "Timeout waiting for connection in seconds") } -func AddClientConfigDirFlag(fs *pflag.FlagSet, dir *string) { - fs.StringVar(dir, "client-config-dir", "", "Directory containing the client configuration (default ~/.docker/)") +func AddRegistryConfigDirFlag(fs *pflag.FlagSet, dir *string) { + fs.StringVar(dir, "registry-config-dir", "", "Directory containing the registry configuration (default ~/.docker/)") } diff --git a/cmd/ignite/cmd/cmdutil/providers.go b/cmd/ignite/cmd/cmdutil/providers.go index 180f3a7aa..36ecdaa03 100644 --- a/cmd/ignite/cmd/cmdutil/providers.go +++ b/cmd/ignite/cmd/cmdutil/providers.go @@ -6,17 +6,17 @@ import ( "github.com/weaveworks/ignite/pkg/providers" ) -// ResolveClientConfigDir reads various configuration to resolve the client +// ResolveRegistryConfigDir reads various configuration to resolve the registry // configuration directory. -func ResolveClientConfigDir() { +func ResolveRegistryConfigDir() { if providers.ComponentConfig != nil { - // Set the providers client config dir from ignite configuration if + // Set the providers registry config dir from ignite configuration if // it's empty. When it's set in the providers and in the ignite // configuration, log about the override. - if providers.ClientConfigDir == "" { - providers.ClientConfigDir = providers.ComponentConfig.Spec.ClientConfigDir - } else if providers.ComponentConfig.Spec.ClientConfigDir != "" { - log.Debug("client-config-dir flag overriding the ignite configuration") + if providers.RegistryConfigDir == "" { + providers.RegistryConfigDir = providers.ComponentConfig.Spec.RegistryConfigDir + } else if providers.ComponentConfig.Spec.RegistryConfigDir != "" { + log.Debug("registry-config-dir flag overriding the ignite configuration") } } } diff --git a/cmd/ignite/cmd/imgcmd/import.go b/cmd/ignite/cmd/imgcmd/import.go index 0c631e910..d1f0f6c45 100644 --- a/cmd/ignite/cmd/imgcmd/import.go +++ b/cmd/ignite/cmd/imgcmd/import.go @@ -38,5 +38,5 @@ func NewCmdImport(out io.Writer) *cobra.Command { func addImportFlags(fs *pflag.FlagSet) { runtimeflag.RuntimeVar(fs, &providers.RuntimeName) - cmdutil.AddClientConfigDirFlag(fs, &providers.ClientConfigDir) + cmdutil.AddRegistryConfigDirFlag(fs, &providers.RegistryConfigDir) } diff --git a/cmd/ignite/cmd/kerncmd/import.go b/cmd/ignite/cmd/kerncmd/import.go index 36a05fe01..19e41b899 100644 --- a/cmd/ignite/cmd/kerncmd/import.go +++ b/cmd/ignite/cmd/kerncmd/import.go @@ -38,5 +38,5 @@ func NewCmdImport(out io.Writer) *cobra.Command { func addImportFlags(fs *pflag.FlagSet) { runtimeflag.RuntimeVar(fs, &providers.RuntimeName) - cmdutil.AddClientConfigDirFlag(fs, &providers.ClientConfigDir) + cmdutil.AddRegistryConfigDirFlag(fs, &providers.RegistryConfigDir) } diff --git a/cmd/ignite/cmd/vmcmd/create.go b/cmd/ignite/cmd/vmcmd/create.go index ed088a8b6..3650ec46d 100644 --- a/cmd/ignite/cmd/vmcmd/create.go +++ b/cmd/ignite/cmd/vmcmd/create.go @@ -88,5 +88,5 @@ func addCreateFlags(fs *pflag.FlagSet, cf *run.CreateFlags) { runtimeflag.RuntimeVar(fs, &providers.RuntimeName) networkflag.NetworkPluginVar(fs, &providers.NetworkPluginName) cmdutil.AddIDPrefixFlag(fs, &providers.IDPrefix) - cmdutil.AddClientConfigDirFlag(fs, &providers.ClientConfigDir) + cmdutil.AddRegistryConfigDirFlag(fs, &providers.RegistryConfigDir) } diff --git a/cmd/ignite/run/create.go b/cmd/ignite/run/create.go index 723b5b486..6c8df71b1 100644 --- a/cmd/ignite/run/create.go +++ b/cmd/ignite/run/create.go @@ -59,8 +59,8 @@ func (cf *CreateFlags) NewCreateOptions(args []string, fs *flag.FlagSet) (*Creat baseVM.Spec = providers.ComponentConfig.Spec.VMDefaults } - // Resolve client configuration used for pulling image if required. - cmdutil.ResolveClientConfigDir() + // Resolve registry configuration used for pulling image if required. + cmdutil.ResolveRegistryConfigDir() // Initialize the VM's Prefixer baseVM.Status.IDPrefix = providers.IDPrefix diff --git a/cmd/ignite/run/import.go b/cmd/ignite/run/import.go index 9d88dfb56..2a5ecdb2f 100644 --- a/cmd/ignite/run/import.go +++ b/cmd/ignite/run/import.go @@ -17,7 +17,7 @@ func ImportImage(source string) (image *api.Image, err error) { return nil, err } - cmdutil.ResolveClientConfigDir() + cmdutil.ResolveRegistryConfigDir() ociRef, err := meta.NewOCIImageRef(source) if err != nil { @@ -41,7 +41,7 @@ func ImportKernel(source string) (kernel *api.Kernel, err error) { return nil, err } - cmdutil.ResolveClientConfigDir() + cmdutil.ResolveRegistryConfigDir() ociRef, err := meta.NewOCIImageRef(source) if err != nil { diff --git a/docs/api/ignite_v1alpha4.md b/docs/api/ignite_v1alpha4.md index e75eb64d4..efb64d116 100644 --- a/docs/api/ignite_v1alpha4.md +++ b/docs/api/ignite_v1alpha4.md @@ -166,15 +166,15 @@ type Configuration struct { Configuration represents the ignite runtime configuration. +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -## type [ConfigurationSpec](https://github.com/weaveworks/ignite/tree/main/pkg/apis/ignite/v1alpha4/types.go?s=9475:9871#L261) +## type [ConfigurationSpec](https://github.com/weaveworks/ignite/tree/main/pkg/apis/ignite/v1alpha4/types.go?s=9475:9883#L261) ``` go type ConfigurationSpec struct { - Runtime igniteRuntime.Name `json:"runtime,omitempty"` - NetworkPlugin igniteNetwork.PluginName `json:"networkPlugin,omitempty"` - VMDefaults VMSpec `json:"vmDefaults,omitempty"` - IDPrefix string `json:"idPrefix,omitempty"` - ClientConfigDir string `json:"clientConfigDir,omitempty"` + Runtime igniteRuntime.Name `json:"runtime,omitempty"` + NetworkPlugin igniteNetwork.PluginName `json:"networkPlugin,omitempty"` + VMDefaults VMSpec `json:"vmDefaults,omitempty"` + IDPrefix string `json:"idPrefix,omitempty"` + RegistryConfigDir string `json:"registryConfigDir,omitempty"` } ``` diff --git a/docs/cli/ignite/ignite_create.md b/docs/cli/ignite/ignite_create.md index ae5fd9e1d..5a2f9245d 100644 --- a/docs/cli/ignite/ignite_create.md +++ b/docs/cli/ignite/ignite_create.md @@ -34,25 +34,25 @@ ignite create [flags] ### Options ``` - --client-config-dir string Directory containing the client configuration (default ~/.docker/) - --config string Specify a path to a file with the API resources you want to pass - -f, --copy-files strings Copy files/directories from the host to the created VM - --cpus uint VM vCPU count, 1 or even numbers between 1 and 32 (default 1) - -h, --help help for create - --id-prefix string Prefix string for system identifiers (default ignite) - --kernel-args string Set the command line for the kernel (default "console=ttyS0 reboot=k panic=1 pci=off ip=dhcp") - -k, --kernel-image oci-image Specify an OCI image containing the kernel at /boot/vmlinux and optionally, modules (default weaveworks/ignite-kernel:5.4.108) - -l, --label stringArray Set a label (foo=bar) - --memory size Amount of RAM to allocate for the VM (default 512.0 MB) - -n, --name string Specify the name - --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) - -p, --ports strings Map host ports to VM ports - --require-name Require VM name to be passed, no name generation - --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) - --sandbox-image oci-image Specify an OCI image for the VM sandbox (default weaveworks/ignite:dev) - -s, --size size VM filesystem size, for example 5GB or 2048MB (default 4.0 GB) - --ssh[=] Enable SSH for the VM. If is given, it will be imported as the public key. If just '--ssh' is specified, a new keypair will be generated. (default is unset, which disables SSH access to the VM) - -v, --volumes volume Expose block devices from the host inside the VM + --config string Specify a path to a file with the API resources you want to pass + -f, --copy-files strings Copy files/directories from the host to the created VM + --cpus uint VM vCPU count, 1 or even numbers between 1 and 32 (default 1) + -h, --help help for create + --id-prefix string Prefix string for system identifiers (default ignite) + --kernel-args string Set the command line for the kernel (default "console=ttyS0 reboot=k panic=1 pci=off ip=dhcp") + -k, --kernel-image oci-image Specify an OCI image containing the kernel at /boot/vmlinux and optionally, modules (default weaveworks/ignite-kernel:5.4.108) + -l, --label stringArray Set a label (foo=bar) + --memory size Amount of RAM to allocate for the VM (default 512.0 MB) + -n, --name string Specify the name + --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) + -p, --ports strings Map host ports to VM ports + --registry-config-dir string Directory containing the registry configuration (default ~/.docker/) + --require-name Require VM name to be passed, no name generation + --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) + --sandbox-image oci-image Specify an OCI image for the VM sandbox (default weaveworks/ignite:dev) + -s, --size size VM filesystem size, for example 5GB or 2048MB (default 4.0 GB) + --ssh[=] Enable SSH for the VM. If is given, it will be imported as the public key. If just '--ssh' is specified, a new keypair will be generated. (default is unset, which disables SSH access to the VM) + -v, --volumes volume Expose block devices from the host inside the VM ``` ### Options inherited from parent commands diff --git a/docs/cli/ignite/ignite_image_import.md b/docs/cli/ignite/ignite_image_import.md index 330a1192e..bda74abad 100644 --- a/docs/cli/ignite/ignite_image_import.md +++ b/docs/cli/ignite/ignite_image_import.md @@ -17,9 +17,9 @@ ignite image import [flags] ### Options ``` - --client-config-dir string Directory containing the client configuration (default ~/.docker/) - -h, --help help for import - --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) + -h, --help help for import + --registry-config-dir string Directory containing the registry configuration (default ~/.docker/) + --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) ``` ### Options inherited from parent commands diff --git a/docs/cli/ignite/ignite_kernel_import.md b/docs/cli/ignite/ignite_kernel_import.md index aaf40b2c5..5988fe9bd 100644 --- a/docs/cli/ignite/ignite_kernel_import.md +++ b/docs/cli/ignite/ignite_kernel_import.md @@ -17,9 +17,9 @@ ignite kernel import [flags] ### Options ``` - --client-config-dir string Directory containing the client configuration (default ~/.docker/) - -h, --help help for import - --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) + -h, --help help for import + --registry-config-dir string Directory containing the registry configuration (default ~/.docker/) + --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) ``` ### Options inherited from parent commands diff --git a/docs/cli/ignite/ignite_run.md b/docs/cli/ignite/ignite_run.md index de3930600..5ebd382d8 100644 --- a/docs/cli/ignite/ignite_run.md +++ b/docs/cli/ignite/ignite_run.md @@ -27,7 +27,6 @@ ignite run [flags] ### Options ``` - --client-config-dir string Directory containing the client configuration (default ~/.docker/) --config string Specify a path to a file with the API resources you want to pass -f, --copy-files strings Copy files/directories from the host to the created VM --cpus uint VM vCPU count, 1 or even numbers between 1 and 32 (default 1) @@ -43,6 +42,7 @@ ignite run [flags] -n, --name string Specify the name --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) -p, --ports strings Map host ports to VM ports + --registry-config-dir string Directory containing the registry configuration (default ~/.docker/) --require-name Require VM name to be passed, no name generation --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) --sandbox-image oci-image Specify an OCI image for the VM sandbox (default weaveworks/ignite:dev) diff --git a/docs/cli/ignite/ignite_vm_create.md b/docs/cli/ignite/ignite_vm_create.md index 9f7108666..def7403d3 100644 --- a/docs/cli/ignite/ignite_vm_create.md +++ b/docs/cli/ignite/ignite_vm_create.md @@ -34,25 +34,25 @@ ignite vm create [flags] ### Options ``` - --client-config-dir string Directory containing the client configuration (default ~/.docker/) - --config string Specify a path to a file with the API resources you want to pass - -f, --copy-files strings Copy files/directories from the host to the created VM - --cpus uint VM vCPU count, 1 or even numbers between 1 and 32 (default 1) - -h, --help help for create - --id-prefix string Prefix string for system identifiers (default ignite) - --kernel-args string Set the command line for the kernel (default "console=ttyS0 reboot=k panic=1 pci=off ip=dhcp") - -k, --kernel-image oci-image Specify an OCI image containing the kernel at /boot/vmlinux and optionally, modules (default weaveworks/ignite-kernel:5.4.108) - -l, --label stringArray Set a label (foo=bar) - --memory size Amount of RAM to allocate for the VM (default 512.0 MB) - -n, --name string Specify the name - --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) - -p, --ports strings Map host ports to VM ports - --require-name Require VM name to be passed, no name generation - --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) - --sandbox-image oci-image Specify an OCI image for the VM sandbox (default weaveworks/ignite:dev) - -s, --size size VM filesystem size, for example 5GB or 2048MB (default 4.0 GB) - --ssh[=] Enable SSH for the VM. If is given, it will be imported as the public key. If just '--ssh' is specified, a new keypair will be generated. (default is unset, which disables SSH access to the VM) - -v, --volumes volume Expose block devices from the host inside the VM + --config string Specify a path to a file with the API resources you want to pass + -f, --copy-files strings Copy files/directories from the host to the created VM + --cpus uint VM vCPU count, 1 or even numbers between 1 and 32 (default 1) + -h, --help help for create + --id-prefix string Prefix string for system identifiers (default ignite) + --kernel-args string Set the command line for the kernel (default "console=ttyS0 reboot=k panic=1 pci=off ip=dhcp") + -k, --kernel-image oci-image Specify an OCI image containing the kernel at /boot/vmlinux and optionally, modules (default weaveworks/ignite-kernel:5.4.108) + -l, --label stringArray Set a label (foo=bar) + --memory size Amount of RAM to allocate for the VM (default 512.0 MB) + -n, --name string Specify the name + --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) + -p, --ports strings Map host ports to VM ports + --registry-config-dir string Directory containing the registry configuration (default ~/.docker/) + --require-name Require VM name to be passed, no name generation + --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) + --sandbox-image oci-image Specify an OCI image for the VM sandbox (default weaveworks/ignite:dev) + -s, --size size VM filesystem size, for example 5GB or 2048MB (default 4.0 GB) + --ssh[=] Enable SSH for the VM. If is given, it will be imported as the public key. If just '--ssh' is specified, a new keypair will be generated. (default is unset, which disables SSH access to the VM) + -v, --volumes volume Expose block devices from the host inside the VM ``` ### Options inherited from parent commands diff --git a/docs/cli/ignite/ignite_vm_run.md b/docs/cli/ignite/ignite_vm_run.md index 0a66cb23a..375721113 100644 --- a/docs/cli/ignite/ignite_vm_run.md +++ b/docs/cli/ignite/ignite_vm_run.md @@ -27,7 +27,6 @@ ignite vm run [flags] ### Options ``` - --client-config-dir string Directory containing the client configuration (default ~/.docker/) --config string Specify a path to a file with the API resources you want to pass -f, --copy-files strings Copy files/directories from the host to the created VM --cpus uint VM vCPU count, 1 or even numbers between 1 and 32 (default 1) @@ -43,6 +42,7 @@ ignite vm run [flags] -n, --name string Specify the name --network-plugin plugin Network plugin to use. Available options are: [cni docker-bridge] (default cni) -p, --ports strings Map host ports to VM ports + --registry-config-dir string Directory containing the registry configuration (default ~/.docker/) --require-name Require VM name to be passed, no name generation --runtime runtime Container runtime to use. Available options are: [docker containerd] (default containerd) --sandbox-image oci-image Specify an OCI image for the VM sandbox (default weaveworks/ignite:dev) diff --git a/docs/usage.md b/docs/usage.md index 40a220b61..5036c7cfb 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -104,7 +104,7 @@ image pull will fail with errors related to the specific credential helper. In presence of both auth tokens and credential helpers in a configuration file, credential helper takes precedence. -The `--registry-config-dir` flag can be used to override the default directory(`$HOME/.docker/config.json`). +The `--registry-config-dir` flag can be used to override the default directory(`$HOME/.docker/`). This can also be done from the ignite [Configuration](./ignite-configuration). When using the `containerd` runtime to pull images, TLS verification can be disabled, diff --git a/e2e/registry_auth_test.go b/e2e/registry_auth_test.go index 630ddb378..3d00e6043 100644 --- a/e2e/registry_auth_test.go +++ b/e2e/registry_auth_test.go @@ -22,11 +22,11 @@ const ( httpsTestKernelImage = "127.5.0.1:5443/weaveworks/ignite-kernel:test" ) -// client config with auth info for the registry setup in +// registry config with auth info for the registry setup in // e2e/util/setup-private-registry.sh. // NOTE: Update the auth token if the credentials in setup-private-registry.sh // is updated. -const clientConfigContent = ` +const registryConfigContent = ` { "auths": { "http://127.5.0.1:5080": { @@ -65,21 +65,21 @@ func TestPullFromAuthRegistry(t *testing.T) { os.Setenv(containerd.InsecureRegistriesEnvVar, "http://127.5.0.1:5080,https://127.5.0.1:5443") defer os.Unsetenv(containerd.InsecureRegistriesEnvVar) - // Create a client config directory to use in test. + // Create a registry config directory to use in test. emptyDir, err := ioutil.TempDir("", "ignite-test") assert.NilError(t, err) defer os.RemoveAll(emptyDir) - // Create a client config directory to use in test. - ccDir, err := ioutil.TempDir("", "ignite-test") + // Create a registry config directory to use in test. + rcDir, err := ioutil.TempDir("", "ignite-test") assert.NilError(t, err) - defer os.RemoveAll(ccDir) + defer os.RemoveAll(rcDir) // Ensure the directory exists and create a config file in the // directory. - assert.NilError(t, os.MkdirAll(ccDir, 0755)) - configPath := filepath.Join(ccDir, "config.json") - assert.NilError(t, os.WriteFile(configPath, []byte(clientConfigContent), 0600)) + assert.NilError(t, os.MkdirAll(rcDir, 0755)) + configPath := filepath.Join(rcDir, "config.json") + assert.NilError(t, os.WriteFile(configPath, []byte(registryConfigContent), 0600)) defer os.Remove(configPath) templateConfig := `--- @@ -88,16 +88,16 @@ kind: Configuration metadata: name: test-config spec: - clientConfigDir: %s + registryConfigDir: %s ` - igniteConfigContent := fmt.Sprintf(templateConfig, ccDir) + igniteConfigContent := fmt.Sprintf(templateConfig, rcDir) type testCase struct { - name string - runtime runtime.Name - clientConfigFlag string - igniteConfig string - wantErr bool + name string + runtime runtime.Name + registryConfigFlag string + igniteConfig string + wantErr bool } cases := []testCase{ { @@ -111,57 +111,57 @@ spec: wantErr: true, }, { - name: "client config flag - containerd", - runtime: runtime.RuntimeContainerd, - clientConfigFlag: ccDir, + name: "registry config flag - containerd", + runtime: runtime.RuntimeContainerd, + registryConfigFlag: rcDir, }, { - name: "client config flag - docker", - runtime: runtime.RuntimeDocker, - clientConfigFlag: ccDir, + name: "registry config flag - docker", + runtime: runtime.RuntimeDocker, + registryConfigFlag: rcDir, }, { - name: "client config in ignite config - containerd", + name: "registry config in ignite config - containerd", runtime: runtime.RuntimeContainerd, igniteConfig: igniteConfigContent, }, { - name: "client config in ignite config - docker", + name: "registry config in ignite config - docker", runtime: runtime.RuntimeDocker, igniteConfig: igniteConfigContent, }, - // Following sets the client config dir to a location without a valid - // client config file, although the client config dir in the ignite + // Following sets the registry config dir to a location without a valid + // registry config file, although the registry config dir in the ignite // config is correct, the import fails due to bad configuration by the // flag override. { - name: "flag override client config - containerd", - runtime: runtime.RuntimeContainerd, - clientConfigFlag: emptyDir, - igniteConfig: igniteConfigContent, - wantErr: true, + name: "flag override registry config - containerd", + runtime: runtime.RuntimeContainerd, + registryConfigFlag: emptyDir, + igniteConfig: igniteConfigContent, + wantErr: true, }, { - name: "flag override client config - docker", - runtime: runtime.RuntimeDocker, - clientConfigFlag: emptyDir, - igniteConfig: igniteConfigContent, - wantErr: true, + name: "flag override registry config - docker", + runtime: runtime.RuntimeDocker, + registryConfigFlag: emptyDir, + igniteConfig: igniteConfigContent, + wantErr: true, }, - // Following sets the client config dir via flag without any actual - // client config. Import fails due to missing auth info in the given - // client config dir. + // Following sets the registry config dir via flag without any actual + // registry config. Import fails due to missing auth info in the given + // registry config dir. { - name: "invalid client config - containerd", - runtime: runtime.RuntimeContainerd, - clientConfigFlag: emptyDir, - wantErr: true, + name: "invalid registry config - containerd", + runtime: runtime.RuntimeContainerd, + registryConfigFlag: emptyDir, + wantErr: true, }, { - name: "invalid client config - docker", - runtime: runtime.RuntimeDocker, - clientConfigFlag: emptyDir, - wantErr: true, + name: "invalid registry config - docker", + runtime: runtime.RuntimeDocker, + registryConfigFlag: emptyDir, + wantErr: true, }, } @@ -195,8 +195,8 @@ spec: // Construct the ignite image import command. imageImportCmdArgs := []string{"--runtime", rt.runtime.String()} - if len(rt.clientConfigFlag) > 0 { - imageImportCmdArgs = append(imageImportCmdArgs, "--client-config-dir", rt.clientConfigFlag) + if len(rt.registryConfigFlag) > 0 { + imageImportCmdArgs = append(imageImportCmdArgs, "--registry-config-dir", rt.registryConfigFlag) } if len(igniteConfigPath) > 0 { imageImportCmdArgs = append(imageImportCmdArgs, "--ignite-config", igniteConfigPath) diff --git a/go.mod b/go.mod index 7a26b6f2f..b1e12834e 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,7 @@ require ( golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d // indirect golang.org/x/sys v0.0.0-20210510120138-977fb7262007 - golang.org/x/tools v0.1.1 // indirect + golang.org/x/tools v0.1.2 // indirect google.golang.org/genproto v0.0.0-20210416161957-9910b6c460de // indirect google.golang.org/grpc v1.37.0 // indirect gotest.tools v2.2.0+incompatible diff --git a/go.sum b/go.sum index 3fe693f08..0946b6129 100644 --- a/go.sum +++ b/go.sum @@ -1285,8 +1285,8 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/apis/ignite/types.go b/pkg/apis/ignite/types.go index 9f898eeac..b86a400b5 100644 --- a/pkg/apis/ignite/types.go +++ b/pkg/apis/ignite/types.go @@ -259,9 +259,9 @@ type Configuration struct { // ConfigurationSpec defines the ignite configuration. type ConfigurationSpec struct { - Runtime igniteRuntime.Name `json:"runtime,omitempty"` - NetworkPlugin igniteNetwork.PluginName `json:"networkPlugin,omitempty"` - VMDefaults VMSpec `json:"vmDefaults,omitempty"` - IDPrefix string `json:"idPrefix,omitempty"` - ClientConfigDir string `json:"clientConfigDir,omitempty"` + Runtime igniteRuntime.Name `json:"runtime,omitempty"` + NetworkPlugin igniteNetwork.PluginName `json:"networkPlugin,omitempty"` + VMDefaults VMSpec `json:"vmDefaults,omitempty"` + IDPrefix string `json:"idPrefix,omitempty"` + RegistryConfigDir string `json:"registryConfigDir,omitempty"` } diff --git a/pkg/apis/ignite/v1alpha3/zz_generated.conversion.go b/pkg/apis/ignite/v1alpha3/zz_generated.conversion.go index d9398492e..7baeaddf4 100644 --- a/pkg/apis/ignite/v1alpha3/zz_generated.conversion.go +++ b/pkg/apis/ignite/v1alpha3/zz_generated.conversion.go @@ -376,7 +376,7 @@ func autoConvert_ignite_ConfigurationSpec_To_v1alpha3_ConfigurationSpec(in *igni return err } out.IDPrefix = in.IDPrefix - // WARNING: in.ClientConfigDir requires manual conversion: does not exist in peer-type + // WARNING: in.RegistryConfigDir requires manual conversion: does not exist in peer-type return nil } diff --git a/pkg/apis/ignite/v1alpha4/types.go b/pkg/apis/ignite/v1alpha4/types.go index 2ced76c6f..45995c504 100644 --- a/pkg/apis/ignite/v1alpha4/types.go +++ b/pkg/apis/ignite/v1alpha4/types.go @@ -259,9 +259,9 @@ type Configuration struct { // ConfigurationSpec defines the ignite configuration. type ConfigurationSpec struct { - Runtime igniteRuntime.Name `json:"runtime,omitempty"` - NetworkPlugin igniteNetwork.PluginName `json:"networkPlugin,omitempty"` - VMDefaults VMSpec `json:"vmDefaults,omitempty"` - IDPrefix string `json:"idPrefix,omitempty"` - ClientConfigDir string `json:"clientConfigDir,omitempty"` + Runtime igniteRuntime.Name `json:"runtime,omitempty"` + NetworkPlugin igniteNetwork.PluginName `json:"networkPlugin,omitempty"` + VMDefaults VMSpec `json:"vmDefaults,omitempty"` + IDPrefix string `json:"idPrefix,omitempty"` + RegistryConfigDir string `json:"registryConfigDir,omitempty"` } diff --git a/pkg/apis/ignite/v1alpha4/zz_generated.conversion.go b/pkg/apis/ignite/v1alpha4/zz_generated.conversion.go index 4adb5fff0..00aadc551 100644 --- a/pkg/apis/ignite/v1alpha4/zz_generated.conversion.go +++ b/pkg/apis/ignite/v1alpha4/zz_generated.conversion.go @@ -361,7 +361,7 @@ func autoConvert_v1alpha4_ConfigurationSpec_To_ignite_ConfigurationSpec(in *Conf return err } out.IDPrefix = in.IDPrefix - out.ClientConfigDir = in.ClientConfigDir + out.RegistryConfigDir = in.RegistryConfigDir return nil } @@ -377,7 +377,7 @@ func autoConvert_ignite_ConfigurationSpec_To_v1alpha4_ConfigurationSpec(in *igni return err } out.IDPrefix = in.IDPrefix - out.ClientConfigDir = in.ClientConfigDir + out.RegistryConfigDir = in.RegistryConfigDir return nil } diff --git a/pkg/openapi/openapi_generated.go b/pkg/openapi/openapi_generated.go index fd6fed19d..388cfc712 100644 --- a/pkg/openapi/openapi_generated.go +++ b/pkg/openapi/openapi_generated.go @@ -1955,7 +1955,7 @@ func schema_pkg_apis_ignite_v1alpha4_ConfigurationSpec(ref common.ReferenceCallb Format: "", }, }, - "clientConfigDir": { + "registryConfigDir": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, Format: "", diff --git a/pkg/providers/providers.go b/pkg/providers/providers.go index 9bc2fbba8..b9bd45e78 100644 --- a/pkg/providers/providers.go +++ b/pkg/providers/providers.go @@ -37,10 +37,10 @@ var Storage storage.Storage var ComponentConfig *api.Configuration -// ClientConfigDir is the container runtime client configuration directory. +// RegistryConfigDir is the container runtime registry configuration directory. // This is used during operations like image import for loading registry // configurations. -var ClientConfigDir string +var RegistryConfigDir string type ProviderInitFunc func() error diff --git a/pkg/runtime/auth/auth.go b/pkg/runtime/auth/auth.go index 6f920d2b0..8f9628e75 100644 --- a/pkg/runtime/auth/auth.go +++ b/pkg/runtime/auth/auth.go @@ -19,7 +19,7 @@ type AuthCreds func(string) (string, string, error) // NewAuthCreds returns an AuthCreds which loads the credentials from the // docker client config. func NewAuthCreds(refHostname string, configPath string) (AuthCreds, string, error) { - log.Debugf("runtime.auth: client config dir path: %q", configPath) + log.Debugf("runtime.auth: registry config dir path: %q", configPath) // Load does not raise an error on ENOENT dockerConfigFile, err := dockercliconfig.Load(configPath) diff --git a/pkg/runtime/containerd/client.go b/pkg/runtime/containerd/client.go index 2a164a6e1..baf23f01b 100644 --- a/pkg/runtime/containerd/client.go +++ b/pkg/runtime/containerd/client.go @@ -213,7 +213,7 @@ func (cc *ctdClient) PullImage(image meta.OCIImageRef) error { refDomain := refdocker.Domain(named) // Create a remote resolver for the domain. - resolver, err := newRemoteResolver(refDomain, providers.ClientConfigDir) + resolver, err := newRemoteResolver(refDomain, providers.RegistryConfigDir) if err != nil { return err } diff --git a/pkg/runtime/docker/client.go b/pkg/runtime/docker/client.go index f35fc7743..76b779029 100644 --- a/pkg/runtime/docker/client.go +++ b/pkg/runtime/docker/client.go @@ -69,7 +69,7 @@ func (dc *dockerClient) PullImage(image meta.OCIImageRef) (err error) { } // Get available credentials from docker cli config. - authCreds, _, err := auth.NewAuthCreds(refDomain, providers.ClientConfigDir) + authCreds, _, err := auth.NewAuthCreds(refDomain, providers.RegistryConfigDir) if err != nil { return err } diff --git a/vendor/modules.txt b/vendor/modules.txt index 67b7572a3..c302575e0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -563,7 +563,7 @@ golang.org/x/text/transform golang.org/x/text/unicode/bidi golang.org/x/text/unicode/norm golang.org/x/text/width -# golang.org/x/tools v0.1.1 +# golang.org/x/tools v0.1.2 ## explicit golang.org/x/tools/go/ast/astutil golang.org/x/tools/imports From a56e73907858310925a86e7d83f3fc50e43d47a4 Mon Sep 17 00:00:00 2001 From: Sunny Date: Mon, 31 May 2021 20:38:33 +0530 Subject: [PATCH 8/8] registry: Use a test docker config directory When the registry set up script is execited from the go e2e tests, it modified the user's default docker config permissions. Use a test config directory to avoid modifying the default docker configuration file. --- e2e/util/setup-private-registry.sh | 47 +++++++++++++++++------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/e2e/util/setup-private-registry.sh b/e2e/util/setup-private-registry.sh index 3443fcab5..0f83f64d9 100644 --- a/e2e/util/setup-private-registry.sh +++ b/e2e/util/setup-private-registry.sh @@ -38,17 +38,24 @@ openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \ -keyout "${PRIVATE_KEY}" -out "${CERT}" chmod 400 "${PRIVATE_KEY}" +# Use a test config directory to avoid modifying the user's default docker +# configuration. +DOCKER_CONFIG_DIR="$(mktemp -d)/ignite-docker-config" +mkdir -p "${DOCKER_CONFIG_DIR}" + +DOCKER="docker --config=${DOCKER_CONFIG_DIR}" + # Create htpasswd files. -docker run --rm \ +${DOCKER} run --rm \ --entrypoint htpasswd \ httpd:2 -Bbn "${HTTP_USERNAME}" "${HTTP_PASSWORD}" > "${HTTP_REGISTRY_SECRET_PATH}/auth/htpasswd" -docker run --rm \ +${DOCKER} run --rm \ --entrypoint htpasswd \ httpd:2 -Bbn "${HTTPS_USERNAME}" "${HTTPS_PASSWORD}" > "${HTTPS_REGISTRY_SECRET_PATH}/auth/htpasswd" # Run the registries -docker run -d --rm \ +${DOCKER} run -d --rm \ --name ignite-test-http-registry \ -v "${HTTP_REGISTRY_SECRET_PATH}/auth":/auth \ -e REGISTRY_AUTH=htpasswd \ @@ -57,7 +64,7 @@ docker run -d --rm \ -p "${HTTP_ADDR}":5000 \ registry:2 -docker run -d --rm \ +${DOCKER} run -d --rm \ --name ignite-test-https-registry \ -v "${HTTPS_REGISTRY_SECRET_PATH}/auth":/auth \ -v "${HTTPS_REGISTRY_SECRET_PATH}/certs":/certs \ @@ -70,26 +77,26 @@ docker run -d --rm \ registry:2 # Login, push test images to download in tests and logout. -docker pull "${OS_IMG}" -docker pull "${KERNEL_IMG}" +${DOCKER} pull "${OS_IMG}" +${DOCKER} pull "${KERNEL_IMG}" -docker tag "${OS_IMG}" "${HTTP_LOCAL_OS_IMG}" -docker tag "${KERNEL_IMG}" "${HTTP_LOCAL_KERNEL_IMG}" -docker tag "${OS_IMG}" "${HTTPS_LOCAL_OS_IMG}" -docker tag "${KERNEL_IMG}" "${HTTPS_LOCAL_KERNEL_IMG}" +${DOCKER} tag "${OS_IMG}" "${HTTP_LOCAL_OS_IMG}" +${DOCKER} tag "${KERNEL_IMG}" "${HTTP_LOCAL_KERNEL_IMG}" +${DOCKER} tag "${OS_IMG}" "${HTTPS_LOCAL_OS_IMG}" +${DOCKER} tag "${KERNEL_IMG}" "${HTTPS_LOCAL_KERNEL_IMG}" -docker login -u "${HTTP_USERNAME}" -p "${HTTP_PASSWORD}" "https://${HTTP_ADDR}" -docker login -u "${HTTPS_USERNAME}" -p "${HTTPS_PASSWORD}" "https://${HTTPS_ADDR}" +${DOCKER} login -u "${HTTP_USERNAME}" -p "${HTTP_PASSWORD}" "https://${HTTP_ADDR}" +${DOCKER} login -u "${HTTPS_USERNAME}" -p "${HTTPS_PASSWORD}" "https://${HTTPS_ADDR}" # push in parallel, block until all finished -docker push "${HTTP_LOCAL_OS_IMG}" & -docker push "${HTTP_LOCAL_KERNEL_IMG}" & -docker push "${HTTPS_LOCAL_OS_IMG}" & -docker push "${HTTPS_LOCAL_KERNEL_IMG}" & +${DOCKER} push "${HTTP_LOCAL_OS_IMG}" & +${DOCKER} push "${HTTP_LOCAL_KERNEL_IMG}" & +${DOCKER} push "${HTTPS_LOCAL_OS_IMG}" & +${DOCKER} push "${HTTPS_LOCAL_KERNEL_IMG}" & wait -docker logout "http://${HTTP_ADDR}" -docker logout "https://${HTTPS_ADDR}" +${DOCKER} logout "http://${HTTP_ADDR}" +${DOCKER} logout "https://${HTTPS_ADDR}" -docker rmi "${HTTP_LOCAL_OS_IMG}" "${HTTP_LOCAL_KERNEL_IMG}" -docker rmi "${HTTPS_LOCAL_OS_IMG}" "${HTTPS_LOCAL_KERNEL_IMG}" +${DOCKER} rmi "${HTTP_LOCAL_OS_IMG}" "${HTTP_LOCAL_KERNEL_IMG}" +${DOCKER} rmi "${HTTPS_LOCAL_OS_IMG}" "${HTTPS_LOCAL_KERNEL_IMG}"