implement parsing and conversion, get command

This commit is contained in:
Roberto Hidalgo 2022-11-16 00:46:14 -06:00
parent 20ba4ba898
commit 2619181be4
11 changed files with 857 additions and 0 deletions

13
cmd/fetch.go Normal file
View File

@ -0,0 +1,13 @@
// 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 cmd

13
cmd/flush.go Normal file
View File

@ -0,0 +1,13 @@
// 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 cmd

122
cmd/get.go Normal file
View File

@ -0,0 +1,122 @@
// 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 cmd
import (
"encoding/json"
"fmt"
"io/ioutil"
"strings"
"git.rob.mx/nidito/joao/pkg/config"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
)
func init() {
getCommand.Flags().StringP("output", "o", "raw", "the format to output in")
getCommand.Flags().Bool("remote", false, "query 1password instead of the filesystem")
getCommand.Flags().Bool("redacted", false, "do not print secrets")
}
var getCommand = &cobra.Command{
Use: "get CONFIG [--output|-o=(raw|json|yaml)] [--remote] [--redacted] [jq expr]",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
path := args[0]
query := ""
if len(args) > 1 {
query = args[1]
}
var cfg *config.Config
if strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml") {
buf, err := ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("could not read file %s", path)
}
if len(buf) == 0 {
buf = []byte("{}")
}
cfg, err = config.ConfigFromYAML(buf)
if err != nil {
return err
}
}
format, _ := cmd.Flags().GetString("output")
redacted, _ := cmd.Flags().GetBool("redacted")
if query == "" {
switch format {
case "yaml", "raw":
bytes, err := cfg.AsYAML(redacted)
if err != nil {
return err
}
_, err = cmd.OutOrStdout().Write(bytes)
return err
case "json", "json-op":
bytes, err := cfg.AsJSON(redacted, format == "json-op")
if err != nil {
return err
}
_, err = cmd.OutOrStdout().Write(bytes)
return err
}
return fmt.Errorf("unknown format %s", format)
}
parts := strings.Split(query, ".")
entry := cfg.Tree
for _, part := range parts {
entry = entry.Children[part]
if entry == nil {
return fmt.Errorf("value not found at %s of %s", part, query)
}
}
var bytes []byte
var err error
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
}
bytes, err = json.Marshal(val)
if err != nil {
return err
}
} else {
if valString, ok := entry.Value.(string); ok {
bytes = []byte(valString)
} else {
bytes, err = json.Marshal(entry.Value)
if err != nil {
return err
}
}
}
_, err = cmd.OutOrStdout().Write(bytes)
return err
},
}

13
cmd/list.go Normal file
View File

@ -0,0 +1,13 @@
// 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 cmd

103
cmd/root.go Normal file
View File

@ -0,0 +1,103 @@
// 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 cmd
import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
var Root = &cobra.Command{
Use: "joao [--silent|-v|--verbose] [--[no-]color] [-h|--help] [--version]",
Short: "does config",
Long: `does config with 1password and stuff`,
// DisableAutoGenTag: true,
// SilenceUsage: true,
// SilenceErrors: true,
ValidArgs: []string{""},
Annotations: map[string]string{},
Args: func(cmd *cobra.Command, args []string) error {
err := cobra.OnlyValidArgs(cmd, args)
if err != nil {
suggestions := []string{}
bold := color.New(color.Bold)
for _, l := range cmd.SuggestionsFor(args[len(args)-1]) {
suggestions = append(suggestions, bold.Sprint(l))
}
errMessage := fmt.Sprintf("Unknown subcommand %s", bold.Sprint(strings.Join(args, " ")))
if len(suggestions) > 0 {
errMessage += ". Perhaps you meant " + strings.Join(suggestions, ", ") + "?"
}
return fmt.Errorf("command not found")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
if ok, err := cmd.Flags().GetBool("version"); err == nil && ok {
vc, _, err := cmd.Root().Find([]string{"__version"})
if err != nil {
return err
}
return vc.RunE(vc, []string{})
}
return fmt.Errorf("no command provided")
}
return nil
},
}
func RootCommand(version string) *cobra.Command {
Root.Annotations["version"] = version
rootFlagset := pflag.NewFlagSet("joao", pflag.ContinueOnError)
// for name, opt := range Root.Options {
// def, ok := opt.Default.(bool)
// if !ok {
// def = false
// }
// if opt.ShortName != "" {
// rootFlagset.BoolP(name, opt.ShortName, def, opt.Description)
// } else {
// rootFlagset.Bool(name, def, opt.Description)
// }
// }
rootFlagset.Usage = func() {}
rootFlagset.SortFlags = false
Root.PersistentFlags().AddFlagSet(rootFlagset)
Root.Flags().Bool("version", false, "Display the version")
// Root.CompletionOptions.DisableDefaultCmd = true
Root.AddCommand(getCommand)
// Root.AddCommand(completionCommand)
// Root.AddCommand(generateDocumentationCommand)
// Root.AddCommand(doctorCommand)
// Root.SetHelpCommand(helpCommand)
// helpCommand.AddCommand(docsCommand)
// docsCommand.SetHelpFunc(docs.HelpRenderer(Root.Options))
// Root.SetHelpFunc(Root.HelpRenderer(Root.Options))
return Root
}

