Skip to content

Commit

Permalink
Merge pull request #38 from go-kivik/kouchctl
Browse files Browse the repository at this point in the history
Beginning of kouchctl
  • Loading branch information
flimzy committed Sep 23, 2020
2 parents c9bbe70 + cd0cf2e commit bf646b2
Show file tree
Hide file tree
Showing 58 changed files with 1,550 additions and 5 deletions.
4 changes: 0 additions & 4 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ coverage:
script:
- ./script/coverage.sh

go-1.13:
<<: *test_template
image: golang:1.13

go-1.14:
<<: *test_template
image: golang:1.14
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ time.

There is no backward compatibility guarantee for anything in this package.

This package depends on Go 1.13 or newer.
This package is tested against Go 1.14 and newer.


## License
Expand Down
4 changes: 4 additions & 0 deletions cmd/kouchctl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
kouchctl
--------

`kouchctl` is a very early-stage work-in-progress. The goal is to be a CLI tool to facilitate administration of CouchDB. Thing of it as a replacement for 'curl', tailor-made for CouchDB interaction and scripting.
22 changes: 22 additions & 0 deletions cmd/kouchctl/cmd/foo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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 "github.com/spf13/cobra"

func fooCmd() *cobra.Command {
return &cobra.Command{
Use: "foo",
Run: func(*cobra.Command, []string) {},
}
}
47 changes: 47 additions & 0 deletions cmd/kouchctl/cmd/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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 (
"github.com/spf13/cobra"

"github.com/go-kivik/xkivik/v4/cmd/kouchctl/config"
"github.com/go-kivik/xkivik/v4/cmd/kouchctl/log"
)

type get struct {
log log.Logger
conf *config.Config
}

func getCmd(lg log.Logger, conf *config.Config) *cobra.Command {
g := &get{
log: lg,
conf: conf,
}
return &cobra.Command{
Use: "get [dsn]",
Short: "get a document",
Long: `Fetch a document with the HTTP GET verb`,
RunE: g.RunE,
}
}

func (c *get) RunE(cmd *cobra.Command, _ []string) error {
dsn, err := c.conf.DSN()
if err != nil {
return err
}
c.log.Debugf("[get] Will fetch document: %q", dsn)
return nil
}
47 changes: 47 additions & 0 deletions cmd/kouchctl/cmd/get_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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 (
"testing"

"gitlab.com/flimzy/testy"
)

func Test_get_RunE(t *testing.T) {
tests := testy.NewTable()

tests.Add("missing document", cmdTest{
args: []string{"get"},
err: "no context specified",
})
tests.Add("invalid URL on command line", cmdTest{
args: []string{"-d", "get", "http://localhost:1/foo/bar/%xxx"},
err: `parse "http://localhost:1/foo/bar/%xxx": invalid URL escape "%xx"`,
})
tests.Add("full url on command line", cmdTest{
args: []string{"-d", "get", "http://localhost:1/foo/bar"},
})
tests.Add("path only on command line", cmdTest{
args: []string{"-d", "--kouchconfig", "./testdata/localhost.yaml", "get", "/foo/bar"},
})
tests.Add("document only on command line", cmdTest{
args: []string{"-d", "--kouchconfig", "./testdata/localhost.yaml", "get", "bar"},
})

tests.Run(t, func(t *testing.T, tt cmdTest) {
cmd := rootCmd()

testCmd(t, cmd, tt)
})
}
48 changes: 48 additions & 0 deletions cmd/kouchctl/cmd/ping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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 (
"github.com/spf13/cobra"

"github.com/go-kivik/xkivik/v4/cmd/kouchctl/config"
"github.com/go-kivik/xkivik/v4/cmd/kouchctl/log"
)

type ping struct {
log log.Logger
conf *config.Config
}

func pingCmd(lg log.Logger, conf *config.Config) *cobra.Command {
c := &ping{
log: lg,
conf: conf,
}

return &cobra.Command{
Use: "ping [dsn]",
Short: "Ping a server",
Long: "Ping a server's /_up endpoint to determine availability to serve requests",
RunE: c.RunE,
}
}

func (c *ping) RunE(cmd *cobra.Command, args []string) error {
dsn, err := c.conf.ServerDSN()
if err != nil {
return err
}
c.log.Debugf("[ping] Will ping server: %q", dsn)
return nil
}
48 changes: 48 additions & 0 deletions cmd/kouchctl/cmd/ping_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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 (
"testing"

"gitlab.com/flimzy/testy"
)

func Test_ping_RunE(t *testing.T) {
tests := testy.NewTable()

tests.Add("missing document", cmdTest{
args: []string{"get"},
err: "no context specified",
})
tests.Add("invalid URL on command line", cmdTest{
args: []string{"-d", "ping", "http://localhost:1/foo/bar/%xxx"},
err: `parse "http://localhost:1/foo/bar/%xxx": invalid URL escape "%xx"`,
})
tests.Add("full url on command line", cmdTest{
args: []string{"-d", "ping", "http://localhost:1/foo/bar"},
})
tests.Add("server only on command line", cmdTest{
args: []string{"-d", "--kouchconfig", "./testdata/localhost.yaml", "ping", "http://localhost:1"},
})
tests.Add("no server provided", cmdTest{
args: []string{"ping", "foo/bar"},
err: "server hostname required",
})

tests.Run(t, func(t *testing.T, tt cmdTest) {
cmd := rootCmd()

testCmd(t, cmd, tt)
})
}
100 changes: 100 additions & 0 deletions cmd/kouchctl/cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// 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 (
"context"
"fmt"
"os"

"github.com/spf13/cobra"

"github.com/go-kivik/xkivik/v4/cmd/kouchctl/config"
"github.com/go-kivik/xkivik/v4/cmd/kouchctl/log"
)

type root struct {
confFile string
debug bool
log log.Logger
conf *config.Config
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute(ctx context.Context) {
fmt.Println(os.Args)
root := rootCmd()

root.AddCommand(fooCmd())

if err := root.ExecuteContext(ctx); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

func rootCmd() *cobra.Command {
r := &root{
log: log.New(),
conf: config.New(),
}

cmd := &cobra.Command{
Use: "kouchctl",
Short: "kouchctl facilitates controlling CouchDB instances",
Long: `This tool makes it easier to administrate and interact with CouchDB's HTTP API`,
PersistentPreRunE: r.init,
RunE: r.RunE,
}

pf := cmd.PersistentFlags()

pf.StringVar(&r.confFile, "kouchconfig", "~/.kouchctl/config", "Path to kouchconfig file to use for CLI requests")
pf.BoolVarP(&r.debug, "debug", "d", false, "Enable debug output")

cmd.AddCommand(getCmd(r.log, r.conf))
cmd.AddCommand(pingCmd(r.log, r.conf))

return cmd
}

func (r *root) init(cmd *cobra.Command, args []string) error {
r.log.SetOut(cmd.OutOrStdout())
r.log.SetErr(cmd.ErrOrStderr())
r.log.SetDebug(r.debug)

r.log.Debug("Debug mode enabled")

if err := r.conf.Read(r.confFile, r.log); err != nil {
return err
}

if len(args) > 0 {
if err := r.conf.SetURL(args[0]); err != nil {
return err
}
}

return nil
}

func (r *root) RunE(cmd *cobra.Command, args []string) error {
cx, err := r.conf.DSN()
if err != nil {
return err
}
r.log.Debugf("DSN: %s from %q", cx, r.conf.CurrentContext)

return nil
}
Loading

0 comments on commit bf646b2

Please sign in to comment.