Skip to content

Commit

Permalink
Added ansible molecule completer
Browse files Browse the repository at this point in the history
  • Loading branch information
aftix authored and rsteube committed Apr 7, 2024
1 parent 9f715de commit 09d010d
Show file tree
Hide file tree
Showing 25 changed files with 770 additions and 0 deletions.
22 changes: 22 additions & 0 deletions completers/molecule_completer/cmd/action/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package action

import (
"strings"

"github.com/carapace-sh/carapace"
)

// ActionDrivers completes molecule drivers
func ActionDrivers() carapace.Action {
return carapace.ActionExecCommand("molecule", "drivers", "-f", "plain")(func(output []byte) carapace.Action {
lines := strings.Split(string(output), "\n")

drivers := make([]string, 0)
for _, driver := range lines {
if driver != "" {
drivers = append(drivers, driver)
}
}
return carapace.ActionValues(drivers...)
})
}
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
}
28 changes: 28 additions & 0 deletions completers/molecule_completer/cmd/action/scenario.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package action

import (
"os"
"strings"

"github.com/carapace-sh/carapace"
"github.com/carapace-sh/carapace/pkg/style"
)

// ActionScenarios completes molecule scenarios
// Does not use ActionDirectories since this completion is not recursive
func ActionScenarios() carapace.Action {
moleculeFiles, err := os.ReadDir("molecule")
if err != nil {
return carapace.ActionValues()
}

scenarios := make([]string, 0)
for _, entry := range moleculeFiles {
if !entry.IsDir() && strings.HasPrefix(".", entry.Name()) {
continue
}
scenarios = append(scenarios, strings.TrimSuffix("/", entry.Name()))
}

return carapace.ActionValues(scenarios...).StyleF(style.ForPath).Tag("scenarios")
}
30 changes: 30 additions & 0 deletions completers/molecule_completer/cmd/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package cmd

import (
"github.com/carapace-sh/carapace"
"github.com/carapace-sh/carapace-bin/completers/molecule_completer/cmd/action"
"github.com/spf13/cobra"
)

var checkCmd = &cobra.Command{
Use: "check [flags]",
Short: "Use the provisioner to perform a Dry-Run",
Run: func(cmd *cobra.Command, args []string) {},
}

func init() {
carapace.Gen(checkCmd)

checkCmd.Flags().Bool("help", false, "Show help message and exit")
checkCmd.Flags().Bool("no-parallel", true, "Disable parallel mode (default)")
checkCmd.Flags().Bool("parallel", false, "Enabe parallel mode")
checkCmd.Flags().StringP("scenario-name", "s", "default", "Name of the scenario to target")

checkCmd.MarkFlagsMutuallyExclusive("parallel", "no-parallel")

carapace.Gen(checkCmd).FlagCompletion(carapace.ActionMap{
"scenario-name": action.ActionScenarios(),
})

rootCmd.AddCommand(checkCmd)
}
26 changes: 26 additions & 0 deletions completers/molecule_completer/cmd/cleanup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cmd

import (
"github.com/carapace-sh/carapace"
"github.com/carapace-sh/carapace-bin/completers/molecule_completer/cmd/action"
"github.com/spf13/cobra"
)

var cleanupCmd = &cobra.Command{
Use: "cleanup [flags]",
Short: "Use the provisioner to cleanup any changes made to external systems",
Run: func(cmd *cobra.Command, args []string) {},
}

func init() {
carapace.Gen(cleanupCmd)

cleanupCmd.Flags().Bool("help", false, "Show help message and exit")
cleanupCmd.Flags().StringP("scenario-name", "s", "default", "Name of the scenario to target")

carapace.Gen(cleanupCmd).FlagCompletion(carapace.ActionMap{
"scenario-name": action.ActionScenarios(),
})

rootCmd.AddCommand(cleanupCmd)
}
26 changes: 26 additions & 0 deletions completers/molecule_completer/cmd/converge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cmd

import (
"github.com/carapace-sh/carapace"
"github.com/carapace-sh/carapace-bin/completers/molecule_completer/cmd/action"
"github.com/spf13/cobra"
)

var convergeCmd = &cobra.Command{
Use: "converge [flags]",
Short: "Use the provisioner to configure instances",
Run: func(cmd *cobra.Command, args []string) {},
}

func init() {
carapace.Gen(convergeCmd)

convergeCmd.Flags().Bool("help", false, "Show help message and exit")
convergeCmd.Flags().StringP("scenario-name", "s", "default", "Name of the scenario to target")

carapace.Gen(convergeCmd).FlagCompletion(carapace.ActionMap{
"scenario-name": action.ActionScenarios(),
})

rootCmd.AddCommand(convergeCmd)
}
28 changes: 28 additions & 0 deletions completers/molecule_completer/cmd/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package cmd

import (
"github.com/carapace-sh/carapace"
"github.com/carapace-sh/carapace-bin/completers/molecule_completer/cmd/action"
"github.com/spf13/cobra"
)