13
cmd/set.go Normal file
View File

@ -0,0 +1,13 @@
// 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 cmd

19
go.mod Normal file
View File

@ -0,0 +1,19 @@
module git.rob.mx/nidito/joao
go 1.18
require (
github.com/1Password/connect-sdk-go v1.5.0
github.com/fatih/color v1.13.0
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
)

96
go.sum Normal file
View File

@ -0,0 +1,96 @@
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/1Password/connect-sdk-go v1.5.0 h1:F0WJcLSzGg3iXEDY49/ULdszYKsQLGTzn+2cyYXqiyk=
github.com/1Password/connect-sdk-go v1.5.0/go.mod h1:TdynFeyvaRoackENbJ8RfJokH+WAowAu1MLmUbdMq6s=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

33
main.go Normal file
View File

@ -0,0 +1,33 @@
// 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 main
import (
"git.rob.mx/nidito/joao/cmd"
"github.com/sirupsen/logrus"
)
var version = "dev"
func main() {
logrus.SetFormatter(&logrus.TextFormatter{
DisableLevelTruncation: true,
DisableTimestamp: true,
// ForceColors: runtime.ColorEnabled(),
})
err := cmd.RootCommand(version).Execute()
if err != nil {
logrus.Fatal(err)
}
}

145
pkg/config/config.go Normal file
View File

@ -0,0 +1,145 @@
// 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 config
import (
"encoding/json"
"fmt"
op "github.com/1Password/connect-sdk-go/onepassword"
"gopkg.in/yaml.v3"
)
type Config struct {
Path string
Vault string
Name string
Tree *Entry
}
var redactOutput = false
func (cfg *Config) ToMap() map[string]interface{} {
ret := map[string]interface{}{}
for _, child := range cfg.Tree.Children {
ret[child.Key] = child.AsMap()
}
return ret
}
func (cfg *Config) ToOP() *op.Item {
annotationsSection := &op.ItemSection{
ID: "~annotations",
Label: "~annotations",
}
sections := []*op.ItemSection{annotationsSection}
fields := []*op.ItemField{
{
ID: "password",
Type: "CONCEALED",
Purpose: "PASSWORD",
Label: "password",
Value: "hash",
}, {
ID: "notesPlain",
Type: "STRING",
Purpose: "NOTES",
Label: "notesPlain",
Value: "flushed by joao",
},
}
for key, leaf := range cfg.Tree.Children {
if len(leaf.Children) == 0 {
fields = append(fields, leaf.ToOP(annotationsSection)...)
continue
}
if !leaf.isSequence {
sections = append(sections, &op.ItemSection{
ID: key,
Label: key,
})
} else {
fmt.Printf("Found sequence for %s", leaf.Key)
}
for _, child := range leaf.Children {
fields = append(fields, child.ToOP(annotationsSection)...)
}
}
return &op.Item{
Title: cfg.Name,
Sections: sections,
Vault: op.ItemVault{ID: "nidito-admin"},
Category: op.Password,
Fields: fields,
}
}
func ConfigFromYAML(data []byte) (*Config, error) {
cfg := &Config{
Vault: "vault",
Name: "title",
Tree: NewEntry("root"),
}
yaml.Unmarshal(data, cfg.Tree.Children)
for k, leaf := range cfg.Tree.Children {
leaf.SetKey(k, []string{})
}
return cfg, nil
}
func ConfigFromOP(item *op.Item) (*Config, error) {
cfg := &Config{
Vault: item.Vault.ID,
Name: item.Title,
Tree: NewEntry("root"),
}
err := cfg.Tree.FromOP(item.Fields)
return cfg, err
}
func (cfg *Config) MarshalYAML() (interface{}, error) {
return cfg.Tree.MarshalYAML()
}
func (cfg *Config) AsYAML(redacted bool) ([]byte, error) {
redactOutput = redacted
bytes, err := yaml.Marshal(cfg)
if err != nil {
return nil, fmt.Errorf("could not serialize config as yaml: %w", err)
}
return bytes, nil
}
func (cfg *Config) AsJSON(redacted bool, item bool) ([]byte, error) {
var repr interface{}
if item {
repr = cfg.ToOP()
} else {
redactOutput = redacted
repr = cfg.ToMap()
}
bytes, err := json.Marshal(repr)
if err != nil {
return nil, fmt.Errorf("could not serialize config as json: %w", err)
}
return bytes, nil
}

287
pkg/config/entry.go Normal file
View File

