Add a setting for kustomize plugins

This adds an env var-driven configuration for the filesystem path
in which kustomize should expect its plugins (including airshipctl)
to be.  The value defaults to a subfolder off the `.airshipit` folder.

The config is persisted as a singleton rather than a member of
AirshipCTLSettings (or the Config) because
1. the settings object would have had to have been passed around
   a couple dozen additional files/functions,
2. it's reasonable to expect the plugin location to be consistent
   across threads in a multi-threaded, airshipctl-as-library context.

Settings_test.go was moved in to an environment_test package to avoid
a circular import dependency.

Change-Id: Icdd21bd3687ef42492e388af982d7b490af3eff3
This commit is contained in:
Matt McEuen 2020-05-14 14:42:43 -05:00
parent aab8325af5
commit 4e64e74b6f
4 changed files with 45 additions and 7 deletions

View File

@ -51,6 +51,8 @@ const (
AirshipDefaultManifestRepoLocation = "https://opendev.org/airship/" + AirshipDefaultManifestRepo AirshipDefaultManifestRepoLocation = "https://opendev.org/airship/" + AirshipDefaultManifestRepo
AirshipKubeConfig = "kubeconfig" AirshipKubeConfig = "kubeconfig"
AirshipKubeConfigEnv = "AIRSHIP_KUBECONFIG" AirshipKubeConfigEnv = "AIRSHIP_KUBECONFIG"
AirshipPluginPath = "kustomize-plugins"
AirshipPluginPathEnv = "AIRSHIP_KUSTOMIZE_PLUGINS"
// Modules // Modules
AirshipDefaultBootstrapImage = "quay.io/airshipit/isogen:latest-debian_stable" AirshipDefaultBootstrapImage = "quay.io/airshipit/isogen:latest-debian_stable"

View File

@ -22,6 +22,7 @@ import (
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"opendev.org/airship/airshipctl/pkg/environment"
utilyaml "opendev.org/airship/airshipctl/pkg/util/yaml" utilyaml "opendev.org/airship/airshipctl/pkg/util/yaml"
) )
@ -85,7 +86,7 @@ func NewBundle(fSys FileSystem, kustomizePath string) (Bundle, error) {
LoadRestrictions: options.LoadRestrictions, LoadRestrictions: options.LoadRestrictions,
DoPrune: false, // Default DoPrune: false, // Default
PluginConfig: &types.PluginConfig{ PluginConfig: &types.PluginConfig{
AbsPluginHome: kustomizePath, AbsPluginHome: environment.PluginPath(),
PluginRestrictions: types.PluginRestrictionsNone, PluginRestrictions: types.PluginRestrictionsNone,
}, },
} }

View File

