implement remote loading via op cli
This commit is contained in:
parent
2619181be4
commit
095503f29a
31
cmd/get.go
31
cmd/get.go
@ -18,6 +18,7 @@ import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
opClient "git.rob.mx/nidito/joao/internal/op-client"
|
||||
"git.rob.mx/nidito/joao/pkg/config"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
@ -39,7 +40,10 @@ var getCommand = &cobra.Command{
|
||||
query = args[1]
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read file %s", path)
|
||||
@ -53,6 +57,22 @@ var getCommand = &cobra.Command{
|
||||
if err != nil {
|
||||
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")
|
||||
@ -93,12 +113,9 @@ var getCommand = &cobra.Command{
|
||||
if len(entry.Children) > 0 {
|
||||
val := entry.AsMap()
|
||||
if format == "yaml" {
|
||||
bytes, err = yaml.Marshal(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = cmd.OutOrStdout().Write(bytes)
|
||||
return err
|
||||
enc := yaml.NewEncoder(cmd.OutOrStdout())
|
||||
enc.SetIndent(2)
|
||||
return enc.Encode(val)
|
||||
}
|
||||
|
||||
bytes, err = json.Marshal(val)
|
||||
|
50
internal/op-client/op.go
Normal file
50
internal/op-client/op.go
Normal 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
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
@ -121,11 +122,14 @@ func (cfg *Config) MarshalYAML() (interface{}, error) {
|
||||
|
||||
func (cfg *Config) AsYAML(redacted bool) ([]byte, error) {
|
||||
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 {
|
||||
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) {
|
||||
|
@ -130,7 +130,6 @@ func (e *Entry) MarshalYAML() (interface{}, error) {
|
||||
func (e *Entry) FromOP(fields []*op.ItemField) error {
|
||||
annotations := map[string]string{}
|
||||
data := map[string]string{}
|
||||
labels := []string{}
|
||||
|
||||
for i := 0; i < len(fields); i++ {
|
||||
field := fields[i]
|
||||
@ -143,58 +142,73 @@ func (e *Entry) FromOP(fields []*op.ItemField) error {
|
||||
label = field.Section.Label + "." + label
|
||||
}
|
||||
}
|
||||
labels = append(labels, label)
|
||||
if label == "password" || label == "notesPlain" {
|
||||
continue
|
||||
}
|
||||
data[label] = field.Value
|
||||
}
|
||||
|
||||
for _, label := range labels {
|
||||
var value interface{} = data[label]
|
||||
|
||||
if typeString, ok := annotations[label]; ok {
|
||||
switch typeString {
|
||||
case "bool":
|
||||
value = value == "true"
|
||||
case "int":
|
||||
var err error
|
||||
value, err = strconv.ParseInt(value.(string), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for label, valueStr := range data {
|
||||
var value interface{}
|
||||
typeString := annotations[label]
|
||||
switch typeString {
|
||||
case "bool":
|
||||
value = valueStr == "true"
|
||||
case "int":
|
||||
var err error
|
||||
value, err = strconv.ParseInt(valueStr, 10, 64)
|
||||
if err != nil {
|
||||
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, ".")
|
||||
container := e
|
||||
|
||||
for idx, key := range path {
|
||||
if idx == len(path)-1 {
|
||||
if !isNumeric(key) {
|
||||
isSecretLabel := annotations[label]
|
||||
container.Children[key] = &Entry{
|
||||
Key: key,
|
||||
Path: path,
|
||||
Value: value,
|
||||
isSecret: isSecretLabel == "!!secret",
|
||||
}
|
||||
break
|
||||
isSecret := annotations[label] == "secret"
|
||||
var style yaml.Style
|
||||
var tag string
|
||||
if isSecret {
|
||||
style = yaml.TaggedStyle
|
||||
tag = "!!secret"
|
||||
}
|
||||
|
||||
holderI := container.Value
|
||||
if container.Value == nil {
|
||||
holderI = []interface{}{}
|
||||
child := &Entry{
|
||||
Key: key,
|
||||
Path: path,
|
||||
Value: value,
|
||||
isSecret: isSecret,
|
||||
node: &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Value: valueStr,
|
||||
Style: style,
|
||||
Tag: tag,
|
||||
},
|
||||
}
|
||||
|
||||
holder := holderI.([]interface{})
|
||||
container.Value = append(holder, value)
|
||||
break
|
||||
container.isSequence = isNumeric(key)
|
||||
container.Children[key] = child
|
||||
continue
|
||||
}
|
||||
|
||||
subContainer, exists := container.Children[key]
|
||||
if exists {
|
||||
container = subContainer
|
||||
} else {
|
||||
container.Children[key] = NewEntry(key)
|
||||
container = container.Children[key]
|
||||
child := NewEntry(key)
|
||||
child.Path = append(container.Path, key)
|
||||
container.Children[key] = child
|
||||
container = child
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user