@ -0,0 +1,287 @@
// 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 config
import (
"fmt"
"strconv"
"strings"
op "github.com/1Password/connect-sdk-go/onepassword"
"gopkg.in/yaml.v3"
)
func isNumeric(s string) bool {
for _, v := range s {
if v < '0' || v > '9' {
return false
}
}
return true
}
type Entry struct {
Key string
Path []string
Value interface{}
Children map[string]*Entry
isSecret bool
isSequence bool
node *yaml.Node
}
func NewEntry(name string) *Entry {
return &Entry{Key: name, Children: map[string]*Entry{}}
}
func (e *Entry) SetKey(key string, parent []string) {
e.Path = append(parent, key)
e.Key = key
for k, child := range e.Children {
child.SetKey(k, e.Path)
}
}
func (e *Entry) UnmarshalYAML(node *yaml.Node) error {
e.node = node
switch node.Kind {
case yaml.DocumentNode, yaml.MappingNode:
if e.Children == nil {
e.Children = map[string]*Entry{}
}
err := node.Decode(&e.Children)
if err != nil {
return err
}
case yaml.SequenceNode:
list := []*Entry{}
err := node.Decode(&list)
if err != nil {
return err
}
if e.Children == nil {
e.Children = map[string]*Entry{}
}
for idx, child := range list {
child.Key = fmt.Sprintf("%d", idx)
e.Children[child.Key] = child
}
e.isSequence = true
case yaml.ScalarNode:
var val interface{}
err := node.Decode(&val)
if err != nil {
return err
}
e.Value = val
e.isSecret = node.Tag == "!!secret"
default:
return fmt.Errorf("unknown yaml type: %v", node.Kind)
}
return nil
}
func (e *Entry) MarshalYAML() (interface{}, error) {
if len(e.Children) == 0 {
if redactOutput && e.isSecret {
n := e.node
return &yaml.Node{
Kind: n.Kind,
Style: yaml.TaggedStyle,
Tag: n.Tag,
Value: "",
HeadComment: n.HeadComment,
LineComment: n.LineComment,
FootComment: n.FootComment,
Line: n.Line,
Column: n.Column,
}, nil
}
return e.node, nil
}
if e.isSequence {
ret := make([]*Entry, len(e.Children))
for k, child := range e.Children {
idx, _ := strconv.Atoi(k)
ret[idx] = child
}
return ret, nil
}
ret := map[string]*Entry{}
for k, child := range e.Children {
ret[k] = child
}
return ret, nil
}
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]
label := field.Label
if field.Section != nil {
if field.Section.Label == "~annotations" {
annotations[label] = field.Value
continue
} else {
label = field.Section.Label + "." + label
}
}
labels = append(labels, label)
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
}
}
}
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
}
holderI := container.Value
if container.Value == nil {
holderI = []interface{}{}
}
holder := holderI.([]interface{})
container.Value = append(holder, value)
break
}
subContainer, exists := container.Children[key]
if exists {
container = subContainer
} else {
container.Children[key] = NewEntry(key)
container = container.Children[key]
}
}
}
return nil
}
func (e *Entry) ToOP(annotationsSection *op.ItemSection) []*op.ItemField {
ret := []*op.ItemField{}
var section *op.ItemSection
name := e.Key
if len(e.Path) > 1 {
section = &op.ItemSection{ID: e.Path[0]}
name = strings.Join(e.Path[1:], ".")
}
if e.isSecret {
ret = append(ret, &op.ItemField{
ID: "~annotations." + strings.Join(e.Path, "."),
Section: annotationsSection,
Label: name,
Type: "STRING",
Value: "secret",
})
} else if _, ok := e.Value.(bool); ok {
ret = append(ret, &op.ItemField{
ID: "~annotations." + strings.Join(e.Path, "."),
Section: annotationsSection,
Label: name,
Type: "STRING",
Value: "bool",
})
} else if _, ok := e.Value.(int); ok {
ret = append(ret, &op.ItemField{
ID: "~annotations." + strings.Join(e.Path, "."),
Section: annotationsSection,
Label: name,
Type: "STRING",
Value: "int",
})
} else if _, ok := e.Value.(float64); ok {
ret = append(ret, &op.ItemField{
ID: "~annotations." + strings.Join(e.Path, "."),
Section: annotationsSection,
Label: name,
Type: "STRING",
Value: "float",
})
}
if len(e.Children) == 0 {
ret = append(ret, &op.ItemField{
ID: strings.Join(e.Path, "."),
Section: section,
Label: name,
Type: "STRING",
Value: fmt.Sprintf("%s", e.Value),
})
} else {
for _, child := range e.Children {
ret = append(ret, child.ToOP(annotationsSection)...)
}
}
return ret
}
func (e *Entry) AsMap() interface{} {
if len(e.Children) == 0 {
if redactOutput && e.isSecret {
return ""
}
return e.Value
}
if e.isSequence {
ret := make([]interface{}, len(e.Children))
for key, child := range e.Children {
idx, _ := strconv.Atoi(key)
ret[idx] = child.AsMap()
}
return ret
}
ret := map[string]interface{}{}
for key, child := range e.Children {
ret[key] = child.AsMap()
}
return ret
}