@ -17,6 +17,7 @@ package environment
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"sync"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -35,6 +36,10 @@ type AirshipCTLSettings struct {
Config *config.Config Config *config.Config
} }
// A singleton for the kustomize plugin path configuration
var pluginPath string
var pluginPathLock = &sync.Mutex{}
// InitFlags adds the default settings flags to cmd // InitFlags adds the default settings flags to cmd
func (a *AirshipCTLSettings) InitFlags(cmd *cobra.Command) { func (a *AirshipCTLSettings) InitFlags(cmd *cobra.Command) {
flags := cmd.PersistentFlags() flags := cmd.PersistentFlags()
@ -67,6 +72,7 @@ func (a *AirshipCTLSettings) InitConfig() {
a.initAirshipConfigPath() a.initAirshipConfigPath()
a.initKubeConfigPath() a.initKubeConfigPath()
initPluginPath()
err := a.Config.LoadConfig(a.AirshipConfigPath, a.KubeConfigPath) err := a.Config.LoadConfig(a.AirshipConfigPath, a.KubeConfigPath)
if err != nil { if err != nil {
@ -116,6 +122,28 @@ func (a *AirshipCTLSettings) initKubeConfigPath() {
a.KubeConfigPath = filepath.Join(homeDir, config.AirshipConfigDir, config.AirshipKubeConfig) a.KubeConfigPath = filepath.Join(homeDir, config.AirshipConfigDir, config.AirshipKubeConfig)
} }
// Sets the location to look for kustomize plugins (including airshipctl itself).
func initPluginPath() {
pluginPathLock.Lock()
defer pluginPathLock.Unlock()
// Check if we got the path via ENVIRONMENT variable
pluginPath = os.Getenv(config.AirshipPluginPathEnv)
if pluginPath != "" {
return
}
// Otherwise, we'll try putting it in the home directory
homeDir := userHomeDir()
pluginPath = filepath.Join(homeDir, config.AirshipConfigDir, config.AirshipPluginPath)
}
func PluginPath() string {
pluginPathLock.Lock()
defer pluginPathLock.Unlock()
return pluginPath
}
// userHomeDir is a utility function that wraps os.UserHomeDir and returns no // userHomeDir is a utility function that wraps os.UserHomeDir and returns no
// errors. If the user has no home directory, the returned value will be the // errors. If the user has no home directory, the returned value will be the
// empty string // empty string

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package environment package environment_test
import ( import (
"os" "os"
@ -25,12 +25,13 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"opendev.org/airship/airshipctl/pkg/config" "opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/testutil" "opendev.org/airship/airshipctl/testutil"
) )
func TestInitFlags(t *testing.T) { func TestInitFlags(t *testing.T) {
// Get the Environment // Get the Environment
settings := &AirshipCTLSettings{} settings := &environment.AirshipCTLSettings{}
testCmd := &cobra.Command{} testCmd := &cobra.Command{}
settings.InitFlags(testCmd) settings.InitFlags(testCmd)
assert.True(t, testCmd.HasPersistentFlags()) assert.True(t, testCmd.HasPersistentFlags())
@ -43,13 +44,15 @@ func TestInitConfig(t *testing.T) {
defer cleanup(t) defer cleanup(t)
defer setHome(testDir)() defer setHome(testDir)()
var testSettings AirshipCTLSettings var testSettings environment.AirshipCTLSettings
expectedAirshipConfig := filepath.Join(testDir, config.AirshipConfigDir, config.AirshipConfig) expectedAirshipConfig := filepath.Join(testDir, config.AirshipConfigDir, config.AirshipConfig)
expectedKubeConfig := filepath.Join(testDir, config.AirshipConfigDir, config.AirshipKubeConfig) expectedKubeConfig := filepath.Join(testDir, config.AirshipConfigDir, config.AirshipKubeConfig)
expectedPluginPath := filepath.Join(testDir, config.AirshipConfigDir, config.AirshipPluginPath)
testSettings.InitConfig() testSettings.InitConfig()
assert.Equal(t, expectedAirshipConfig, testSettings.AirshipConfigPath) assert.Equal(t, expectedAirshipConfig, testSettings.AirshipConfigPath)
assert.Equal(t, expectedKubeConfig, testSettings.KubeConfigPath) assert.Equal(t, expectedKubeConfig, testSettings.KubeConfigPath)
assert.Equal(t, expectedPluginPath, environment.PluginPath())
}) })
t.Run("PreferEnvToDefault", func(subTest *testing.T) { t.Run("PreferEnvToDefault", func(subTest *testing.T) {
@ -58,18 +61,22 @@ func TestInitConfig(t *testing.T) {
defer cleanup(t) defer cleanup(t)
defer setHome(testDir)() defer setHome(testDir)()
var testSettings AirshipCTLSettings var testSettings environment.AirshipCTLSettings
expectedAirshipConfig := filepath.Join(testDir, "airshipEnv") expectedAirshipConfig := filepath.Join(testDir, "airshipEnv")
expectedKubeConfig := filepath.Join(testDir, "kubeEnv") expectedKubeConfig := filepath.Join(testDir, "kubeEnv")
expectedPluginPath := filepath.Join(testDir, "pluginPath")
os.Setenv(config.AirshipConfigEnv, expectedAirshipConfig) os.Setenv(config.AirshipConfigEnv, expectedAirshipConfig)
os.Setenv(config.AirshipKubeConfigEnv, expectedKubeConfig) os.Setenv(config.AirshipKubeConfigEnv, expectedKubeConfig)
os.Setenv(config.AirshipPluginPathEnv, expectedPluginPath)
defer os.Unsetenv(config.AirshipConfigEnv) defer os.Unsetenv(config.AirshipConfigEnv)
defer os.Unsetenv(config.AirshipKubeConfigEnv) defer os.Unsetenv(config.AirshipKubeConfigEnv)
defer os.Unsetenv(config.AirshipPluginPathEnv)
testSettings.InitConfig() testSettings.InitConfig()
assert.Equal(t, expectedAirshipConfig, testSettings.AirshipConfigPath) assert.Equal(t, expectedAirshipConfig, testSettings.AirshipConfigPath)
assert.Equal(t, expectedKubeConfig, testSettings.KubeConfigPath) assert.Equal(t, expectedKubeConfig, testSettings.KubeConfigPath)
assert.Equal(t, expectedPluginPath, environment.PluginPath())
}) })
t.Run("PreferCmdLineArgToDefault", func(subTest *testing.T) { t.Run("PreferCmdLineArgToDefault", func(subTest *testing.T) {
@ -81,7 +88,7 @@ func TestInitConfig(t *testing.T) {
expectedAirshipConfig := filepath.Join(testDir, "airshipCmdLine") expectedAirshipConfig := filepath.Join(testDir, "airshipCmdLine")
expectedKubeConfig := filepath.Join(testDir, "kubeCmdLine") expectedKubeConfig := filepath.Join(testDir, "kubeCmdLine")
testSettings := AirshipCTLSettings{ testSettings := environment.AirshipCTLSettings{
AirshipConfigPath: expectedAirshipConfig, AirshipConfigPath: expectedAirshipConfig,
KubeConfigPath: expectedKubeConfig, KubeConfigPath: expectedKubeConfig,
} }
@ -111,7 +118,7 @@ func TestInitConfig(t *testing.T) {
defer os.Unsetenv(config.AirshipConfigEnv) defer os.Unsetenv(config.AirshipConfigEnv)
defer os.Unsetenv(config.AirshipKubeConfigEnv) defer os.Unsetenv(config.AirshipKubeConfigEnv)
testSettings := AirshipCTLSettings{ testSettings := environment.AirshipCTLSettings{
AirshipConfigPath: expectedAirshipConfig, AirshipConfigPath: expectedAirshipConfig,
KubeConfigPath: expectedKubeConfig, KubeConfigPath: expectedKubeConfig,
} }