From 864ca4ccd69930ff5879ef083a54f873b9e24b19 Mon Sep 17 00:00:00 2001 From: Damiano Donati Date: Mon, 11 Sep 2023 00:06:29 +0200 Subject: [PATCH] wip: gok: add gus command + subcommands --- go.mod | 13 +++++- go.sum | 32 ++++++++++++-- internal/gok/diff.go | 97 +++++++++++++++++++++++++++++++++++++++++++ internal/gok/gus.go | 35 ++++++++++++++++ internal/gok/root.go | 1 + internal/gok/set.go | 72 ++++++++++++++++++++++++++++++++ internal/gok/unset.go | 64 ++++++++++++++++++++++++++++ 7 files changed, 308 insertions(+), 6 deletions(-) create mode 100644 internal/gok/diff.go create mode 100644 internal/gok/gus.go create mode 100644 internal/gok/set.go create mode 100644 internal/gok/unset.go diff --git a/go.mod b/go.mod index 798f093..ec065de 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,11 @@ module github.com/gokrazy/tools go 1.19 require ( + github.com/antihax/optional v1.0.0 github.com/breml/rootcerts v0.2.10 github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 - github.com/gokrazy/internal v0.0.0-20231010202000-a93c67aeb340 + github.com/gokrazy/gokapi v0.0.0-20230221202227-fc8991bfa4ab + github.com/gokrazy/internal v0.0.0-20231013065356-235d00ca4845 github.com/gokrazy/updater v0.0.0-20230215172637-813ccc7f21e2 github.com/google/go-cmp v0.5.9 github.com/google/renameio/v2 v2.0.0 @@ -16,4 +18,11 @@ require ( golang.org/x/sys v0.5.0 ) -require github.com/inconshreveable/mousetrap v1.1.0 // indirect +require ( + github.com/golang/protobuf v1.5.2 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + golang.org/x/net v0.6.0 // indirect + golang.org/x/oauth2 v0.5.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.28.0 // indirect +) diff --git a/go.sum b/go.sum index 337ea11..a166c33 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,21 @@ +github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/breml/rootcerts v0.2.10 h1:UGVZ193UTSUASpGtg6pbDwzOd7XQP+at0Ssg1/2E4h8= github.com/breml/rootcerts v0.2.10/go.mod h1:24FDtzYMpqIeYC7QzaE8VPRQaFZU5TIUDlyk8qwjD88= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/damdo/internal v0.0.0-20231010185710-329afa6a2d6e h1:8gKsjwVk9zS/SYhK0f5wmGE6Mm1fTd3Xo17JPSNYC+0= -github.com/damdo/internal v0.0.0-20231010185710-329afa6a2d6e/go.mod h1:CIE3ta1pA9UGyV1BM6wSc9BvNIZTm6keFCy/ifi6PCw= github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 h1:C7t6eeMaEQVy6e8CarIhscYQlNmw5e3G36y7l7Y21Ao= github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0/go.mod h1:56wL82FO0bfMU5RvfXoIwSOP2ggqqxT+tAfNEIyxuHw= -github.com/gokrazy/internal v0.0.0-20231010202000-a93c67aeb340 h1:ddYQ2L0WsOFaD28ex00IgDP9r3oRlDVyprwaUk2BGYo= -github.com/gokrazy/internal v0.0.0-20231010202000-a93c67aeb340/go.mod h1:CIE3ta1pA9UGyV1BM6wSc9BvNIZTm6keFCy/ifi6PCw= +github.com/gokrazy/gokapi v0.0.0-20230221202227-fc8991bfa4ab h1:w40XuAWHVKBrglRpyTGDCxn6LgTB2IhrAjRadZuYkVk= +github.com/gokrazy/gokapi v0.0.0-20230221202227-fc8991bfa4ab/go.mod h1:gsfT8xJ3ocl55b26EKYqBihG/Z5iu/9AP/6YlaAd16U= +github.com/gokrazy/internal v0.0.0-20231013065356-235d00ca4845 h1:wMiZodG33pO4pTMAO9g4uWmuBkv5m0KOJ6XlVygA+CU= +github.com/gokrazy/internal v0.0.0-20231013065356-235d00ca4845/go.mod h1:CIE3ta1pA9UGyV1BM6wSc9BvNIZTm6keFCy/ifi6PCw= github.com/gokrazy/updater v0.0.0-20230215172637-813ccc7f21e2 h1:kBY5R1tSf+EYZ+QaSrofLaVJtBqYsVNVBWkdMq3Smcg= github.com/gokrazy/updater v0.0.0-20230215172637-813ccc7f21e2/go.mod h1:PYOvzGOL4nlBmuxu7IyKQTFLaxr61+WPRNRzVtuYOHw= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= @@ -21,11 +28,28 @@ 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= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/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.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/gok/diff.go b/internal/gok/diff.go new file mode 100644 index 0000000..398ec85 --- /dev/null +++ b/internal/gok/diff.go @@ -0,0 +1,97 @@ +package gok + +import ( + "bytes" + "context" + "fmt" + "io" + "strings" + + "github.com/antihax/optional" + "github.com/gokrazy/gokapi/gusapi" + "github.com/gokrazy/internal/config" + "github.com/gokrazy/internal/instanceflag" + "github.com/spf13/cobra" +) + +// diffCmd is gok gus diff. +var diffCmd = &cobra.Command{ + // GroupID: "server", + Use: "diff", + Short: "Compare the SBOM on disk with the one set on the remote GUS server", + Long: `gok gus diff compares the SBOM of the gokrazy instance on disk with the one currently set on the remote GUS server + +Examples: + # check if there is diff between a local and remote GUS server's SBOM + % gok -i scanner diff --server gus.gokrazy.org +`, + RunE: func(cmd *cobra.Command, args []string) error { + return diffImpl.run(cmd.Context(), args, cmd.OutOrStdout(), cmd.OutOrStderr()) + }, +} + +type diffConfig struct { + server string +} + +var diffImpl diffConfig + +func init() { + diffCmd.Flags().StringVarP(&diffImpl.server, "server", "", "", "HTTP(S) URL to the server to diff against") + instanceflag.RegisterPflags(diffCmd.Flags()) + diffCmd.MarkFlagRequired("server") +} + +func (r *diffConfig) run(ctx context.Context, args []string, stdout, stderr io.Writer) error { + cfg, err := config.ReadFromFile() + if err != nil { + return err + } + + machineID, err := getMachineID(cfg) + if err != nil { + return fmt.Errorf("error getting machineID: %w", err) + } + + sc := sbomConfig{format: "hash"} + stdOutbuf := new(bytes.Buffer) + if err := sc.run(ctx, nil, stdOutbuf, nil); err != nil { + return err + } + localSBOMHash := strings.TrimSpace(stdOutbuf.String()) + + gusCfg := gusapi.NewConfiguration() + gusCfg.BasePath = r.server + gusCli := gusapi.NewAPIClient(gusCfg) + + response, _, err := gusCli.UpdateApi.Update(ctx, &gusapi.UpdateApiUpdateOpts{ + Body: optional.NewInterface(&gusapi.UpdateRequest{ + MachineId: machineID, + }), + }) + if err != nil { + return fmt.Errorf("error making update request to GUS server: %w", err) + } + + if localSBOMHash != response.SbomHash { + return fmt.Errorf("local: %s != remote: %s", localSBOMHash, response.SbomHash) + } + + return nil +} + +func getMachineID(cfg *config.Struct) (string, error) { + if cfg == nil { + return "", fmt.Errorf("error reading nil config") + } + v, ok := cfg.PackageConfig["github.com/gokrazy/gokrazy/cmd/randomd"] + if !ok { + return "", fmt.Errorf("error undefined machineID") + } + rawMachineID, ok := v.ExtraFileContents["/etc/machine-id"] + if !ok { + return "", fmt.Errorf("error undefined machineID") + } + + return strings.TrimSpace(rawMachineID), nil +} diff --git a/internal/gok/gus.go b/internal/gok/gus.go new file mode 100644 index 0000000..cf74e67 --- /dev/null +++ b/internal/gok/gus.go @@ -0,0 +1,35 @@ +package gok + +import ( + "github.com/gokrazy/internal/instanceflag" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +// gusCmd is gok push. +var gusCmd = &cobra.Command{ + GroupID: "server", + Use: "gus", + Short: "Interacts with a remote GUS server", + Long: `gok gus interacts with a remote GUS server. + +When the --json flag is specified, the server response is printed to stdout. + +Examples: + # interact with a remote GUS server + % gok gus diff ... + % gok gus set ... + % gok gus unset ... + +`, + RunE: func(cmd *cobra.Command, args []string) error { + return pflag.ErrHelp + }, +} + +func init() { + instanceflag.RegisterPflags(gusCmd.Flags()) + gusCmd.AddCommand(diffCmd) + gusCmd.AddCommand(setCmd) + gusCmd.AddCommand(unsetCmd) +} diff --git a/internal/gok/root.go b/internal/gok/root.go index 2a6eb83..a8d49ca 100644 --- a/internal/gok/root.go +++ b/internal/gok/root.go @@ -71,4 +71,5 @@ func init() { RootCmd.AddCommand(getCmd) RootCmd.AddCommand(sbomCmd) RootCmd.AddCommand(pushCmd) + RootCmd.AddCommand(gusCmd) } diff --git a/internal/gok/set.go b/internal/gok/set.go new file mode 100644 index 0000000..aa029cd --- /dev/null +++ b/internal/gok/set.go @@ -0,0 +1,72 @@ +package gok + +import ( + "context" + "fmt" + "io" + + "github.com/antihax/optional" + "github.com/gokrazy/gokapi/gusapi" + "github.com/gokrazy/internal/instanceflag" + "github.com/spf13/cobra" +) + +// setCmd is gok gus set. +var setCmd = &cobra.Command{ + // GroupID: "server", + Use: "set", + Short: "Set the desired SBOM version for a machineID pattern on the remote GUS server", + Long: `gok gus set sets the SBOM version for a machineID pattern on the remote GUS server + +Examples: + # check if there is set between a local and remote GUS server's SBOM + % gok -i scanner set --server gus.gokrazy.org --sbom_hash="..." --download_link="..." --registry_type="..." --machine_id_pattern="" +`, + RunE: func(cmd *cobra.Command, args []string) error { + return setImpl.run(cmd.Context(), args, cmd.OutOrStdout(), cmd.OutOrStderr()) + }, +} + +type setConfig struct { + server string + sbomHash string + registryType string + downloadLink string + machineIDPattern string +} + +var setImpl setConfig + +func init() { + setCmd.Flags().StringVarP(&setImpl.server, "server", "", "", "HTTP(S) URL to the server to set against") + setCmd.Flags().StringVarP(&setImpl.sbomHash, "sbom_hash", "", "", "The version (SBOM Hash string) of the desired gokrazy image") + setCmd.Flags().StringVarP(&setImpl.downloadLink, "download_link", "", "", "relative (localdisk registry) or absolute download link with which gokrazy devices can download the build") + setCmd.Flags().StringVarP(&setImpl.registryType, "registry_type", "", "", "The type of registry on which the build is stored. see download_link") + setCmd.Flags().StringVarP(&setImpl.machineIDPattern, "machine_id_pattern", "", "", "The pattern to match the machineIDs to which apply the provided version") + instanceflag.RegisterPflags(setCmd.Flags()) + setCmd.MarkFlagRequired("server") + setCmd.MarkFlagRequired("sbom_hash") + setCmd.MarkFlagRequired("machine_id_pattern") + setCmd.MarkFlagRequired("registry_type") + setCmd.MarkFlagRequired("download_link") +} + +func (r *setConfig) run(ctx context.Context, args []string, stdout, stderr io.Writer) error { + gusCfg := gusapi.NewConfiguration() + gusCfg.BasePath = r.server + gusCli := gusapi.NewAPIClient(gusCfg) + + _, _, err := gusCli.IngestApi.Ingest(ctx, &gusapi.IngestApiIngestOpts{ + Body: optional.NewInterface(&gusapi.IngestRequest{ + MachineIdPattern: r.machineIDPattern, + SbomHash: r.sbomHash, + RegistryType: r.registryType, + DownloadLink: r.downloadLink, + }), + }) + if err != nil { + return fmt.Errorf("error making update request to GUS server: %w", err) + } + + return nil +} diff --git a/internal/gok/unset.go b/internal/gok/unset.go new file mode 100644 index 0000000..46c0b5b --- /dev/null +++ b/internal/gok/unset.go @@ -0,0 +1,64 @@ +package gok + +import ( + "context" + "fmt" + "io" + + "github.com/gokrazy/internal/instanceflag" + "github.com/spf13/cobra" +) + +// unsetCmd is gok gus unset. +var unsetCmd = &cobra.Command{ + // GroupID: "server", + Use: "unset", + Short: "unset the desired SBOM version for a machineID pattern on the remote GUS server", + Long: `gok gus unset unsets the SBOM version for a machineID pattern on the remote GUS server + +Examples: + # check if there is unset between a local and remote GUS server's SBOM + % gok -i scanner unset --server gus.gokrazy.org +`, + RunE: func(cmd *cobra.Command, args []string) error { + return unsetImpl.run(cmd.Context(), args, cmd.OutOrStdout(), cmd.OutOrStderr()) + }, +} + +type unsetConfig struct { + server string + machineIDPattern string +} + +var unsetImpl unsetConfig + +func init() { + unsetCmd.Flags().StringVarP(&unsetImpl.server, "server", "", "", "HTTP(S) URL to the server to unset against") + unsetCmd.Flags().StringVarP(&unsetImpl.machineIDPattern, "machine_id_pattern", "", "", "The pattern to match the machineIDs to which apply the provided version") + instanceflag.RegisterPflags(unsetCmd.Flags()) + unsetCmd.MarkFlagRequired("server") + unsetCmd.MarkFlagRequired("machine_id_pattern") +} + +func (r *unsetConfig) run(ctx context.Context, args []string, stdout, stderr io.Writer) error { + fmt.Println("NOT IMPLEMENTED!") + + // // TODO: finish this when gokapi has a method for unsetting. + // gusCfg := gusapi.NewConfiguration() + // gusCfg.BasePath = r.server + // gusCli := gusapi.NewAPIClient(gusCfg) + + // _, _, err := gusCli.IngestApi.Ingest(ctx, &gusapi.IngestApiIngestOpts{ + // Body: optional.NewInterface(&gusapi.IngestRequest{ + // MachineIdPattern: r.machineIDPattern, + // SbomHash: "", + // RegistryType: "", + // DownloadLink: "", + // }), + // }) + // if err != nil { + // return fmt.Errorf("error making update request to GUS server: %w", err) + // } + + return nil +}