diff --git a/cmd/humanlog/account.go b/cmd/humanlog/account.go new file mode 100644 index 00000000..1ce766ae --- /dev/null +++ b/cmd/humanlog/account.go @@ -0,0 +1,158 @@ +package main + +import ( + "context" + "net/http" + + "github.com/humanlogio/humanlog/internal/pkg/config" + "github.com/humanlogio/humanlog/internal/pkg/state" + "github.com/humanlogio/humanlog/pkg/auth" + "github.com/urfave/cli" +) + +const ( + accountCmdName = "account" +) + +func accountCmd( + getCtx func(cctx *cli.Context) context.Context, + getCfg func(cctx *cli.Context) *config.Config, + getState func(cctx *cli.Context) *state.State, + getTokenSource func(cctx *cli.Context) *auth.UserRefreshableTokenSource, + getAPIUrl func(cctx *cli.Context) string, + getHTTPClient func(*cli.Context) *http.Client, +) cli.Command { + return cli.Command{ + Name: accountCmdName, + Usage: "Manage accounts for the current user or org.", + Before: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + _, err := ensureLoggedIn(ctx, cctx, state, tokenSource, apiURL, httpClient) + if err != nil { + return err + } + return nil + }, + Subcommands: []cli.Command{ + { + Name: "set-current", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + accountName := cctx.Args().First() + // lookup `accountName` and set its ID in `state` + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + _ = accountName + + return nil + }, + }, + { + Name: "get-current", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + accountName := cctx.Args().First() + // lookup `account ID` in state, and resolve to `accountName` via API + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + _ = accountName + + return nil + }, + }, + { + Name: "create", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + + return nil + }, + }, + { + Name: "get", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + + return nil + }, + }, + { + Name: "update", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + + return nil + }, + }, + { + Name: "list", + Usage: "list the accounts for the current user or org", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + + return nil + }, + }, + }, + } +} diff --git a/cmd/humanlog/machine.go b/cmd/humanlog/machine.go new file mode 100644 index 00000000..e4305c87 --- /dev/null +++ b/cmd/humanlog/machine.go @@ -0,0 +1,76 @@ +package main + +import ( + "context" + "fmt" + "net/http" + + "github.com/humanlogio/humanlog/internal/pkg/config" + "github.com/humanlogio/humanlog/internal/pkg/state" + "github.com/humanlogio/humanlog/pkg/auth" + "github.com/urfave/cli" +) + +const ( + machineCmdName = "machine" +) + +func machineCmd( + getCtx func(cctx *cli.Context) context.Context, + getCfg func(cctx *cli.Context) *config.Config, + getState func(cctx *cli.Context) *state.State, + getTokenSource func(cctx *cli.Context) *auth.UserRefreshableTokenSource, + getAPIUrl func(cctx *cli.Context) string, + getHTTPClient func(*cli.Context) *http.Client, +) cli.Command { + return cli.Command{ + Name: machineCmdName, + Usage: "Manage machines in the current account.", + Before: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + _, err := ensureLoggedIn(ctx, cctx, state, tokenSource, apiURL, httpClient) + if err != nil { + return err + } + return nil + }, + Subcommands: []cli.Command{ + { + Name: "register", + Usage: "register this machine to save logs in account", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + accountToken, err := createIngestionToken(ctx, cctx, state, tokenSource, apiURL, httpClient) + if err != nil { + return fmt.Errorf("ingestion token couldn't be generated: %v", err) + } + state.IngestionToken = accountToken + if err := state.WriteBack(); err != nil { + return fmt.Errorf("writing back generated ingestion token: %v", err) + } + return nil + }, + }, + { + Name: "deregister", + Usage: "deregister this machine from saving logs in account", + Action: func(cctx *cli.Context) error { + state := getState(cctx) + state.IngestionToken = nil + if err := state.WriteBack(); err != nil { + return fmt.Errorf("writing back generated ingestion token: %v", err) + } + return nil + }, + }, + }, + } +} diff --git a/cmd/humanlog/main.go b/cmd/humanlog/main.go index 8efa97e6..21887c25 100644 --- a/cmd/humanlog/main.go +++ b/cmd/humanlog/main.go @@ -175,11 +175,22 @@ func newApp() *cli.App { } app := cli.NewApp() - app.Author = "Antoine Grondin" - app.Email = "antoinegrondin@gmail.com" + app.Author = "humanlog.io" + app.Email = "hi@humanlog.io" app.Name = "humanlog" app.Version = semverVersion.String() app.Usage = "reads structured logs from stdin, makes them pretty on stdout!" + app.Description = `humanlog parses logs and makes them easier to read and search. + + When invoked with no argument, it consumes stdin and parses it, + attempts to make it prettier on stdout. It also allows searching + the logs that were parsed, both in a TUI by pressing "s" or in a + webapp by pressing "space". + + If registered to ingest logs via "humanlog machine register" logs + will be saved to humanlog.io for vizualization, searching and + analysis. +` var ( ctx context.Context @@ -297,6 +308,9 @@ func newApp() *cli.App { app.Commands, versionCmd(getCtx, getLogger, getCfg, getState, getTokenSource, getAPIUrl, getHTTPClient), authCmd(getCtx, getCfg, getState, getTokenSource, getAPIUrl, getHTTPClient), + organizationCmd(getCtx, getCfg, getState, getTokenSource, getAPIUrl, getHTTPClient), + accountCmd(getCtx, getCfg, getState, getTokenSource, getAPIUrl, getHTTPClient), + machineCmd(getCtx, getCfg, getState, getTokenSource, getAPIUrl, getHTTPClient), queryCmd(getCtx, getCfg, getState, getTokenSource, getAPIUrl, getHTTPClient), gennyCmd(getCtx, getCfg, getState), ) diff --git a/cmd/humanlog/organization.go b/cmd/humanlog/organization.go new file mode 100644 index 00000000..f6297b5d --- /dev/null +++ b/cmd/humanlog/organization.go @@ -0,0 +1,221 @@ +package main + +import ( + "context" + "net/http" + + "github.com/humanlogio/humanlog/internal/pkg/config" + "github.com/humanlogio/humanlog/internal/pkg/state" + "github.com/humanlogio/humanlog/pkg/auth" + "github.com/urfave/cli" +) + +const ( + organizationCmdName = "organization" +) + +func organizationCmd( + getCtx func(cctx *cli.Context) context.Context, + getCfg func(cctx *cli.Context) *config.Config, + getState func(cctx *cli.Context) *state.State, + getTokenSource func(cctx *cli.Context) *auth.UserRefreshableTokenSource, + getAPIUrl func(cctx *cli.Context) string, + getHTTPClient func(*cli.Context) *http.Client, +) cli.Command { + return cli.Command{ + Name: organizationCmdName, + ShortName: "org", + Usage: "Manage organizations for the current user.", + Before: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + _, err := ensureLoggedIn(ctx, cctx, state, tokenSource, apiURL, httpClient) + if err != nil { + return err + } + return nil + }, + Subcommands: []cli.Command{ + { + Name: "set-current", + Usage: "set the org currently configured in the CLI", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + organizationName := cctx.Args().First() + // lookup `organizationName` and set its ID in `state` + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + _ = organizationName + + return nil + }, + }, + { + Name: "get-current", + Usage: "get the org currently configured in the CLI", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + organizationName := cctx.Args().First() + // lookup `organization ID` in state, and resolve to `organizationName` via API + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + _ = organizationName + + return nil + }, + }, + { + Name: "create", + Usage: "create an org", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + + return nil + }, + }, + { + Name: "get", + Usage: "get an org's details", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + + return nil + }, + }, + { + Name: "update", + Usage: "update an org's details", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + + return nil + }, + }, + { + Name: "list-orgs", + Usage: "list the orgs you belong to", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + + return nil + }, + }, + { + Name: "list-users", + Usage: "list the users in an org you belong to", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + + return nil + }, + }, + { + Name: "invite", + Usage: "invite someone to access an org", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + + return nil + }, + }, + { + Name: "revoke", + Usage: "revoke someone's access to an org", + Action: func(cctx *cli.Context) error { + ctx := getCtx(cctx) + state := getState(cctx) + tokenSource := getTokenSource(cctx) + apiURL := getAPIUrl(cctx) + httpClient := getHTTPClient(cctx) + + _ = ctx + _ = state + _ = tokenSource + _ = apiURL + _ = httpClient + + return nil + }, + }, + }, + } +}