var createCmd = &cobra.Command{
Use: "create [flags]",
Short: "Use the provisioner to start the instances",
Run: func(cmd *cobra.Command, args []string) {},
}

func init() {
carapace.Gen(createCmd)

createCmd.Flags().StringP("driver-name", "d", "delegated", "Name of the driver to use")
createCmd.Flags().Bool("help", false, "Show help message and exit")
createCmd.Flags().StringP("scenario-name", "s", "default", "Name of the scenario to target")

carapace.Gen(createCmd).FlagCompletion(carapace.ActionMap{
"driver-name": action.ActionDrivers(),
"scenario-name": action.ActionScenarios(),
})

rootCmd.AddCommand(createCmd)
}
26 changes: 26 additions & 0 deletions completers/molecule_completer/cmd/dependency.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cmd

import (
"github.com/carapace-sh/carapace"
"github.com/carapace-sh/carapace-bin/completers/molecule_completer/cmd/action"
"github.com/spf13/cobra"
)

var dependencyCmd = &cobra.Command{
Use: "dependency [flags]",
Short: "Manage the role's dependencies",
Run: func(cmd *cobra.Command, args []string) {},
}

func init() {
carapace.Gen(dependencyCmd)

dependencyCmd.Flags().Bool("help", false, "Show help message and exit")
dependencyCmd.Flags().StringP("scenario-name", "s", "default", "Name of the scenario to target")

carapace.Gen(dependencyCmd).FlagCompletion(carapace.ActionMap{
"scenario-name": action.ActionScenarios(),
})

rootCmd.AddCommand(dependencyCmd)
}
36 changes: 36 additions & 0 deletions completers/molecule_completer/cmd/destroy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cmd

import (
"github.com/carapace-sh/carapace"
"github.com/carapace-sh/carapace-bin/completers/molecule_completer/cmd/action"
"github.com/spf13/cobra"
)

var destroyCmd = &cobra.Command{
Use: "destroy [flags]",
Short: "Use the provisioner to destroy the instances",
Run: func(cmd *cobra.Command, args []string) {},
}

func init() {
carapace.Gen(destroyCmd)

destroyCmd.Flags().Bool("all", false, "Destroy all scenarios")
destroyCmd.Flags().StringP("driver-name", "d", "delegated", "Name of the driver to use")
destroyCmd.Flags().Bool("help", false, "Show help message and exit")
destroyCmd.Flags().Bool("no-all", true, "Don't destroy all scenarios (default)")
destroyCmd.Flags().Bool("no-parallel", true, "Disable parallel mode (default)")
destroyCmd.Flags().Bool("parallel", false, "Enabe parallel mode")
destroyCmd.Flags().StringP("scenario-name", "s", "default", "Name of the scenario to target")

destroyCmd.MarkFlagsMutuallyExclusive("all", "scenario-name")
destroyCmd.MarkFlagsMutuallyExclusive("all", "no-all")
destroyCmd.MarkFlagsMutuallyExclusive("parallel", "no-parallel")

carapace.Gen(destroyCmd).FlagCompletion(carapace.ActionMap{
"driver-name": action.ActionDrivers(),
"scenario-name": action.ActionScenarios(),
})

rootCmd.AddCommand(destroyCmd)
}
25 changes: 25 additions & 0 deletions completers/molecule_completer/cmd/drivers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package cmd

import (
"github.com/carapace-sh/carapace"
"github.com/spf13/cobra"
)

var driversCmd = &cobra.Command{
Use: "drivers [flags]",
Short: "List drivers",
Run: func(cmd *cobra.Command, args []string) {},
}

func init() {
carapace.Gen(driversCmd)

driversCmd.Flags().StringP("format", "f", "plain", "Change output format")
driversCmd.Flags().Bool("help", false, "Show help message and exit")

carapace.Gen(driversCmd).FlagCompletion(carapace.ActionMap{
"format": carapace.ActionValues("plain", "simple"),
})

rootCmd.AddCommand(driversCmd)
}
26 changes: 26 additions & 0 deletions completers/molecule_completer/cmd/idempotence.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cmd

import (
"github.com/carapace-sh/carapace"
"github.com/carapace-sh/carapace-bin/completers/molecule_completer/cmd/action"
"github.com/spf13/cobra"
)

var idempotenceCmd = &cobra.Command{
Use: "idempotence [flags]",
Short: "Use the provisioner to configure instances and determine idempotence",
Run: func(cmd *cobra.Command, args []string) {},
}

func init() {
carapace.Gen(idempotenceCmd)

idempotenceCmd.Flags().Bool("help", false, "Show help message and exit")
idempotenceCmd.Flags().StringP("scenario-name", "s", "default", "Name of the scenario to target")

carapace.Gen(idempotenceCmd).FlagCompletion(carapace.ActionMap{
"scenario-name": action.ActionScenarios(),
})

rootCmd.AddCommand(idempotenceCmd)
}
Loading

0 comments on commit 09d010d

Please sign in to comment.