Skip to content

Commit

Permalink
molecule: instance completion
Browse files Browse the repository at this point in the history
  • Loading branch information
rsteube committed Apr 8, 2024
1 parent 4809423 commit 83ee2e1
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 1 deletion.
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
95 changes: 95 additions & 0 deletions pkg/actions/tools/molecule/host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package molecule

import (
"os"
"path"

"github.com/carapace-sh/carapace"
"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...)
})
}

// Find the location molecule is storing ephemeral data
func getMoleculeEphemeralDir(envFilePath string) 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)
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
}

0 comments on commit 83ee2e1

Please sign in to comment.