Repository: getporter/porter
Pull request #1785 (2021-09-30 19:43:04)

Sync operator with porter v1
# What does this change
These are changes necessary to get the Porter Operator updated to work with the latest version of Porter v1

* Log which config file we loaded, since the operator injects it in non-standard spot.
* Print the used config on a separate line. If the file didn't have a newline at the end, the logs were continuing on the same line as the config output.
* Allow specifying some runtime flags (normally specified on porter install) during porter apply: runtime-driver and
allow-docker-host-access.
* Log the runtime driver used

# What issue does it fix
N/A

# Notes for the reviewer
None

# Checklist
- [ ] Unit Tests
- [x] Documentation
- [ ] Schema (porter.yaml)



Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 886c4619..fc37a8f5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -443,6 +443,33 @@ generated by our CLI. You should regenerate that documentation when you change
 any files in **cmd/porter** by running `mage DocsGen` which is run every time
 you run `mage build`.
 
+## Work on the Porter Operator
+
+Instructions for building the Porter Operator from source are located in its repository: https://github.com/getporter/operator.
+Sometimes you may need to make changes to Porter and work on the Operator at the same time.
+Here's how to build porter so that you can use it locally:
+
+1. You must be on a feature branch. Not release/v1 or main. This matters because it affects the generated
+   docker image tag.
+1. Deploy the operator to a KinD cluster by running `mage deploy` from inside the operator repository.
+   That cluster has a local registry running that you can publish to, and it will pull images from it, 
+   running on localhost:5000.
+1. Run the following command from the porter repository to build the Porter Agent image, and publish it
+   to the test cluster's registry. `mage XBuildAll LocalPorterAgentBuild`.
+1. Edit your AgentConfig in the Porter Operator and set it to use your local build of the porter-agent.
+
+```yaml
+apiVersion: porter.sh/v1
+kind: AgentConfig
+metadata:
+  name: porter
+  namespace: test # You may need to change this depending on what you are testing
+spec:
+  porterRepository: localhost:5000/porter-agent
+  porterVersion: canary-dev
+  serviceAccount: porter-agent
+```
+
 # Code structure and practices
 
 Carolyn Van Slyck gave a talk about the design of Porter, [Designing
diff --git a/build/images/agent/run.sh b/build/images/agent/run.sh
index e76dd364..8907a8c2 100755
--- a/build/images/agent/run.sh
+++ b/build/images/agent/run.sh
@@ -8,6 +8,7 @@ if test -n "$(find /porter-config -maxdepth 1 -name 'config.*' -print -quit)"; t
   cp -L /porter-config/config.* /root/.porter/
   ls | grep config.*
   cat /root/.porter/config.*
+  echo ""
 fi
 
 # Print the version of porter we are using for this run
diff --git a/cmd/porter/bundle.go b/cmd/porter/bundle.go
index bc46cbfd..a17dc017 100644
--- a/cmd/porter/bundle.go
+++ b/cmd/porter/bundle.go
@@ -149,7 +149,7 @@ For example, the 'debug' driver may be specified, which simply logs the info giv
 	}
 
 	f := cmd.Flags()
-	f.BoolVar(&opts.AllowAccessToDockerHost, "allow-docker-host-access", false,
+	f.BoolVar(&opts.AllowDockerHostAccess, "allow-docker-host-access", false,
 		"Controls if the bundle should have access to the host's Docker daemon with elevated privileges. See https://porter.sh/configuration/#allow-docker-host-access for the full implications of this flag.")
 	f.StringVarP(&opts.File, "file", "f", "",
 		"Path to the porter manifest file. Defaults to the bundle in the current directory.")
@@ -199,7 +199,7 @@ For example, the 'debug' driver may be specified, which simply logs the info giv
 	}
 
 	f := cmd.Flags()
