Skip to content

Commit

Permalink
v0.0.36: add cert management choice and misc refinements
Browse files Browse the repository at this point in the history
  • Loading branch information
geemus committed May 20, 2024
1 parent 96f08cc commit 91cc689
Show file tree
Hide file tree
Showing 28 changed files with 376 additions and 207 deletions.
7 changes: 3 additions & 4 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"net/http"
"net/url"
"os/exec"
"runtime"
"strings"

"github.com/anchordotdev/cli"
Expand Down Expand Up @@ -66,7 +65,7 @@ func NewClient(cfg *cli.Config) (*Session, error) {
if apiToken, err = kr.Get(keyring.APIToken); err == keyring.ErrNotFound {
return anc, ErrSignedOut
}
if err != nil && gnomeKeyringMissing() {
if err != nil && gnomeKeyringMissing(cfg) {
return anc, ErrGnomeKeyringRequired
}

Expand Down Expand Up @@ -390,8 +389,8 @@ const NotFoundErr = StatusCodeError(http.StatusNotFound)
func (err StatusCodeError) StatusCode() int { return int(err) }
func (err StatusCodeError) Error() string { return fmt.Sprintf("unexpected %d status response", err) }

func gnomeKeyringMissing() bool {
if runtime.GOOS != "linux" {
func gnomeKeyringMissing(cfg *cli.Config) bool {
if cfg.GOOS() != "linux" {
return false
}
if path, _ := exec.LookPath("gnome-keyring-daemon"); path != "" {
Expand Down
2 changes: 1 addition & 1 deletion auth/testdata/TestCmdAuth/--help.golden
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Manage Anchor.dev Authentication

Usage:
anchor auth <subcommand> [flags]
anchor auth [flags]
anchor auth [command]

Available Commands:
Expand Down
2 changes: 1 addition & 1 deletion auth/testdata/TestCmdAuth/auth.golden
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Manage Anchor.dev Authentication

Usage:
anchor auth <subcommand> [flags]
anchor auth [flags]
anchor auth [command]

Available Commands:
Expand Down
5 changes: 3 additions & 2 deletions cert/models/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ func (m *Provision) View() string {
fmt.Fprintln(&b, ui.StepDone(fmt.Sprintf("Wrote certificate to %s", ui.Emphasize(m.certFile))))
fmt.Fprintln(&b, ui.StepDone(fmt.Sprintf("Wrote chain to %s", ui.Emphasize(m.chainFile))))
fmt.Fprintln(&b, ui.StepDone(fmt.Sprintf("Wrote key to %s", ui.Emphasize(m.keyFile))))
fmt.Fprintln(&b, ui.StepHint("You can use these certificate files to manually configure your application and"))
fmt.Fprintln(&b, ui.StepHint("start using HTTPS in your development environment."))

fmt.Fprintln(&b, ui.StepHint("To use these certificates please reference your language and/or framework docs."))
fmt.Fprintln(&b, ui.StepHint("When these certificates expire, rerun `anchor lcl mkcert` to generate new ones."))

return b.String()
}
91 changes: 1 addition & 90 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"go/build"
"io/fs"
"net/url"
"os"
"regexp"
Expand Down Expand Up @@ -49,89 +48,6 @@ func VersionString() string {
return fmt.Sprintf("%s (%s/%s) Commit: %s BuildDate: %s", Version.Version, Version.Os, Version.Arch, Version.Commit, Version.Date)
}

type Config struct {
JSON bool `desc:"Only print JSON output to STDOUT." flag:"json,j" env:"JSON_OUTPUT" toml:"json-output"`
NonInteractive bool `desc:"Run without ever asking for user input." flag:"non-interactive,n" env:"NON_INTERACTIVE" toml:"non-interactive"`
Verbose bool `desc:"Verbose output." flag:"verbose,v" env:"VERBOSE" toml:"verbose"`

AnchorURL string `default:"https://anchor.dev" desc:"TODO" flag:"host" env:"ANCHOR_HOST" toml:"anchor-host"`

API struct {
URL string `default:"https://api.anchor.dev/v0" desc:"Anchor API endpoint URL." flag:"api-url,u" env:"API_URL" json:"api_url" toml:"api-url"`
Token string `desc:"Anchor API personal access token (PAT)." flag:"api-token,t" env:"API_TOKEN" json:"api_token" toml:"token"`
}

Lcl struct {
Service string `desc:"Name for lcl.host diagnostic service." flag:"service" env:"SERVICE" json:"service" toml:"service"`
Subdomain string `desc:"Subdomain for lcl.host diagnostic service." flag:"subdomain" env:"SUBDOMAIN" json:"subdomain" toml:"subdomain"`

DiagnosticAddr string `default:":4433" desc:"Local server address" flag:"addr,a" env:"ADDR" json:"address" toml:"address"`
LclHostURL string `default:"https://lcl.host" env:"LCL_HOST_URL"`

Audit struct {
} `cmd:"audit"`

Clean struct {
} `cmd:"clean"`

Config struct {
} `cmd:"config"`

MkCert struct {
Domains []string `flag:"domains"`
SubCa string `flag:"subca"`
} `cmd:"mkcert"`

Setup struct {
Language string `desc:"Language to use for integrating Anchor." flag:"language" json:"language" toml:"language"`
} `cmd:"setup"`
} `cmd:"lcl"`

Test struct {
Browserless bool `desc:"run as though browserless"`
SkipRunE bool `desc:"skip RunE for testing purposes"`
}

Trust struct {
Org string `desc:"organization" flag:"org,o" env:"ORG" json:"org" toml:"org"`
Realm string `desc:"realm" flag:"realm,r" env:"REALM" json:"realm" toml:"realm"`

NoSudo bool `desc:"Disable sudo prompts." flag:"no-sudo" env:"NO_SUDO" toml:"no-sudo"`

MockMode bool `env:"ANCHOR_CLI_TRUSTSTORE_MOCK_MODE"`

Stores []string `default:"[system,nss,homebrew]" desc:"trust stores" flag:"trust-stores" env:"TRUST_STORES" toml:"trust-stores"`

Audit struct{} `cmd:"audit"`

Clean struct {
States []string `default:"[expired]" desc:"cert state(s)" flag:"cert-states" env:"CERT_STATES" toml:"cert-states"`
} `cmd:"clean"`
} `cmd:"trust"`

User struct {
Auth struct {
SignIn struct{} `cmd:"signin"`

SignOut struct{} `cmd:"signout"`

WhoAmI struct{} `cmd:"whoami"`
} `cmd:"auth"`
} `group:"user,user management" toml:"user"`

Keyring struct {
MockMode bool `env:"ANCHOR_CLI_KEYRING_MOCK_MODE"`
}

Version struct{} `cmd:"version"`

// values used for testing

GOOS string `desc:"change OS identifier in tests"`
ProcFS fs.FS `desc:"change the proc filesystem in tests"`
Timestamp time.Time `desc:"timestamp to use/display in tests"`
}

type UI struct {
RunTUI func(context.Context, *ui.Driver) error
}
Expand Down Expand Up @@ -228,12 +144,7 @@ func ReportError(ctx context.Context, drv *ui.Driver, cmd *cobra.Command, args [
fmt.Fprintf(&body, "**Version:** `%s`\n", VersionString())
fmt.Fprintf(&body, "**Arguments:** `[%s]`\n", strings.Join(args, ", "))
fmt.Fprintf(&body, "**Flags:** `[%s]`\n", strings.Join(flags, ", "))

timestamp := cfg.Timestamp
if timestamp.IsZero() {
timestamp = time.Now().UTC()
}
fmt.Fprintf(&body, "**Timestamp:** `%s`\n", timestamp.Format(time.RFC3339Nano))
fmt.Fprintf(&body, "**Timestamp:** `%s`\n", cfg.Timestamp().Format(time.RFC3339Nano))
if stack != "" {
fmt.Fprintf(&body, "**Stack:**\n```\n%s\n```\n", normalizeStack(stack))
}
Expand Down
32 changes: 14 additions & 18 deletions cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,13 @@ func TestError(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

var err error
cfg := cli.Config{}
cfg.NonInteractive = true
cfg.Test.Browserless = true
cfg.Timestamp = Timestamp
if err != nil {
t.Fatal(err)
}
ctx = cli.ContextWithConfig(ctx, &cfg)
ctx = cli.ContextWithConfig(ctx, &cli.Config{
NonInteractive: true,
Test: cli.ConfigTest{
Browserless: true,
Timestamp: Timestamp,
},
})

t.Run(fmt.Sprintf("golden-%s", testTag()), func(t *testing.T) {
var returnedError error
Expand Down Expand Up @@ -124,15 +122,13 @@ func TestPanic(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

var err error
cfg := cli.Config{}
cfg.NonInteractive = true
cfg.Test.Browserless = true
cfg.Timestamp = Timestamp
if err != nil {
t.Fatal(err)
}
ctx = cli.ContextWithConfig(ctx, &cfg)
ctx = cli.ContextWithConfig(ctx, &cli.Config{
NonInteractive: true,
Test: cli.ConfigTest{
Browserless: true,
Timestamp: Timestamp,
},
})

t.Run(fmt.Sprintf("golden-%s", testTag()), func(t *testing.T) {
var returnedError error
Expand Down
2 changes: 1 addition & 1 deletion cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var rootDef = CmdDef{
{
Name: "auth",

Use: "auth <subcommand> [flags]",
Use: "auth [flags]",
Args: cobra.NoArgs,
Short: "Manage Anchor.dev Authentication",

Expand Down
122 changes: 122 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package cli

import (
"io/fs"
"os"
"runtime"
"time"
)

type Config struct {
JSON bool `desc:"Only print JSON output to STDOUT." flag:"json,j" env:"JSON_OUTPUT" toml:"json-output"`
NonInteractive bool `desc:"Run without ever asking for user input." flag:"non-interactive,n" env:"NON_INTERACTIVE" toml:"non-interactive"`
Verbose bool `desc:"Verbose output." flag:"verbose,v" env:"VERBOSE" toml:"verbose"`

AnchorURL string `default:"https://anchor.dev" desc:"TODO" flag:"host" env:"ANCHOR_HOST" toml:"anchor-host"`

API struct {
URL string `default:"https://api.anchor.dev/v0" desc:"Anchor API endpoint URL." flag:"api-url,u" env:"API_URL" json:"api_url" toml:"api-url"`
Token string `desc:"Anchor API personal access token (PAT)." flag:"api-token,t" env:"API_TOKEN" json:"api_token" toml:"token"`
}

Lcl struct {
Service string `desc:"Name for lcl.host diagnostic service." flag:"service" env:"SERVICE" json:"service" toml:"service"`
Subdomain string `desc:"Subdomain for lcl.host diagnostic service." flag:"subdomain" env:"SUBDOMAIN" json:"subdomain" toml:"subdomain"`

DiagnosticAddr string `default:":4433" desc:"Local server address" flag:"addr,a" env:"ADDR" json:"address" toml:"address"`
LclHostURL string `default:"https://lcl.host" env:"LCL_HOST_URL"`

Audit struct {
} `cmd:"audit"`

Clean struct {
} `cmd:"clean"`

Config struct {
} `cmd:"config"`

MkCert struct {
Domains []string `flag:"domains"`
SubCa string `flag:"subca"`
} `cmd:"mkcert"`

Setup struct {
Language string `desc:"Language to use for integrating Anchor." flag:"language" json:"language" toml:"language"`
Method string `desc:"Integration method for certificates."`
} `cmd:"setup"`
} `cmd:"lcl"`

Service struct {
Probe struct {
Name string `desc:"service name"`

Org string `desc:"organization" flag:"org,o" env:"ORG" json:"org" toml:"org"`
Realm string `desc:"realm" flag:"realm,r" env:"REALM" json:"realm" toml:"realm"`
} `cmd:"probe"`
} `cmd:"service"`

Trust struct {
Org string `desc:"organization" flag:"org,o" env:"ORG" json:"org" toml:"org"`
Realm string `desc:"realm" flag:"realm,r" env:"REALM" json:"realm" toml:"realm"`

NoSudo bool `desc:"Disable sudo prompts." flag:"no-sudo" env:"NO_SUDO" toml:"no-sudo"`

MockMode bool `env:"ANCHOR_CLI_TRUSTSTORE_MOCK_MODE"`

Stores []string `default:"[system,nss,homebrew]" desc:"trust stores" flag:"trust-stores" env:"TRUST_STORES" toml:"trust-stores"`

Audit struct{} `cmd:"audit"`

Clean struct {
States []string `default:"[expired]" desc:"cert state(s)" flag:"cert-states" env:"CERT_STATES" toml:"cert-states"`
} `cmd:"clean"`
} `cmd:"trust"`

User struct {
Auth struct {
SignIn struct{} `cmd:"signin"`

SignOut struct{} `cmd:"signout"`

WhoAmI struct{} `cmd:"whoami"`
} `cmd:"auth"`
} `group:"user,user management" toml:"user"`

Keyring struct {
MockMode bool `env:"ANCHOR_CLI_KEYRING_MOCK_MODE"`
}

Version struct{} `cmd:"version"`

Test ConfigTest
}

// values used for testing
type ConfigTest struct {
Browserless bool `desc:"run as though browserless"`
GOOS string `desc:"change OS identifier in tests"`
ProcFS fs.FS `desc:"change the proc filesystem in tests"`
SkipRunE bool `desc:"skip RunE for testing purposes"`
Timestamp time.Time `desc:"timestamp to use/display in tests"`
}

func (c Config) GOOS() string {
if goos := c.Test.GOOS; goos != "" {
return goos
}
return runtime.GOOS
}

func (c Config) ProcFS() fs.FS {
if procFS := c.Test.ProcFS; procFS != nil {
return procFS
}
return os.DirFS("/proc")
}

func (c Config) Timestamp() time.Time {
if timestamp := c.Test.Timestamp; !timestamp.IsZero() {
return timestamp
}
return time.Now().UTC()
}
1 change: 1 addition & 0 deletions lcl/lcl.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var CmdLcl = cli.NewCmd[Command](cli.CmdRoot, "lcl", func(cmd *cobra.Command) {

// setup
cmd.Flags().StringVar(&cfg.Lcl.Setup.Language, "language", "", "Language to integrate with Anchor.")
cmd.Flags().StringVar(&cfg.Lcl.Setup.Method, "method", "", "Integration method for certificates.")
})

type Command struct {
Expand Down
13 changes: 13 additions & 0 deletions lcl/lcl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ func TestCmdLcl(t *testing.T) {
cfg := cmdtest.TestCfg(t, CmdLcl, "--language", "ruby")
require.Equal(t, "ruby", cfg.Lcl.Setup.Language)
})

t.Run("--method anchor", func(t *testing.T) {
cfg := cmdtest.TestCfg(t, CmdLcl, "--method", "anchor")
require.Equal(t, "anchor", cfg.Lcl.Setup.Method)
})
}

func TestLcl(t *testing.T) {
Expand Down Expand Up @@ -223,6 +228,14 @@ func TestLcl(t *testing.T) {
Type: tea.KeyEnter,
})

uitest.WaitForGoldenContains(t, drv, errc,
"? What certificate management method?",
)

tm.Send(tea.KeyMsg{
Type: tea.KeyEnter,
})

t.Skip("Pending workaround for consistent setup guide port value")

uitest.WaitForGoldenContains(t, drv, errc,
Expand Down
4 changes: 0 additions & 4 deletions lcl/models/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,6 @@ func (m LclConfig) View() string {
)))

fmt.Fprintln(&b, ui.StepHint("Next, we'll add your personal CA certificates to your system's trust stores."))
fmt.Fprintln(&b, ui.StepHint(fmt.Sprintf("%s %s",
ui.Accentuate("This may require sudo privileges, learn why here: "),
ui.URL("https://lcl.host/why-sudo"),
)))

return b.String()
}
Expand Down
Loading

0 comments on commit 91cc689

Please sign in to comment.