2016-08-23 08:31:50 +08:00
// Package config provides functions for reading and parsing the provider credentials json file.
// It cleans nonstandard json features (comments and trailing commas), as well as replaces environment variable placeholders with
// their environment variable equivalents. To reference an environment variable in your json file, simply use values in this format:
// "key"="$ENV_VAR_NAME"
2022-05-03 08:12:30 +08:00
package credsfile
2016-08-23 08:31:50 +08:00
import (
"encoding/json"
2018-12-12 00:56:06 +08:00
"fmt"
2016-08-23 08:31:50 +08:00
"os"
2021-04-13 04:20:32 +08:00
"os/exec"
"path/filepath"
2016-08-23 08:31:50 +08:00
"strings"
"github.com/DisposaBoy/JsonConfigReader"
"github.com/TomOnTime/utfutil"
)
2022-04-05 03:33:59 +08:00
func quotedList ( l [ ] string ) string {
if len ( l ) == 0 {
return ""
}
return ` " ` + strings . Join ( l , ` ", " ` ) + ` " `
}
func keysWithColons ( list [ ] string ) [ ] string {
var r [ ] string
for _ , k := range list {
if strings . Contains ( k , ":" ) {
r = append ( r , k )
}
}
return r
}
2021-04-13 04:20:32 +08:00
// LoadProviderConfigs will open or execute the specified file name, and parse its contents. It will replace environment variables it finds if any value matches $[A-Za-z_-0-9]+
2016-08-23 08:31:50 +08:00
func LoadProviderConfigs ( fname string ) ( map [ string ] map [ string ] string , error ) {
var results = map [ string ] map [ string ] string { }
2021-04-13 04:20:32 +08:00
var dat [ ] byte
var err error
if strings . HasPrefix ( fname , "!" ) {
dat , err = executeCredsFile ( strings . TrimPrefix ( fname , "!" ) )
if err != nil {
return nil , err
}
2022-03-26 03:20:30 +08:00
} else if strings . HasSuffix ( fname , ".json" ) {
// .json files are never executable (needed because in Windows WSL
// all files are executable).
dat , err = readCredsFile ( fname )
if err != nil {
return nil , err
}
2021-04-13 04:20:32 +08:00
} else if isExecutable ( fname ) {
dat , err = executeCredsFile ( fname )
if err != nil {
return nil , err
2017-03-23 00:38:08 +08:00
}
2021-04-13 04:20:32 +08:00
} else {
// no executable bit found nor marked as executable so read it in
dat , err = readCredsFile ( fname )
2021-12-14 22:47:32 +08:00
if err != nil {
return nil , err
}
2016-08-23 08:31:50 +08:00
}
2021-04-13 04:20:32 +08:00
2016-08-23 08:31:50 +08:00
s := string ( dat )
r := JsonConfigReader . New ( strings . NewReader ( s ) )
err = json . NewDecoder ( r ) . Decode ( & results )
if err != nil {
2020-08-31 07:52:37 +08:00
return nil , fmt . Errorf ( "failed parsing provider credentials file %v: %v" , fname , err )
2016-08-23 08:31:50 +08:00
}
if err = replaceEnvVars ( results ) ; err != nil {
return nil , err
}
2022-04-05 03:33:59 +08:00
2022-05-09 02:23:45 +08:00
// For backwards compatibility, insert NONE and BIND entries if
// they do not exist. These are the only providers that previously
// did not require entries in creds.json prior to v4.0.
if _ , ok := results [ "none" ] ; ! ok {
results [ "none" ] = map [ string ] string { "TYPE" : "NONE" }
2022-04-05 03:33:59 +08:00
}
2022-05-09 02:23:45 +08:00
if _ , ok := results [ "bind" ] ; ! ok {
results [ "bind" ] = map [ string ] string { "TYPE" : "BIND" }
}
2016-08-23 08:31:50 +08:00
return results , nil
}
2021-04-13 04:20:32 +08:00
func isExecutable ( filename string ) bool {
if stat , statErr := os . Stat ( filename ) ; statErr == nil {
if mode := stat . Mode ( ) ; mode & 0111 == 0111 {
return true
}
}
return false
}
func readCredsFile ( filename string ) ( [ ] byte , error ) {
dat , err := utfutil . ReadFile ( filename , utfutil . POSIX )
if err != nil {
// no creds file is ok. Bind requires nothing for example. Individual providers will error if things not found.
if os . IsNotExist ( err ) {
fmt . Printf ( "INFO: Config file %q does not exist. Skipping.\n" , filename )
return [ ] byte { } , nil
}
return nil , fmt . Errorf ( "failed reading provider credentials file %v: %v" , filename , err )
}
return dat , nil
}
func executeCredsFile ( filename string ) ( [ ] byte , error ) {
cmd := filename
if ! strings . HasPrefix ( filename , "/" ) {
// if the path doesn't start with `/` make sure we aren't relying on $PATH.
cmd = strings . Join ( [ ] string { "." , filename } , string ( filepath . Separator ) )
}
out , err := exec . Command ( cmd ) . Output ( )
return out , err
}
2016-08-23 08:31:50 +08:00
func replaceEnvVars ( m map [ string ] map [ string ] string ) error {
2017-03-17 13:42:53 +08:00
for _ , keys := range m {
2016-08-23 08:31:50 +08:00
for k , v := range keys {
if strings . HasPrefix ( v , "$" ) {
env := v [ 1 : ]
newVal := os . Getenv ( env )
keys [ k ] = newVal
}
}
}
return nil
}