Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

molecule: instace completion #2339

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions completers/molecule_completer/cmd/action/host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package action

import (
"os"
"path"
"time"

"github.com/carapace-sh/carapace"
"github.com/carapace-sh/carapace-bin/pkg/actions/net"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
)

// ActionInstances completes known host names and known molecule instance hosts
func ActionInstances(cmd *cobra.Command) carapace.Action {
return carapace.Batch(
net.ActionHosts(),
carapace.ActionValuesDescribed(getInstances(cmd)...).Cache(time.Minute),
).ToA()
}

// Get active instances
func getInstances(cmd *cobra.Command) []string {
ephemeralPath := getMoleculeEphemeralDir(cmd)

cwd, err := os.Getwd()
if err != nil {
return []string{}
}
roleName := path.Base(cwd)

scenarioName, err := cmd.Flags().GetString("scenario-name")
if err != nil {
scenarioName = "default"
}

inventoryPath := path.Join(ephemeralPath, roleName, scenarioName, "inventory", "ansible_inventory.yml")
inventoryData, err := os.ReadFile(inventoryPath)
if err != nil {
return []string{}
}

var inventoryMap struct {
all struct {
hosts map[string]struct {
ansibleHost string `yaml:"ansible_host"`
}
}
}
if err := yaml.Unmarshal(inventoryData, &inventoryMap); err != nil {
return []string{}
}

inventory := make([]string, 0)
for host, hostData := range inventoryMap.all.hosts {
inventory = append(inventory, hostData.ansibleHost, host)
}

return inventory
}

// Find the location molecule is storing ephemeral data
func getMoleculeEphemeralDir(cmd *cobra.Command) string {
// Default to the platform equivalent to "$HOME" joined with .cache/molecule"
// (molecule does not support windows)
ephemeralPath, err := os.UserHomeDir()
if err != nil {
return ""
}
ephemeralPath = path.Join(ephemeralPath, ".cache", "molecule")

// Check for more specific cache directory environment variable
if cacheHome, ok := os.LookupEnv("XDG_CACHE_HOME"); ok {
ephemeralPath = path.Join(cacheHome, "molecule")
}

// Check for specific molecule environment variable
if moleculeEphemeral, ok := os.LookupEnv("MOLECULE_EPHEMERAL_DIRECTORY"); ok {
ephemeralPath = moleculeEphemeral
}

// Check for path specified in molecule .env.yml file
// GetString defaults to the correct value (.env.yml)
envFilePath, err := cmd.Flags().GetString("env-file")
if err != nil {
envFilePath = ".env.yml"
}

envData, err := os.ReadFile(envFilePath)
if err != nil {
return ephemeralPath
}

envMap := make(map[string]any)
if err := yaml.Unmarshal(envData, envMap); err != nil {
return ephemeralPath
}

if moleculeEphemeral, ok := envMap["MOLECULE_EPHEMERAL_DIRECTORY"]; ok {
if s, ok := moleculeEphemeral.(string); ok {
ephemeralPath = s
}
}

return ephemeralPath
}
3 changes: 2 additions & 1 deletion completers/molecule_completer/cmd/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"github.com/carapace-sh/carapace"
"github.com/carapace-sh/carapace-bin/completers/molecule_completer/cmd/action"
"github.com/carapace-sh/carapace-bin/pkg/actions/tools/molecule"
"github.com/spf13/cobra"
)
Expand All @@ -20,7 +21,7 @@ func init() {
loginCmd.Flags().StringP("scenario-name", "s", "default", "Name of the scenario to target")

carapace.Gen(loginCmd).FlagCompletion(carapace.ActionMap{
// "host": action.ActionInstances(loginCmd), // TODO
"host": action.ActionInstances(loginCmd),
"scenario-name": molecule.ActionScenarios(),
})

Expand Down
5 changes: 5 additions & 0 deletions completers/molecule_completer/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ func init() {
rootCmd.Flags().BoolSliceP("verbose", "v", nil, "Increase Ansible verbosity level")

rootCmd.MarkFlagsMutuallyExclusive("debug", "no-debug")

carapace.Gen(rootCmd).FlagCompletion(carapace.ActionMap{
"base-config": carapace.ActionFiles(),
"env-file": carapace.ActionFiles(),
})
}
96 changes: 96 additions & 0 deletions pkg/actions/tools/molecule/host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package molecule

import (
"os"
"path"

"github.com/carapace-sh/carapace"
"github.com/carapace-sh/carapace/pkg/traverse"
"github.com/carapace-sh/carapace/pkg/xdg"
"gopkg.in/yaml.v3"
)

type InstanceOpts struct {
Scenario string
EnvFile string
}

// ActionInstances completes known host names and known molecule instance hosts
func ActionInstances(opts InstanceOpts) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
ephemeralPath := getMoleculeEphemeralDir(opts.EnvFile)
roleName := path.Base(c.Dir)

if opts.Scenario == "" {
opts.Scenario = "default"
}

inventoryPath := path.Join(ephemeralPath, roleName, opts.Scenario, "inventory", "ansible_inventory.yml")
inventoryData, err := os.ReadFile(inventoryPath)
if err != nil {
return carapace.ActionMessage(err.Error())
}

var inventoryMap struct {
all struct {
hosts map[string]struct {
ansibleHost string `yaml:"ansible_host"`
}
}
}
if err := yaml.Unmarshal(inventoryData, &inventoryMap); err != nil {
return carapace.ActionMessage(err.Error())
}

inventory := make([]string, 0)
for host, hostData := range inventoryMap.all.hosts {
inventory = append(inventory, hostData.ansibleHost, host)
}

return carapace.ActionValues(inventory...)
})
}

func traverseEphemeralDir(envFilePath string) func(tc traverse.Context) (string, error) {

Check failure on line 54 in pkg/actions/tools/molecule/host.go

View workflow job for this annotation

GitHub Actions / build

func traverseEphemeralDir is unused (U1000)

Check failure on line 54 in pkg/actions/tools/molecule/host.go

View workflow job for this annotation

GitHub Actions / build

func traverseEphemeralDir is unused (U1000)
return func(tc traverse.Context) (string, error) {
return "", nil // TODO
}
}

// Find the location molecule is storing ephemeral data
func getMoleculeEphemeralDir(envFilePath string) string {
cacheDir, err := xdg.UserCacheDir()
if err != nil {
return "" // TODO error
}
ephemeralPath := path.Join(cacheDir, "molecule")

// Check for specific molecule environment variable
if moleculeEphemeral, ok := os.LookupEnv("MOLECULE_EPHEMERAL_DIRECTORY"); ok {
ephemeralPath = moleculeEphemeral
}

// Check for path specified in molecule .env.yml file
// GetString defaults to the correct value (.env.yml)
if envFilePath == "" {
envFilePath = ".env.yml"
}

envData, err := os.ReadFile(envFilePath)
if err != nil {
return ephemeralPath
}

envMap := make(map[string]any)
if err := yaml.Unmarshal(envData, envMap); err != nil {
return ephemeralPath
}

if moleculeEphemeral, ok := envMap["MOLECULE_EPHEMERAL_DIRECTORY"]; ok {
if s, ok := moleculeEphemeral.(string); ok {
ephemeralPath = s
}
}

return ephemeralPath
}
Loading