Skip to content

Commit c7ba6ff

Browse files
committed
Added a file provider which reads variables in env files
1 parent 2a8239c commit c7ba6ff

4 files changed

Lines changed: 156 additions & 12 deletions

File tree

checkenv.go

Lines changed: 109 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,120 @@
11
package main
22

33
import (
4+
"flag"
45
"fmt"
6+
"os"
7+
"strings"
58
)
69

10+
type showSpec struct {
11+
loadFrom map[string]interface{}
12+
providersFull map[string]interface{}
13+
providerVars map[string]map[string]interface{}
14+
}
15+
16+
func parseShowSpec(args []string) *showSpec {
17+
spec := showSpec{loadFrom: map[string]interface{}{}, providersFull: map[string]interface{}{}, providerVars: map[string]map[string]interface{}{}}
18+
for _, arg := range args {
19+
components := strings.Split(arg, "://")
20+
providerSpec := components[0]
21+
spec.loadFrom[providerSpec] = nil
22+
if len(components) == 1 {
23+
spec.providersFull[providerSpec] = nil
24+
} else {
25+
if _, ok := spec.providerVars[providerSpec]; !ok {
26+
spec.providerVars[providerSpec] = map[string]interface{}{}
27+
}
28+
varSpec := strings.Join(components[1:], "://")
29+
varNames := strings.Split(varSpec, ",")
30+
for _, varName := range varNames {
31+
spec.providerVars[providerSpec][varName] = nil
32+
}
33+
}
34+
}
35+
36+
return &spec
37+
}
38+
39+
func VariablesFromProviderSpec(providerSpec string) (map[string]string, error) {
40+
components := strings.Split(providerSpec, "+")
41+
provider := components[0]
42+
var providerArgs string
43+
if len(components) > 1 {
44+
providerArgs = strings.Join(components[1:], "+")
45+
}
46+
plugin, pluginExists := RegisteredPlugins[provider]
47+
if !pluginExists {
48+
return map[string]string{}, fmt.Errorf("unregistered provider: %s", provider)
49+
}
50+
return plugin.Provider(providerArgs)
51+
}
52+
753
func main() {
8-
for name, plugin := range RegisteredPlugins {
9-
pluginInitErr := plugin.Init()
10-
pluginEnv, pluginErr := plugin.Provider("")
11-
fmt.Printf("Plugin: %s\n", name)
12-
fmt.Printf("Help: %s\n", plugin.Help)
13-
if pluginInitErr != nil {
14-
fmt.Printf("Init error: %s\n", pluginInitErr.Error())
54+
pluginsCommand := "plugins"
55+
pluginsFlags := flag.NewFlagSet("plugins", flag.ExitOnError)
56+
pluginsHelp := pluginsFlags.Bool("h", false, "Use this flag if you want help with this command")
57+
pluginsFlags.BoolVar(pluginsHelp, "help", false, "Use this flag if you want help with this command")
58+
59+
showCommand := "show"
60+
showFlags := flag.NewFlagSet("show", flag.ExitOnError)
61+
showHelp := showFlags.Bool("h", false, "Use this flag if you want help with this command")
62+
showFlags.BoolVar(showHelp, "help", false, "Use this flag if you want help with this command")
63+
64+
availableCommands := fmt.Sprintf("%s,%s", pluginsCommand, showCommand)
65+
66+
if len(os.Args) < 2 {
67+
fmt.Fprintf(os.Stderr, "Please use one of the subcommands: %s\n", availableCommands)
68+
os.Exit(2)
69+
}
70+
71+
command := os.Args[1]
72+
73+
switch command {
74+
case pluginsCommand:
75+
pluginsFlags.Parse(os.Args[2:])
76+
if *pluginsHelp {
77+
fmt.Fprintf(os.Stderr, "Usage: %s %s\nTakes no arguments.\nLists available plugins with a brief description of each one.\n", os.Args[0], os.Args[1])
78+
os.Exit(2)
1579
}
16-
if pluginErr != nil {
17-
fmt.Printf("Plugin error: %s\n", pluginErr.Error())
18-
} else {
19-
fmt.Printf("Env:\n%v\n", pluginEnv)
80+
fmt.Println("Available plugins:")
81+
for name, plugin := range RegisteredPlugins {
82+
fmt.Printf("%s\n\t%s\n", name, plugin.Help)
83+
}
84+
case showCommand:
85+
showFlags.Parse(os.Args[2:])
86+
if *showHelp || showFlags.NArg() == 0 {
87+
fmt.Fprintf(os.Stderr, "Usage: %s %s [<provider_name>[+<provider_args>] ...] [<provider_name>[+<provider_args>]://<var_name_1>,<var_name_2>,...,<var_name_n> ...]\nShows the environment variables defined by the given providers.\n", os.Args[0], os.Args[1])
88+
os.Exit(2)
89+
}
90+
spec := parseShowSpec(showFlags.Args())
91+
providedVars := make(map[string]map[string]string)
92+
for providerSpec, _ := range spec.loadFrom {
93+
vars, providerErr := VariablesFromProviderSpec(providerSpec)
94+
if providerErr != nil {
95+
panic(providerErr)
96+
}
97+
providedVars[providerSpec] = vars
98+
}
99+
for providerSpec, _ := range spec.providersFull {
100+
fmt.Printf("%s - all variables:\n", providerSpec)
101+
for k, v := range providedVars[providerSpec] {
102+
fmt.Printf("- %s=%s\n", k, v)
103+
}
104+
}
105+
for providerSpec, queriedVars := range spec.providerVars {
106+
fmt.Printf("%s - specific variables:\n", providerSpec)
107+
definedVars := providedVars[providerSpec]
108+
for k, _ := range queriedVars {
109+
v, ok := definedVars[k]
110+
if !ok {
111+
fmt.Printf("- UNDEFINED: %s\n", k)
112+
} else {
113+
fmt.Printf("- %s=%s\n", k, v)
114+
}
115+
}
20116
}
117+
default:
118+
fmt.Fprintf(os.Stderr, "Unknown command: %s. Please use one of the subcommands: %s.\n", command, availableCommands)
21119
}
22120
}

env.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ func CurrentEnvProvider(filter string) (map[string]string, error) {
2222
}
2323

2424
func init() {
25-
RegisterPlugin("env", "", func() error { return nil }, CurrentEnvProvider)
25+
helpString := "Provides the environment variables defined in the checkenv process."
26+
RegisterPlugin("env", helpString, noop, CurrentEnvProvider)
2627
}

envfile.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"os"
6+
"strings"
7+
)
8+
9+
func EnvFileProvider(filter string) (map[string]string, error) {
10+
// TODO(zomglings): For now, we expect the filter to contain the name of the env file. However,
11+
// we will later want to add other things, like prefixes that we should strip off of lines (e.g. "export" or "set")
12+
// and prefixes that we should ignore (e.g. "#" or "//")
13+
vars := map[string]string{}
14+
filename := filter
15+
ifp, fileErr := os.Open(filename)
16+
if fileErr != nil {
17+
return vars, fileErr
18+
}
19+
defer ifp.Close()
20+
21+
scanner := bufio.NewScanner(ifp)
22+
for scanner.Scan() {
23+
line := scanner.Text()
24+
components := strings.Split(line, "=")
25+
name := components[0]
26+
var value string
27+
if len(components) > 1 {
28+
value = strings.Join(components[1:], "=")
29+
}
30+
vars[name] = value
31+
}
32+
33+
scanErr := scanner.Err()
34+
return vars, scanErr
35+
}
36+
37+
func init() {
38+
helpString := "Provides the environment variables defined in the env file with the given path."
39+
RegisterPlugin("file", helpString, noop, EnvFileProvider)
40+
}

plugins.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ type CheckenvProvider func(string) (map[string]string, error)
1717
// via SSH or on cloud configuration stores.
1818
type InitFromEnvFunction func() error
1919

20+
// noop is an InitFromEnvFunction with no effect. It returns nil.
21+
func noop() error {
22+
return nil
23+
}
24+
2025
// CheckenvPlugin represents all the metadata that checkenv requires about a registered plugin:
2126
// 1. A Help string to display to users explaining the plugins filter syntax, how it is initialized, and possibly more.
2227
// 2. An Init function which initializes the plugin when checkenv starts up.

0 commit comments

Comments
 (0)