-	f.BoolVar(&opts.AllowAccessToDockerHost, "allow-docker-host-access", false,
+	f.BoolVar(&opts.AllowDockerHostAccess, "allow-docker-host-access", false,
 		"Controls if the bundle should have access to the host's Docker daemon with elevated privileges. See https://porter.sh/configuration/#allow-docker-host-access for the full implications of this flag.")
 	f.StringVarP(&opts.File, "file", "f", "",
 		"Path to the porter manifest file. Defaults to the bundle in the current directory.")
@@ -250,7 +250,7 @@ For example, the 'debug' driver may be specified, which simply logs the info giv
 	}
 
 	f := cmd.Flags()
-	f.BoolVar(&opts.AllowAccessToDockerHost, "allow-docker-host-access", false,
+	f.BoolVar(&opts.AllowDockerHostAccess, "allow-docker-host-access", false,
 		"Controls if the bundle should have access to the host's Docker daemon with elevated privileges. See https://porter.sh/configuration/#allow-docker-host-access for the full implications of this flag.")
 	f.StringVar(&opts.Action, "action", "",
 		"Custom action name to invoke.")
@@ -303,7 +303,7 @@ For example, the 'debug' driver may be specified, which simply logs the info giv
 	}
 
 	f := cmd.Flags()
-	f.BoolVar(&opts.AllowAccessToDockerHost, "allow-docker-host-access", false,
+	f.BoolVar(&opts.AllowDockerHostAccess, "allow-docker-host-access", false,
 		"Controls if the bundle should have access to the host's Docker daemon with elevated privileges. See https://porter.sh/configuration/#allow-docker-host-access for the full implications of this flag.")
 	f.StringVarP(&opts.File, "file", "f", "",
 		"Path to the porter manifest file. Defaults to the bundle in the current directory. Optional unless a newer version of the bundle should be used to uninstall the bundle.")
diff --git a/docs/content/cli/mixins_create.md b/docs/content/cli/mixins_create.md
index 9a269fea..fed41ee9 100644
--- a/docs/content/cli/mixins_create.md
+++ b/docs/content/cli/mixins_create.md
@@ -41,8 +41,9 @@ porter mixins create NAME --author "My Name" --username mygithubusername [--dir
 ### Options inherited from parent commands
 
 ```
-      --debug           Enable debug logging
-      --debug-plugins   Enable plugin debug logging
+      --debug                  Enable debug logging
+      --debug-plugins          Enable plugin debug logging
+      --experimental strings   Comma separated list of experimental features to enable. See https://porter.sh/configuration/#experimental-feature-flags for available feature flags.
 ```
 
 ### SEE ALSO
diff --git a/magefile.go b/magefile.go
index e206268c..d05989d0 100644
--- a/magefile.go
+++ b/magefile.go
@@ -262,13 +262,24 @@ func PublishImages() {
 	}
 }
 
+// Builds the porter-agent image and publishes it to a local test cluster with the Porter Operator.
+func LocalPorterAgentBuild() {
+	// Publish to the local registry/cluster setup by the Porter Operator.
+	os.Setenv("REGISTRY", "localhost:5000")
+	// Force the image to be pushed to the registry even though it's a local dev build.
+	os.Setenv("PORTER_FORCE_PUBLISH", "true")
+
+	mg.SerialDeps(XBuildPorter, PublishImages)
+}
+
 // Only push tagged versions, canary and latest
 func pushImagesTo(registry string, info mage.GitMetadata) {
 	if info.IsTaggedRelease {
 		pushImages(registry, info.Version)
 	}
 
-	if info.ShouldPublishPermalink() {
+	force, _ := strconv.ParseBool(os.Getenv("PORTER_FORCE_PUBLISH"))
+	if info.ShouldPublishPermalink() || force {
 		pushImages(registry, info.Permalink)
 	} else {
 		fmt.Println("Skipping image publish for permalink", info.Permalink)
diff --git a/pkg/cnab/provider/action.go b/pkg/cnab/provider/action.go
index a57ccc57..82bdf224 100644
--- a/pkg/cnab/provider/action.go
+++ b/pkg/cnab/provider/action.go
@@ -138,6 +138,7 @@ func (r *Runtime) Execute(args ActionArguments) error {
 		return errors.Wrap(err, "could not load credentials")
 	}
 
+	fmt.Fprintf(r.Err, "Using runtime driver %s\n", args.Driver)
 	driver, err := r.newDriver(args.Driver, args)
 	if err != nil {
 		return errors.Wrap(err, "unable to instantiate driver")
diff --git a/pkg/config/datastore.go b/pkg/config/datastore.go
index 163c926f..1042a70e 100644
--- a/pkg/config/datastore.go
+++ b/pkg/config/datastore.go
@@ -15,6 +15,15 @@ type Data struct {
 	// Available values are: docker, buildkit.
 	BuildDriver string `mapstructure:"build-driver"`
 
+	// RuntimeDriver is the driver to use when executing bundles.
+	// Available values are: docker, kubernetes.
+	RuntimeDriver string `mapstructure:"runtime-driver"`
+
+	// AllowDockerHostAccess grants bundles access to the underlying docker host
+	// upon which it is running so that it can do things like build and run containers.
+	// It's a security risk.
+	AllowDockerHostAccess bool `mapstructure:"allow-docker-host-access"`
+
 	// DefaultStoragePlugin is the storage plugin to use when no named storage is specified.
 	DefaultStoragePlugin string `mapstructure:"default-storage-plugin"`
 
diff --git a/pkg/config/loader.go b/pkg/config/loader.go
index 945e550b..9eb65494 100644
--- a/pkg/config/loader.go
+++ b/pkg/config/loader.go
@@ -49,7 +49,7 @@ func LoadFromViper(viperCfg func(v *viper.Viper)) DataStoreLoaderFunc {
 		// Try to read config
 		v.AddConfigPath(home)
 		if cfg.Debug {
-			fmt.Fprintln(cfg.Err, "reading Porter config from", home)
+			fmt.Fprintln(cfg.Err, "detecting Porter config in", home)
 		}
 		err = v.ReadInConfig()
 		if err != nil {
@@ -58,6 +58,9 @@ func LoadFromViper(viperCfg func(v *viper.Viper)) DataStoreLoaderFunc {
 			}
 		}
 
+		if cfg.Debug {
+			fmt.Fprintln(cfg.Err, "loaded Porter config from", v.ConfigFileUsed())
+		}
 		err = v.Unmarshal(&cfg.Data)
 		return errors.Wrapf(err, "error unmarshaling config at %q", v.ConfigFileUsed())
 	}
diff --git a/pkg/porter/dependencies.go b/pkg/porter/dependencies.go
index 56061281..abda563f 100644
--- a/pkg/porter/dependencies.go
+++ b/pkg/porter/dependencies.go
@@ -273,7 +273,7 @@ func (e *dependencyExecutioner) executeDependency(dep *queuedDependency) error {
 		Action:                e.parentArgs.Action,
 		Installation:          depInstallation,
 		Driver:                e.parentArgs.Driver,
-		AllowDockerHostAccess: e.parentOpts.AllowAccessToDockerHost,
+		AllowDockerHostAccess: e.parentOpts.AllowDockerHostAccess,
 		Params:                dep.Parameters,
 	}
 
diff --git a/pkg/porter/lifecycle.go b/pkg/porter/lifecycle.go
index c99e3945..70307669 100644
--- a/pkg/porter/lifecycle.go
+++ b/pkg/porter/lifecycle.go
@@ -34,7 +34,7 @@ type BundleAction interface {
 type BundleActionOptions struct {
 	sharedOptions
 	BundlePullOptions
-	AllowAccessToDockerHost bool
+	AllowDockerHostAccess bool
 
 	bundleRef *cnab.BundleReference
 }
@@ -53,6 +53,18 @@ func (o *BundleActionOptions) Validate(args []string, porter *Porter) error {
 		}
 	}
 
+	// When you run porter installation apply, there are some settings from porter install
+	// that aren't exposed as flags. This allows the user to set them in the config file
+	// and we will use them before running the bundle.
+	if o.Driver == "" {
+		// We have both porter build --driver, and porter install --driver
+		// So in the config file it's named build-driver and runtime-driver
+		// This is why we check first before applying the value. Only apply the config
+		// file setting if they didn't specify a flag.
+		o.Driver = porter.Config.Data.RuntimeDriver
+	}
+	o.AllowDockerHostAccess = porter.Config.Data.AllowDockerHostAccess
+
 	err = o.sharedOptions.Validate(args, porter)
 	if err != nil {
 		return err
@@ -194,7 +206,7 @@ func (p *Porter) BuildActionArgs(installation claims.Installation, action Bundle
 		BundleReference:       bundleRef,
 		Params:                resolvedParams,
 		Driver:                opts.Driver,
-		AllowDockerHostAccess: opts.AllowAccessToDockerHost,
+		AllowDockerHostAccess: opts.AllowDockerHostAccess,
 	}
 
 	return args, nil


Test patch:
diff --git a/pkg/porter/lifecycle_test.go b/pkg/porter/lifecycle_test.go
index af6191cf..d4e89a75 100644
--- a/pkg/porter/lifecycle_test.go
+++ b/pkg/porter/lifecycle_test.go
@@ -101,7 +101,7 @@ func TestPorter_BuildActionArgs(t *testing.T) {
 					},
 					Driver: "docker",
 				},
-				AllowAccessToDockerHost: true,
+				AllowDockerHostAccess: true,
 			},
 		}
 		p.TestParameters.TestSecrets.AddSecret("PARAM2_SECRET", "VALUE2")
@@ -120,7 +120,7 @@ func TestPorter_BuildActionArgs(t *testing.T) {
 			"porter-state":    nil,
 		}
 
-		assert.Equal(t, opts.AllowAccessToDockerHost, args.AllowDockerHostAccess, "AllowDockerHostAccess not populated correctly")
+		assert.Equal(t, opts.AllowDockerHostAccess, args.AllowDockerHostAccess, "AllowDockerHostAccess not populated correctly")
 		assert.Equal(t, opts.Driver, args.Driver, "Driver not populated correctly")
 		assert.EqualValues(t, expectedParams, args.Params, "Params not populated correctly")
 		assert.Equal(t, existingInstall, args.Installation, "Installation not populated correctly")
@@ -148,3 +148,41 @@ func TestManifestIgnoredWithTag(t *testing.T) {
 		require.NoError(t, err, "Validate failed")
 	})
 }
+
+func TestBundleActionOptions_Validate(t *testing.T) {
+	t.Run("allow docker host access", func(t *testing.T) {
+		p := NewTestPorter(t)
+		p.DataLoader = config.LoadFromEnvironment()
+		p.FileSystem.WriteFile("/root/.porter/config.yaml", []byte("allow-docker-host-access: true"), 0600)
+		require.NoError(t, p.LoadData())
+
+		opts := NewInstallOptions()
+		opts.Reference = "getporter/porter-hello:v0.1.1"
+		require.NoError(t, opts.Validate(nil, p.Porter))
+		assert.True(t, opts.AllowDockerHostAccess)
+	})
+
+	t.Run("driver flag unset", func(t *testing.T) {
+		p := NewTestPorter(t)
+		p.DataLoader = config.LoadFromEnvironment()
+		p.FileSystem.WriteFile("/root/.porter/config.yaml", []byte("runtime-driver: kubernetes"), 0600)
+		require.NoError(t, p.LoadData())
+
+		opts := NewInstallOptions()
+		opts.Reference = "getporter/porter-hello:v0.1.1"
+		require.NoError(t, opts.Validate(nil, p.Porter))
+		assert.Equal(t, "kubernetes", opts.Driver)
+	})
+	t.Run("driver flag set", func(t *testing.T) {
+		p := NewTestPorter(t)
+		p.DataLoader = config.LoadFromEnvironment()
+		p.FileSystem.WriteFile("/root/.porter/config.yaml", []byte("driver: kubernetes"), 0600)
+		require.NoError(t, p.LoadData())
+
+		opts := NewInstallOptions()
+		opts.Driver = "docker"
+		opts.Reference = "getporter/porter-hello:v0.1.1"
+		require.NoError(t, opts.Validate(nil, p.Porter))
+		assert.Equal(t, "docker", opts.Driver)
+	})
+}