diff --git a/api/error.go b/api/error.go new file mode 100644 index 0000000000..109132b3b2 --- /dev/null +++ b/api/error.go @@ -0,0 +1,44 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2020-05-03 14:33:51.4620286 -0400 EDT m=+0.042201401 +package api + +import ( + "encoding/json" + + "github.com/fatih/structs" + "github.com/hashicorp/watchtower/api/internal/strutil" +) + +type Error struct { + defaultFields []string + + // The HTTP Status code applicable to this error + Status *int64 `json:"status,omitempty"` + // An application-specific error string + Code *string `json:"code,omitempty"` + // A human readable explanation specific to this occurrence of the error + Message *string `json:"message,omitempty"` + // Additional metadata regarding the error. Depending on the error + // different fields will be populated. + Details *ErrorDetails `json:"details,omitempty"` +} + +func (s *Error) SetDefault(key string) { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) +} + +func (s *Error) UnsetDefault(key string) { + s.defaultFields = strutil.StrListDelete(s.defaultFields, key) +} + +func (s Error) MarshalJSON() ([]byte, error) { + m := structs.Map(s) + if m == nil { + m = make(map[string]interface{}) + } + for _, k := range s.defaultFields { + m[k] = nil + } + return json.Marshal(m) +} diff --git a/api/error_details.go b/api/error_details.go new file mode 100644 index 0000000000..5af50d3fe8 --- /dev/null +++ b/api/error_details.go @@ -0,0 +1,38 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2020-05-03 14:33:51.462308 -0400 EDT m=+0.042480801 +package api + +import ( + "encoding/json" + + "github.com/fatih/structs" + "github.com/hashicorp/watchtower/api/internal/strutil" +) + +type ErrorDetails struct { + defaultFields []string + + TraceId *string `json:"trace_id,omitempty"` + RequestId *string `json:"request_id,omitempty"` + RequestFields []string `json:"request_fields,omitempty"` +} + +func (s *ErrorDetails) SetDefault(key string) { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) +} + +func (s *ErrorDetails) UnsetDefault(key string) { + s.defaultFields = strutil.StrListDelete(s.defaultFields, key) +} + +func (s ErrorDetails) MarshalJSON() ([]byte, error) { + m := structs.Map(s) + if m == nil { + m = make(map[string]interface{}) + } + for _, k := range s.defaultFields { + m[k] = nil + } + return json.Marshal(m) +} diff --git a/api/hosts/awsec2_host_catalog.go b/api/hosts/awsec2_host_catalog.go new file mode 100644 index 0000000000..0123b9a5a0 --- /dev/null +++ b/api/hosts/awsec2_host_catalog.go @@ -0,0 +1,42 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2020-05-03 14:33:51.4628286 -0400 EDT m=+0.043001401 +package hosts + +import ( + "fmt" + + "github.com/mitchellh/mapstructure" +) + +type AwsEc2HostCatalog struct { + *HostCatalog + + // The AWS regions from which this catalog will retrieve the EC2 instances. + Regions []string `json:"regions,omitempty"` + // The access key used for authenticating with AWS when retrieving EC2 instance details. + AccessKey *string `json:"access_key,omitempty"` + // Input only. + SecretKey *string `json:"secret_key,omitempty"` + // This value will never be returned in a response. + Rotate *bool `json:"rotate,omitempty"` +} + +func (s HostCatalog) AsAwsEc2HostCatalog() (*AwsEc2HostCatalog, error) { + out := &AwsEc2HostCatalog{ + HostCatalog: &s, + } + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + Result: out, + TagName: "json", + }) + if err != nil { + return nil, fmt.Errorf("error creating map decoder: %w", err) + } + + if err := decoder.Decode(s.Attributes); err != nil { + return nil, fmt.Errorf("error decoding attributes map: %w", err) + } + + return out, nil +} diff --git a/api/hosts/host.go b/api/hosts/host.go new file mode 100644 index 0000000000..1c8a6b2afa --- /dev/null +++ b/api/hosts/host.go @@ -0,0 +1,53 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2020-05-03 14:33:51.4624224 -0400 EDT m=+0.042595201 +package hosts + +import ( + "encoding/json" + "time" + + "github.com/fatih/structs" + "github.com/hashicorp/watchtower/api/internal/strutil" +) + +type Host struct { + defaultFields []string + + // Canonical path of the resource from the API's base URI + // Output only. + Path *string `json:"path,omitempty"` + // The type of the resource, to help differentiate schemas + Type *string `json:"type,omitempty"` + // Friendly name, if set + FriendlyName *string `json:"friendly_name,omitempty"` + // The time this host was created + // Ouput only. + CreatedTime time.Time `json:"created_time,omitempty"` + // The time this host was last updated + // Output only. + UpdatedTime time.Time `json:"updated_time,omitempty"` + // Whether the host is disabled + Disabled *bool `json:"disabled,omitempty"` + // The address (DNS or IP name) used to reach the host + Address *string `json:"address,omitempty"` +} + +func (s *Host) SetDefault(key string) { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) +} + +func (s *Host) UnsetDefault(key string) { + s.defaultFields = strutil.StrListDelete(s.defaultFields, key) +} + +func (s Host) MarshalJSON() ([]byte, error) { + m := structs.Map(s) + if m == nil { + m = make(map[string]interface{}) + } + for _, k := range s.defaultFields { + m[k] = nil + } + return json.Marshal(m) +} diff --git a/api/hosts/host_catalog.go b/api/hosts/host_catalog.go new file mode 100644 index 0000000000..9e4d9c3370 --- /dev/null +++ b/api/hosts/host_catalog.go @@ -0,0 +1,54 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2020-05-03 14:33:51.4626029 -0400 EDT m=+0.042775701 +package hosts + +import ( + "encoding/json" + "time" + + "github.com/fatih/structs" + "github.com/hashicorp/watchtower/api/internal/strutil" +) + +type HostCatalog struct { + defaultFields []string + + // Canonical path of the resource from the API's base URI + // Output only. + Path *string `json:"path,omitempty"` + // The type of the resource, to help differentiate schemas + Type *string `json:"type,omitempty"` + // Friendly name, if set + FriendlyName *string `json:"friendly_name,omitempty"` + // The time this host was created + // Output only. + CreatedTime time.Time `json:"created_time,omitempty"` + // The time this host was last updated + // Output only. + UpdatedTime time.Time `json:"updated_time,omitempty"` + // Whether the catalog is disabled + Disabled *bool `json:"disabled,omitempty"` + // Attributes specific to the catalog type + Attributes map[string]interface { + } `json:"attributes,omitempty"` +} + +func (s *HostCatalog) SetDefault(key string) { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) +} + +func (s *HostCatalog) UnsetDefault(key string) { + s.defaultFields = strutil.StrListDelete(s.defaultFields, key) +} + +func (s HostCatalog) MarshalJSON() ([]byte, error) { + m := structs.Map(s) + if m == nil { + m = make(map[string]interface{}) + } + for _, k := range s.defaultFields { + m[k] = nil + } + return json.Marshal(m) +} diff --git a/api/hosts/host_set.go b/api/hosts/host_set.go new file mode 100644 index 0000000000..2a50da39c2 --- /dev/null +++ b/api/hosts/host_set.go @@ -0,0 +1,59 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2020-05-03 14:33:51.4625345 -0400 EDT m=+0.042707301 +package hosts + +import ( + "encoding/json" + "time" + + "github.com/fatih/structs" + "github.com/hashicorp/watchtower/api/internal/strutil" +) + +type HostSet struct { + defaultFields []string + + // Canonical path of the resource from the API's base URI + // Output only. + Path *string `json:"path,omitempty"` + // The type of the resource, to help differentiate schemas + Type *string `json:"type,omitempty"` + // Friendly name, if set + FriendlyName *string `json:"friendly_name,omitempty"` + // The time this host was created + // Output only. + CreatedTime time.Time `json:"created_time,omitempty"` + // The time this host was last updated + // Output only. + UpdatedTime time.Time `json:"updated_time,omitempty"` + // Whether the host set is disabled + Disabled *bool `json:"disabled,omitempty"` + // The total count of hosts in this host set + // Output only. + Size *int64 `json:"size,omitempty"` + // A list of hosts in this host set + // TODO: Figure out if this should be in the basic HostSet view and what + // view to use on the Hosts. + // Output only. + Hosts []*Host `json:"hosts,omitempty"` +} + +func (s *HostSet) SetDefault(key string) { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) +} + +func (s *HostSet) UnsetDefault(key string) { + s.defaultFields = strutil.StrListDelete(s.defaultFields, key) +} + +func (s HostSet) MarshalJSON() ([]byte, error) { + m := structs.Map(s) + if m == nil { + m = make(map[string]interface{}) + } + for _, k := range s.defaultFields { + m[k] = nil + } + return json.Marshal(m) +} diff --git a/api/hosts/static_host_catalog.go b/api/hosts/static_host_catalog.go new file mode 100644 index 0000000000..bc33b3b680 --- /dev/null +++ b/api/hosts/static_host_catalog.go @@ -0,0 +1,33 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2020-05-03 14:33:51.4627143 -0400 EDT m=+0.042887101 +package hosts + +import ( + "fmt" + + "github.com/mitchellh/mapstructure" +) + +type StaticHostCatalog struct { + *HostCatalog +} + +func (s HostCatalog) AsStaticHostCatalog() (*StaticHostCatalog, error) { + out := &StaticHostCatalog{ + HostCatalog: &s, + } + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + Result: out, + TagName: "json", + }) + if err != nil { + return nil, fmt.Errorf("error creating map decoder: %w", err) + } + + if err := decoder.Decode(s.Attributes); err != nil { + return nil, fmt.Errorf("error decoding attributes map: %w", err) + } + + return out, nil +} diff --git a/api/internal/genapi/genapi.go b/api/internal/genapi/genapi.go new file mode 100644 index 0000000000..bc34fe3cd8 --- /dev/null +++ b/api/internal/genapi/genapi.go @@ -0,0 +1,488 @@ +package main + +// +build genapi + +import ( + "bufio" + "bytes" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + "text/template" + "time" +) + +type templateType int + +const ( + templateTypeResource templateType = iota + templateTypeDetail +) + +type generateInfo struct { + inFile string + inName string + outFile string + outName string + outPkg string + structFields string + parentName string + detailName string + templateType templateType +} + +var ( + regex = regexp.MustCompile(`(json:".*")`) + + inputStructs = []*generateInfo{ + { + os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/error.pb.go", + "Error", + os.Getenv("APIGEN_BASEPATH") + "/api/error.go", + "Error", + "api", + "", + "", + "", + templateTypeResource, + }, + { + os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/error.pb.go", + "ErrorDetails", + os.Getenv("APIGEN_BASEPATH") + "/api/error_details.go", + "ErrorDetails", + "api", + "", + "", + "", + templateTypeResource, + }, + { + os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host.pb.go", + "Host", + os.Getenv("APIGEN_BASEPATH") + "/api/hosts/host.go", + "Host", + "hosts", + "", + "", + "", + templateTypeResource, + }, + { + os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_set.pb.go", + "HostSet", + os.Getenv("APIGEN_BASEPATH") + "/api/hosts/host_set.go", + "HostSet", + "hosts", + "", + "", + "", + templateTypeResource, + }, + { + os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go", + "HostCatalog", + os.Getenv("APIGEN_BASEPATH") + "/api/hosts/host_catalog.go", + "HostCatalog", + "hosts", + "", + "", + "", + templateTypeResource, + }, + { + os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go", + "StaticHostCatalogDetails", + os.Getenv("APIGEN_BASEPATH") + "/api/hosts/static_host_catalog.go", + "StaticHostCatalogDetails", + "hosts", + "", + "HostCatalog", + "StaticHostCatalog", + templateTypeDetail, + }, + { + os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go", + "AwsEc2HostCatalogDetails", + os.Getenv("APIGEN_BASEPATH") + "/api/hosts/awsec2_host_catalog.go", + "AwsEc2HostCatalogDetails", + "hosts", + "", + "HostCatalog", + "AwsEc2HostCatalog", + templateTypeDetail, + }, + } +) + +type visitFn func(node ast.Node) + +func (fn visitFn) Visit(node ast.Node) ast.Visitor { + fn(node) + return fn +} + +func main() { + createStructs() + createUtilFuncs() +} + +func createStructs() { + for _, inputStruct := range inputStructs { + inFile, err := filepath.Abs(inputStruct.inFile) + if err != nil { + fmt.Printf("error opening file %q: %v\n", inputStruct.inFile, err) + os.Exit(1) + } + + fset := token.NewFileSet() + inAst, err := parser.ParseFile(fset, inFile, nil, parser.ParseComments) + if err != nil { + fmt.Printf("error parsing %s: %v\n", inFile, err) + os.Exit(1) + } + + ast.Walk(visitFn(func(n ast.Node) { + spec, ok := n.(*ast.TypeSpec) + if !ok { + return + } + if spec.Name == nil { + return + } + if spec.Name.Name != inputStruct.inName { + return + } + + spec.Name.Name = inputStruct.outName + st, ok := spec.Type.(*ast.StructType) + if !ok { + fmt.Printf("expected struct type for identifier, got %t\n", spec.Type) + os.Exit(1) + return + } + + if st.Fields.List == nil { + fmt.Printf("no fields found in %q\n", inputStruct.inName) + os.Exit(1) + return + } + + // Tee up unexported field deletion + var elideList []int + { + defer func() { + var cutCount int + // Remove unexported proto stuff + for _, val := range elideList { + loc := val - cutCount + st.Fields.List = append(st.Fields.List[:loc], st.Fields.List[loc+1:]...) + cutCount++ + } + + // Add default fields if a base resource + if inputStruct.templateType == templateTypeResource { + st.Fields.List = append([]*ast.Field{{ + Names: []*ast.Ident{ + { + Name: "defaultFields", + Obj: &ast.Object{ + Kind: ast.Var, + Name: "defaultFields", + }, + }, + }, + Type: &ast.ArrayType{ + Elt: &ast.Ident{ + Name: "string", + }, + }, + }}, st.Fields.List...) + } + }() + } + + for i, field := range st.Fields.List { + if !field.Names[0].IsExported() { + elideList = append(elideList, i) + continue + } + + // Anything that isn't a basic type we expect to be a star + // expression with a selector; that is, a wrapper value, timestamp + // value, etc. + // + // TODO: this isn't necessarily a good assumption, which means we + // might get failures with other types. This is an internal tools + // only; we can revisit as needed! + var selectorExpr *ast.SelectorExpr + switch typ := field.Type.(type) { + case *ast.Ident: + typ.Name = "*" + typ.Name + goto TAGMODIFY + case *ast.ArrayType: + goto TAGMODIFY + case *ast.StarExpr: + switch nextTyp := typ.X.(type) { + case *ast.Ident: + // Already a pointer, don't do anything + goto TAGMODIFY + case *ast.SelectorExpr: + selectorExpr = nextTyp + } + case *ast.SelectorExpr: + selectorExpr = typ + } + + switch { + case selectorExpr != nil: + xident, ok := selectorExpr.X.(*ast.Ident) + if !ok { + fmt.Printf("unexpected non-ident type in selector\n") + os.Exit(1) + } + + switch xident.Name { + case "wrappers": + switch selectorExpr.Sel.Name { + case "StringValue": + st.Fields.List[i] = &ast.Field{ + Names: field.Names, + Type: &ast.Ident{ + Name: "*string", + }, + Tag: field.Tag, + } + case "BoolValue": + st.Fields.List[i] = &ast.Field{ + Names: field.Names, + Type: &ast.Ident{ + Name: "*bool", + }, + Tag: field.Tag, + } + case "Int64Value": + st.Fields.List[i] = &ast.Field{ + Names: field.Names, + Type: &ast.Ident{ + Name: "*int64", + }, + Tag: field.Tag, + } + default: + fmt.Printf("unhandled wrappers selector sel name %q\n", selectorExpr.Sel.Name) + os.Exit(1) + } + + case "timestamp": + switch selectorExpr.Sel.Name { + case "Timestamp": + st.Fields.List[i] = &ast.Field{ + Names: field.Names, + Type: &ast.Ident{ + Name: "time.Time", + }, + Tag: field.Tag, + } + + default: + fmt.Printf("unhandled timestamp selector sel name %q\n", selectorExpr.Sel.Name) + os.Exit(1) + } + + case "_struct": + switch selectorExpr.Sel.Name { + case "Struct": + st.Fields.List[i] = &ast.Field{ + Names: field.Names, + Type: &ast.MapType{ + Key: &ast.Ident{ + Name: "string", + }, + Value: &ast.InterfaceType{ + Methods: &ast.FieldList{}, + }, + }, + Tag: field.Tag, + } + + default: + fmt.Printf("unhandled timestamp selector sel name %q\n", selectorExpr.Sel.Name) + os.Exit(1) + } + + default: + fmt.Printf("unhandled xident name %q\n", xident.Name) + os.Exit(1) + } + + default: + fmt.Println("unhandled non-ident, non-selector case") + os.Exit(1) + } + + TAGMODIFY: + st.Fields.List[i].Tag.Value = "`" + regex.FindString(st.Fields.List[i].Tag.Value) + "`" + } + }), inAst) + + buf := new(bytes.Buffer) + if err := format.Node(buf, fset, inAst); err != nil { + fmt.Printf("error formatting new code: %v\n", err) + os.Exit(1) + } + + // We have to manually cut out the lines we don't want because comments + // won't be preserved otherwise. See the note about lossy comments in + // https://arslan.io/2017/09/14/the-ultimate-guide-to-writing-a-go-tool/ + scanner := bufio.NewScanner(bytes.NewBufferString(buf.String())) + var inType bool + var outBuf []string + for scanner.Scan() { + if !inType { + if strings.HasPrefix(scanner.Text(), "type "+inputStruct.outName+" struct") { + inType = true + // Don't add this line, we'll do it in the template + continue + } + } else { + if scanner.Text() == "}" { + // We've reached the end of the type + break + } + } + + if inType { + outBuf = append(outBuf, scanner.Text()) + } + } + + inputStruct.structFields = strings.Join(outBuf, "\n") + } +} + +func createUtilFuncs() { + for _, inputStruct := range inputStructs { + outBuf := new(bytes.Buffer) + switch inputStruct.templateType { + case templateTypeResource: + utilFuncsTemplate.Execute(outBuf, struct { + Timestamp time.Time + Name string + Package string + StructFields string + }{ + Timestamp: time.Now(), + Name: inputStruct.outName, + Package: inputStruct.outPkg, + StructFields: inputStruct.structFields, + }) + + case templateTypeDetail: + detailTemplate.Execute(outBuf, struct { + Timestamp time.Time + Package string + StructFields string + ParentName string + DetailName string + }{ + Timestamp: time.Now(), + Package: inputStruct.outPkg, + StructFields: inputStruct.structFields, + ParentName: inputStruct.parentName, + DetailName: inputStruct.detailName, + }) + + } + + outFile, err := filepath.Abs(inputStruct.outFile) + if err != nil { + fmt.Printf("error opening file %q: %v\n", inputStruct.outFile, err) + os.Exit(1) + } + if err := ioutil.WriteFile(outFile, outBuf.Bytes(), 0644); err != nil { + fmt.Printf("error writing file %q: %v\n", outFile, err) + os.Exit(1) + } + } +} + +var utilFuncsTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// {{ .Timestamp }} +package {{ .Package }} + +import ( + "encoding/json" + + "github.com/fatih/structs" + "github.com/hashicorp/watchtower/api/internal/strutil" +) + +type {{ .Name }} struct { + {{ .StructFields }} +} + +func (s *{{ .Name }}) SetDefault(key string) { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) +} + +func (s *{{ .Name }}) UnsetDefault(key string) { + s.defaultFields = strutil.StrListDelete(s.defaultFields, key) +} + +func (s {{ .Name }}) MarshalJSON() ([]byte, error) { + m := structs.Map(s) + if m == nil { + m = make(map[string]interface{}) + } + for _, k := range s.defaultFields { + m[k] = nil + } + return json.Marshal(m) +} +`)) + +// TODO: Figure out the right way to write out the specific fields +var detailTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// {{ .Timestamp }} +package {{ .Package }} + +import ( + "fmt" + + "github.com/mitchellh/mapstructure" +) + +type {{ .DetailName }} struct { + *{{ .ParentName }} + {{ .StructFields }} +} + +func (s {{ .ParentName }}) As{{ .DetailName }}() (*{{ .DetailName }}, error) { + out := &{{ .DetailName }}{ + {{ .ParentName }}: &s, + } + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + Result: out, + TagName: "json", + }) + if err != nil { + return nil, fmt.Errorf("error creating map decoder: %w", err) + } + + if err := decoder.Decode(s.Attributes); err != nil { + return nil, fmt.Errorf("error decoding attributes map: %w", err) + } + + return out, nil +} +`)) diff --git a/api/internal/genapi/makeimports.go b/api/internal/genapi/makeimports.go new file mode 100644 index 0000000000..83c9e9434d --- /dev/null +++ b/api/internal/genapi/makeimports.go @@ -0,0 +1,7 @@ +package main + +// +build genapi + +//go:generate go run genapi.go +//go:generate goimports -w ../../hosts +//go:generate goimports -w ../.. diff --git a/api/internal/tests/detail_template_test.go b/api/internal/tests/detail_template_test.go new file mode 100644 index 0000000000..3cc48a1be9 --- /dev/null +++ b/api/internal/tests/detail_template_test.go @@ -0,0 +1,34 @@ +package tests + +import ( + "testing" + "time" + + "github.com/hashicorp/watchtower/api" + "github.com/hashicorp/watchtower/api/hosts" + "github.com/stretchr/testify/assert" +) + +func TestDetailTemplating(t *testing.T) { + lt := time.Now() + c := hosts.HostCatalog{ + Path: api.String("path"), + CreatedTime: lt, + Attributes: map[string]interface{}{ + "regions": []string{"a", "b"}, + "access_key": "access", + "secret_key": "secret", + "rotate": true, + }, + } + + ac, err := c.AsAwsEc2HostCatalog() + assert.NoError(t, err) + assert.Equal(t, &hosts.AwsEc2HostCatalog{ + HostCatalog: &c, + Regions: []string{"a", "b"}, + AccessKey: api.String("access"), + SecretKey: api.String("secret"), + Rotate: api.Bool(true), + }, ac) +} diff --git a/api/util.go b/api/util.go new file mode 100644 index 0000000000..e7c1ca6f8b --- /dev/null +++ b/api/util.go @@ -0,0 +1,19 @@ +package api + +func Bool(in bool) *bool { + ret := new(bool) + *ret = in + return ret +} + +func String(in string) *string { + ret := new(string) + *ret = in + return ret +} + +func Int(in int64) *int64 { + ret := new(int64) + *ret = in + return ret +} diff --git a/go.mod b/go.mod index 839d9ab76f..2971bbcfde 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/armon/go-metrics v0.3.3 github.com/bufbuild/buf v0.11.0 github.com/fatih/color v1.9.0 + github.com/fatih/structs v1.1.0 github.com/favadi/protoc-go-inject-tag v1.0.0 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/go-openapi/runtime v0.19.15 // indirect diff --git a/internal/gen/controller/api/error.pb.go b/internal/gen/controller/api/error.pb.go index ae965e88cf..bfa50847da 100644 --- a/internal/gen/controller/api/error.pb.go +++ b/internal/gen/controller/api/error.pb.go @@ -8,6 +8,7 @@ package api import ( proto "github.com/golang/protobuf/proto" + wrappers "github.com/golang/protobuf/ptypes/wrappers" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -30,9 +31,9 @@ type ErrorDetails struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TraceId string `protobuf:"bytes,1,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"` - RequestId string `protobuf:"bytes,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - RequestFields []string `protobuf:"bytes,3,rep,name=request_fields,json=requestFields,proto3" json:"request_fields,omitempty"` + TraceId *wrappers.StringValue `protobuf:"bytes,1,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"` + RequestId *wrappers.StringValue `protobuf:"bytes,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + RequestFields []string `protobuf:"bytes,3,rep,name=request_fields,json=requestFields,proto3" json:"request_fields,omitempty"` } func (x *ErrorDetails) Reset() { @@ -67,18 +68,18 @@ func (*ErrorDetails) Descriptor() ([]byte, []int) { return file_controller_api_v1_error_proto_rawDescGZIP(), []int{0} } -func (x *ErrorDetails) GetTraceId() string { +func (x *ErrorDetails) GetTraceId() *wrappers.StringValue { if x != nil { return x.TraceId } - return "" + return nil } -func (x *ErrorDetails) GetRequestId() string { +func (x *ErrorDetails) GetRequestId() *wrappers.StringValue { if x != nil { return x.RequestId } - return "" + return nil } func (x *ErrorDetails) GetRequestFields() []string { @@ -94,13 +95,14 @@ type Error struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The HTTP Status code applicable to this error. - Status int32 `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"` - // An application-specific error string. - Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"` - // A human readable explanation specific to this occurrence of the error. - Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` - // Additional metadata regarding the error. Depending on the error different fields will be populated. + // The HTTP Status code applicable to this error + Status *wrappers.Int64Value `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + // An application-specific error string + Code *wrappers.StringValue `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"` + // A human readable explanation specific to this occurrence of the error + Message *wrappers.StringValue `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` + // Additional metadata regarding the error. Depending on the error + // different fields will be populated. Details *ErrorDetails `protobuf:"bytes,4,opt,name=details,proto3" json:"details,omitempty"` } @@ -136,25 +138,25 @@ func (*Error) Descriptor() ([]byte, []int) { return file_controller_api_v1_error_proto_rawDescGZIP(), []int{1} } -func (x *Error) GetStatus() int32 { +func (x *Error) GetStatus() *wrappers.Int64Value { if x != nil { return x.Status } - return 0 + return nil } -func (x *Error) GetCode() string { +func (x *Error) GetCode() *wrappers.StringValue { if x != nil { return x.Code } - return "" + return nil } -func (x *Error) GetMessage() string { +func (x *Error) GetMessage() *wrappers.StringValue { if x != nil { return x.Message } - return "" + return nil } func (x *Error) GetDetails() *ErrorDetails { @@ -170,27 +172,38 @@ var file_controller_api_v1_error_proto_rawDesc = []byte{ 0x0a, 0x1d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x22, 0x6f, 0x0a, 0x0c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, - 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, 0x69, 0x65, - 0x6c, 0x64, 0x73, 0x22, 0x88, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x41, - 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, - 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x61, 0x70, - 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x76, 0x31, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0xab, 0x01, 0x0a, 0x0c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x0a, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, + 0x22, 0xe1, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, + 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x30, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, + 0x65, 0x12, 0x36, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x64, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x07, 0x64, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77, 0x61, 0x74, + 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, + 0x61, 0x70, 0x69, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -207,16 +220,23 @@ func file_controller_api_v1_error_proto_rawDescGZIP() []byte { var file_controller_api_v1_error_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_controller_api_v1_error_proto_goTypes = []interface{}{ - (*ErrorDetails)(nil), // 0: controller.api.v1.ErrorDetails - (*Error)(nil), // 1: controller.api.v1.Error + (*ErrorDetails)(nil), // 0: controller.api.v1.ErrorDetails + (*Error)(nil), // 1: controller.api.v1.Error + (*wrappers.StringValue)(nil), // 2: google.protobuf.StringValue + (*wrappers.Int64Value)(nil), // 3: google.protobuf.Int64Value } var file_controller_api_v1_error_proto_depIdxs = []int32{ - 0, // 0: controller.api.v1.Error.details:type_name -> controller.api.v1.ErrorDetails - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 2, // 0: controller.api.v1.ErrorDetails.trace_id:type_name -> google.protobuf.StringValue + 2, // 1: controller.api.v1.ErrorDetails.request_id:type_name -> google.protobuf.StringValue + 3, // 2: controller.api.v1.Error.status:type_name -> google.protobuf.Int64Value + 2, // 3: controller.api.v1.Error.code:type_name -> google.protobuf.StringValue + 2, // 4: controller.api.v1.Error.message:type_name -> google.protobuf.StringValue + 0, // 5: controller.api.v1.Error.details:type_name -> controller.api.v1.ErrorDetails + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_controller_api_v1_error_proto_init() } diff --git a/internal/gen/controller/api/services/host_set_service.pb.go b/internal/gen/controller/api/services/host_set_service.pb.go index 74184bf30b..3701411680 100644 --- a/internal/gen/controller/api/services/host_set_service.pb.go +++ b/internal/gen/controller/api/services/host_set_service.pb.go @@ -665,8 +665,9 @@ type AddToHostSetRequest struct { ProjectId string `protobuf:"bytes,2,opt,name=project_id,json=projectId,proto3" json:"project_id,omitempty"` HostCatalogId string `protobuf:"bytes,3,opt,name=host_catalog_id,json=hostCatalogId,proto3" json:"host_catalog_id,omitempty"` Id string `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"` - // A list of host IDss which will be added to this host set. Each host referenced here must be a child of the - // same host catalog that this host set is a child of. + // A list of host IDs which will be added to this host set. Each host + // referenced here must be a child of the same host catalog that this host + // set is a child of. HostIds []string `protobuf:"bytes,5,rep,name=host_ids,json=hostIds,proto3" json:"host_ids,omitempty"` } @@ -785,7 +786,8 @@ type RemoveFromHostSetRequest struct { HostCatalogId string `protobuf:"bytes,3,opt,name=host_catalog_id,json=hostCatalogId,proto3" json:"host_catalog_id,omitempty"` // This host set's id. Id string `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"` - // A list of host IDs which should not be in the host set when this request returns. + // A list of host IDs which should not be in the host set when this request + // returns. HostIds []string `protobuf:"bytes,5,rep,name=host_ids,json=hostIds,proto3" json:"host_ids,omitempty"` } diff --git a/internal/proto/local/controller/api/services/v1/host_set_service.proto b/internal/proto/local/controller/api/services/v1/host_set_service.proto index 2ace1ae469..ffaca9e90c 100644 --- a/internal/proto/local/controller/api/services/v1/host_set_service.proto +++ b/internal/proto/local/controller/api/services/v1/host_set_service.proto @@ -155,8 +155,9 @@ message AddToHostSetRequest { string host_catalog_id = 3; string id = 4; - // A list of host IDs which will be added to this host set. Each host referenced here must be a child of the - // same host catalog that this host set is a child of. + // A list of host IDs which will be added to this host set. Each host + // referenced here must be a child of the same host catalog that this host + // set is a child of. repeated string host_ids = 5; } @@ -170,7 +171,8 @@ message RemoveFromHostSetRequest { // This host set's id. string id = 4; - // A list of host IDs which should not be in the host set when this request returns. + // A list of host IDs which should not be in the host set when this request + // returns. repeated string host_ids = 5; } diff --git a/internal/proto/local/controller/api/v1/error.proto b/internal/proto/local/controller/api/v1/error.proto index 522666c19e..9b50cc9688 100644 --- a/internal/proto/local/controller/api/v1/error.proto +++ b/internal/proto/local/controller/api/v1/error.proto @@ -4,20 +4,23 @@ package controller.api.v1; option go_package = "github.com/hashicorp/watchtower/internal/gen/controller/api;api"; +import "google/protobuf/wrappers.proto"; + message ErrorDetails { - string trace_id = 1; - string request_id = 2; - repeated string request_fields = 3; + google.protobuf.StringValue trace_id = 1; + google.protobuf.StringValue request_id = 2; + repeated string request_fields = 3; } // Error is returned by the JSON API when an error occurs. message Error { - // The HTTP Status code applicable to this error. - int32 status = 1; - // An application-specific error string. - string code = 2; - // A human readable explanation specific to this occurrence of the error. - string message = 3; - // Additional metadata regarding the error. Depending on the error different fields will be populated. - ErrorDetails details = 4; + // The HTTP Status code applicable to this error + google.protobuf.Int64Value status = 1; + // An application-specific error string + google.protobuf.StringValue code = 2; + // A human readable explanation specific to this occurrence of the error + google.protobuf.StringValue message = 3; + // Additional metadata regarding the error. Depending on the error + // different fields will be populated. + ErrorDetails details = 4; }