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"
|
"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
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
|
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) {
|
||||||
|
@ -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 = value == "true"
|
value = valueStr == "true"
|
||||||
case "int":
|
case "int":
|
||||||
var err error
|
var err error
|
||||||
value, err = strconv.ParseInt(value.(string), 10, 64)
|
value, err = strconv.ParseInt(valueStr, 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
|
||||||
|
if isSecret {
|
||||||
|
style = yaml.TaggedStyle
|
||||||
|
tag = "!!secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
child := &Entry{
|
||||||
Key: key,
|
Key: key,
|
||||||
Path: path,
|
Path: path,
|
||||||
Value: value,
|
Value: value,
|
||||||
isSecret: isSecretLabel == "!!secret",
|
isSecret: isSecret,
|
||||||
|
node: &yaml.Node{
|
||||||
|
Kind: yaml.ScalarNode,
|
||||||
|
Value: valueStr,
|
||||||
|
Style: style,
|
||||||
|
Tag: tag,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
break
|
container.isSequence = isNumeric(key)
|
||||||
}
|
container.Children[key] = child
|
||||||
|
continue
|
||||||
holderI := container.Value
|
|
||||||
if container.Value == nil {
|
|
||||||
holderI = []interface{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
holder := holderI.([]interface{})
|
|
||||||
container.Value = append(holder, value)
|
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user