implement remote loading via op cli

This commit is contained in:
Roberto Hidalgo 2022-11-16 01:38:04 -06:00
parent 2619181be4
commit 095503f29a
4 changed files with 127 additions and 42 deletions

View File

@ -18,6 +18,7 @@ import (
"io/ioutil" "io/ioutil"
"strings" "strings"
opClient "git.rob.mx/nidito/joao/internal/op-client"
"git.rob.mx/nidito/joao/pkg/config" "git.rob.mx/nidito/joao/pkg/config"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@ -39,7 +40,10 @@ var getCommand = &cobra.Command{
query = args[1] query = args[1]
} }
var cfg *config.Config var cfg *config.Config
if strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml") { remote, _ := cmd.Flags().GetBool("remote")
isYaml := strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml")
if !remote && isYaml {
buf, err := ioutil.ReadFile(path) buf, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return fmt.Errorf("could not read file %s", path) return fmt.Errorf("could not read file %s", path)
@ -53,6 +57,22 @@ var getCommand = &cobra.Command{
if err != nil { if err != nil {
return err return err
} }
} else {
name := path
if isYaml {
comps := strings.Split(path, "config/")
name = strings.ReplaceAll(strings.Replace(comps[len(comps)-1], ".yaml", "", 1), "/", ":")
}
item, err := opClient.Fetch("nidito-admin", name)
if err != nil {
return err
}
cfg, err = config.ConfigFromOP(item)
if err != nil {
return err
}
} }
format, _ := cmd.Flags().GetString("output") format, _ := cmd.Flags().GetString("output")
@ -93,12 +113,9 @@ var getCommand = &cobra.Command{
if len(entry.Children) > 0 { if len(entry.Children) > 0 {
val := entry.AsMap() val := entry.AsMap()
if format == "yaml" { if format == "yaml" {
bytes, err = yaml.Marshal(val) enc := yaml.NewEncoder(cmd.OutOrStdout())
if err != nil { enc.SetIndent(2)
return err return enc.Encode(val)
}
_, err = cmd.OutOrStdout().Write(bytes)
return err
} }
bytes, err = json.Marshal(val) bytes, err = json.Marshal(val)

50
internal/op-client/op.go Normal file
View File

@ -0,0 +1,50 @@
// Copyright © 2022 Roberto Hidalgo <joao@un.rob.mx>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package opClient
import (
"bytes"
"encoding/json"
"fmt"
"os"
"os/exec"
op "github.com/1Password/connect-sdk-go/onepassword"
)
func Fetch(vault, name string) (*op.Item, error) {
return fetchRemote(name, vault)
}
func fetchRemote(name, vault string) (*op.Item, error) {
cmd := exec.Command("op", "item", "--format", "json", "--vault", vault, "get", name)
cmd.Env = os.Environ()
var stdout bytes.Buffer
cmd.Stdout = &stdout
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return nil, err
}
if cmd.ProcessState.ExitCode() > 0 {
return nil, fmt.Errorf("op exited with %d: %s", cmd.ProcessState.ExitCode(), stderr.Bytes())
}
var item *op.Item
if err := json.Unmarshal(stdout.Bytes(), &item); err != nil {
return nil, err
}
return item, nil
}

View File

@ -13,6 +13,7 @@
package config package config
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -121,11 +122,14 @@ func (cfg *Config) MarshalYAML() (interface{}, error) {
func (cfg *Config) AsYAML(redacted bool) ([]byte, error) { func (cfg *Config) AsYAML(redacted bool) ([]byte, error) {
redactOutput = redacted redactOutput = redacted
bytes, err := yaml.Marshal(cfg) var out bytes.Buffer
enc := yaml.NewEncoder(&out)
enc.SetIndent(2)
err := enc.Encode(cfg)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not serialize config as yaml: %w", err) return nil, fmt.Errorf("could not serialize config as yaml: %w", err)
} }
return bytes, nil return out.Bytes(), nil
} }
func (cfg *Config) AsJSON(redacted bool, item bool) ([]byte, error) { func (cfg *Config) AsJSON(redacted bool, item bool) ([]byte, error) {

View File

@ -130,7 +130,6 @@ func (e *Entry) MarshalYAML() (interface{}, error) {
func (e *Entry) FromOP(fields []*op.ItemField) error { func (e *Entry) FromOP(fields []*op.ItemField) error {
annotations := map[string]string{} annotations := map[string]string{}
data := map[string]string{} data := map[string]string{}
labels := []string{}
for i := 0; i < len(fields); i++ { for i := 0; i < len(fields); i++ {
field := fields[i] field := fields[i]
@ -143,58 +142,73 @@ func (e *Entry) FromOP(fields []*op.ItemField) error {
label = field.Section.Label + "." + label label = field.Section.Label + "." + label
} }
} }
labels = append(labels, label) if label == "password" || label == "notesPlain" {
continue
}
data[label] = field.Value data[label] = field.Value
} }
for _, label := range labels { for label, valueStr := range data {
var value interface{} = data[label] var value interface{}
typeString := annotations[label]
if typeString, ok := annotations[label]; ok { switch typeString {
switch typeString { case "bool":
case "bool": value = valueStr == "true"
value = value == "true" case "int":
case "int": var err error
var err error value, err = strconv.ParseInt(valueStr, 10, 64)
value, err = strconv.ParseInt(value.(string), 10, 64) if err != nil {
if err != nil { return err
return err
}
} }
case "float":
var err error
value, err = strconv.ParseFloat(valueStr, 64)
if err != nil {
return err
}
default:
value = valueStr
} }
// logrus.Warnf("processing: %s, %b", label, value)
path := strings.Split(label, ".") path := strings.Split(label, ".")
container := e container := e
for idx, key := range path { for idx, key := range path {
if idx == len(path)-1 { if idx == len(path)-1 {
if !isNumeric(key) { isSecret := annotations[label] == "secret"
isSecretLabel := annotations[label] var style yaml.Style
container.Children[key] = &Entry{ var tag string
Key: key, if isSecret {
Path: path, style = yaml.TaggedStyle
Value: value, tag = "!!secret"
isSecret: isSecretLabel == "!!secret",
}
break
} }
holderI := container.Value child := &Entry{
if container.Value == nil { Key: key,
holderI = []interface{}{} Path: path,
Value: value,
isSecret: isSecret,
node: &yaml.Node{
Kind: yaml.ScalarNode,
Value: valueStr,
Style: style,
Tag: tag,
},
} }
container.isSequence = isNumeric(key)
holder := holderI.([]interface{}) container.Children[key] = child
container.Value = append(holder, value) continue
break
} }
subContainer, exists := container.Children[key] subContainer, exists := container.Children[key]
if exists { if exists {
container = subContainer container = subContainer
} else { } else {
container.Children[key] = NewEntry(key) child := NewEntry(key)
container = container.Children[key] child.Path = append(container.Path, key)
container.Children[key] = child
container = child
} }
} }
} }