diff --git a/WORKSPACE b/WORKSPACE index 6cfa19ab..e8f66fd5 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -44,9 +44,17 @@ go_repository( version = "v0.122.0", ) -# Insert go_repostiry rules before this one to override specific deps. +# Insert go_repository rules before this one to override specific deps. gazelle_dependencies() +# Insert oauth2 before remote_apis_sdks_deps() to override version +go_repository( + name = "org_golang_x_oauth2", + importpath = "golang.org/x/oauth2", + sum = "h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=", + version = "v0.20.0", +) + load("//:go_deps.bzl", "remote_apis_sdks_go_deps") # gazelle:repository_macro go_deps.bzl%remote_apis_sdks_go_deps diff --git a/go.mod b/go.mod index ad7865be..30d28f56 100644 --- a/go.mod +++ b/go.mod @@ -7,9 +7,10 @@ require ( github.com/golang/glog v1.1.0 github.com/google/go-cmp v0.5.9 github.com/google/uuid v1.3.0 + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb github.com/klauspost/compress v1.17.8 github.com/pkg/xattr v0.4.4 - golang.org/x/oauth2 v0.10.0 + golang.org/x/oauth2 v0.20.0 golang.org/x/sync v0.6.0 google.golang.org/api v0.126.0 google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 @@ -20,13 +21,11 @@ require ( ) require ( - cloud.google.com/go/compute v1.23.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect cloud.google.com/go/longrunning v0.5.1 // indirect github.com/golang/protobuf v1.5.3 // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 // indirect ) diff --git a/go.sum b/go.sum index 377acdaf..676714f0 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -22,7 +20,6 @@ github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -46,6 +43,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/pkg/xattr v0.4.4 h1:FSoblPdYobYoKCItkqASqcrKCxRn9Bgurz0sCBwzO5g= @@ -70,7 +69,6 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= @@ -78,8 +76,8 @@ golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -89,6 +87,7 @@ golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -101,7 +100,6 @@ golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -123,8 +121,6 @@ google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= diff --git a/go/api/credshelper/BUILD.bazel b/go/api/credshelper/BUILD.bazel new file mode 100644 index 00000000..a63a8585 --- /dev/null +++ b/go/api/credshelper/BUILD.bazel @@ -0,0 +1,24 @@ +load("@rules_proto//proto:defs.bzl", "proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") + +proto_library( + name = "credshelper_proto", + srcs = ["credshelper.proto"], + visibility = ["//visibility:public"], + deps = ["@com_google_protobuf//:timestamp_proto"], +) + +go_proto_library( + name = "credshelper_go_proto", + importpath = "github.com/bazelbuild/remote-apis-sdks/go/api/credshelper", + proto = ":credshelper_proto", + visibility = ["//visibility:public"], +) + +go_library( + name = "credshelper", + embed = [":credshelper_go_proto"], + importpath = "github.com/bazelbuild/remote-apis-sdks/go/api/credshelper", + visibility = ["//visibility:public"], +) diff --git a/go/api/credshelper/credshelper.pb.go b/go/api/credshelper/credshelper.pb.go new file mode 100644 index 00000000..4051d197 --- /dev/null +++ b/go/api/credshelper/credshelper.pb.go @@ -0,0 +1,202 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc v3.21.12 +// source: credshelper.proto + +package credshelper + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Stores information used for authenticating to the remote execution service. +type Credentials struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Authentication mechanism. + AuthSource string `protobuf:"bytes,1,opt,name=auth_source,json=authSource,proto3" json:"auth_source,omitempty"` + // The token string. + Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` + // Token expiry. + Expiry *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=expiry,proto3" json:"expiry,omitempty"` + // Reauth expiry. + RefreshExpiry *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=refresh_expiry,json=refreshExpiry,proto3" json:"refresh_expiry,omitempty"` + // Credshelper command digest in canonical form of hash/size. + CredsHelperCmdDigest string `protobuf:"bytes,5,opt,name=credsHelperCmdDigest,proto3" json:"credsHelperCmdDigest,omitempty"` +} + +func (x *Credentials) Reset() { + *x = Credentials{} + if protoimpl.UnsafeEnabled { + mi := &file_credshelper_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Credentials) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Credentials) ProtoMessage() {} + +func (x *Credentials) ProtoReflect() protoreflect.Message { + mi := &file_credshelper_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Credentials.ProtoReflect.Descriptor instead. +func (*Credentials) Descriptor() ([]byte, []int) { + return file_credshelper_proto_rawDescGZIP(), []int{0} +} + +func (x *Credentials) GetAuthSource() string { + if x != nil { + return x.AuthSource + } + return "" +} + +func (x *Credentials) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +func (x *Credentials) GetExpiry() *timestamppb.Timestamp { + if x != nil { + return x.Expiry + } + return nil +} + +func (x *Credentials) GetRefreshExpiry() *timestamppb.Timestamp { + if x != nil { + return x.RefreshExpiry + } + return nil +} + +func (x *Credentials) GetCredsHelperCmdDigest() string { + if x != nil { + return x.CredsHelperCmdDigest + } + return "" +} + +var File_credshelper_proto protoreflect.FileDescriptor + +var file_credshelper_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x63, 0x72, 0x65, 0x64, 0x73, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x73, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, + 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0xef, 0x01, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x32, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x41, 0x0a, 0x0e, + 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, + 0x32, 0x0a, 0x14, 0x63, 0x72, 0x65, 0x64, 0x73, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x43, 0x6d, + 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x63, + 0x72, 0x65, 0x64, 0x73, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x43, 0x6d, 0x64, 0x44, 0x69, 0x67, + 0x65, 0x73, 0x74, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x2d, 0x61, 0x70, 0x69, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x73, 0x2f, 0x67, 0x6f, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x73, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_credshelper_proto_rawDescOnce sync.Once + file_credshelper_proto_rawDescData = file_credshelper_proto_rawDesc +) + +func file_credshelper_proto_rawDescGZIP() []byte { + file_credshelper_proto_rawDescOnce.Do(func() { + file_credshelper_proto_rawDescData = protoimpl.X.CompressGZIP(file_credshelper_proto_rawDescData) + }) + return file_credshelper_proto_rawDescData +} + +var file_credshelper_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_credshelper_proto_goTypes = []interface{}{ + (*Credentials)(nil), // 0: credshelper.Credentials + (*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp +} +var file_credshelper_proto_depIdxs = []int32{ + 1, // 0: credshelper.Credentials.expiry:type_name -> google.protobuf.Timestamp + 1, // 1: credshelper.Credentials.refresh_expiry:type_name -> google.protobuf.Timestamp + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_credshelper_proto_init() } +func file_credshelper_proto_init() { + if File_credshelper_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_credshelper_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Credentials); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_credshelper_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_credshelper_proto_goTypes, + DependencyIndexes: file_credshelper_proto_depIdxs, + MessageInfos: file_credshelper_proto_msgTypes, + }.Build() + File_credshelper_proto = out.File + file_credshelper_proto_rawDesc = nil + file_credshelper_proto_goTypes = nil + file_credshelper_proto_depIdxs = nil +} diff --git a/go/api/credshelper/credshelper.proto b/go/api/credshelper/credshelper.proto new file mode 100644 index 00000000..cde85c59 --- /dev/null +++ b/go/api/credshelper/credshelper.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +import "google/protobuf/timestamp.proto"; + +package credshelper; + +option go_package = "github.com/bazelbuild/remote-apis-sdks/go/api/credshelper"; + +// Stores information used for authenticating to the remote execution service. +message Credentials { + // Authentication mechanism. + string auth_source = 1; + // The token string. + string token = 2; + // Token expiry. + google.protobuf.Timestamp expiry = 3; + // Reauth expiry. + google.protobuf.Timestamp refresh_expiry = 4; + // Credshelper command digest in canonical form of hash/size. + string credsHelperCmdDigest = 5; +} diff --git a/go/pkg/credshelper/BUILD.bazel b/go/pkg/credshelper/BUILD.bazel new file mode 100644 index 00000000..3a98a2dd --- /dev/null +++ b/go/pkg/credshelper/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "credshelper", + srcs = [ + "credshelper.go", + "cache.go", + ], + importpath = "github.com/bazelbuild/remote-apis-sdks/go/pkg/credshelper", + visibility = ["//:__subpackages__"], + deps = [ + "//go/api/credshelper", + "//go/pkg/digest", + "@com_github_golang_glog//:glog", + "@com_github_hectane_go_acl//:go-acl", + "@org_golang_google_grpc//credentials/oauth", + "@org_golang_google_protobuf//encoding/prototext", + "@org_golang_google_protobuf//types/known/timestamppb", + "@org_golang_x_oauth2//:oauth2", + "@org_golang_x_oauth2//google", + ], +) + +go_test( + name = "credshelper_test", + srcs = [ + "credshelper_test.go", + ], + embed = [":credshelper"], + deps = [ + "@com_github_google_go_cmp//cmp", + "@org_golang_x_oauth2//:oauth2", + ], +) diff --git a/go/pkg/credshelper/cache.go b/go/pkg/credshelper/cache.go new file mode 100644 index 00000000..80e97f3c --- /dev/null +++ b/go/pkg/credshelper/cache.go @@ -0,0 +1,103 @@ +package credshelper + +import ( + "fmt" + "os" + "time" + + cpb "github.com/bazelbuild/remote-apis-sdks/go/api/credshelper" + + log "github.com/golang/glog" + "github.com/hectane/go-acl" + "golang.org/x/oauth2" + "google.golang.org/protobuf/encoding/prototext" + tspb "google.golang.org/protobuf/types/known/timestamppb" +) + +// CachedCredentials are the credentials cached to disk. +type cachedCredentials struct { + authSource string + refreshExp time.Time + token *oauth2.Token + credsHelperCmdDigest string +} + +func loadFromDisk(tf string) (cachedCredentials, error) { + if tf == "" { + return cachedCredentials{}, fmt.Errorf("creds_file path not provided") + } + blob, err := os.ReadFile(tf) + if err != nil { + return cachedCredentials{}, fmt.Errorf("could not read creds_file (%s): %v", tf, err) + } + cPb := &cpb.Credentials{} + if err := prototext.Unmarshal(blob, cPb); err != nil { + return cachedCredentials{}, fmt.Errorf("could not unmarshal file to proto: %v", err) + } + accessToken := cPb.GetToken() + exp := TimeFromProto(cPb.GetExpiry()) + var token *oauth2.Token + if accessToken != "" && !exp.IsZero() { + token = &oauth2.Token{ + AccessToken: accessToken, + Expiry: exp, + } + } + c := cachedCredentials{ + authSource: cPb.GetAuthSource(), + token: token, + refreshExp: TimeFromProto(cPb.GetRefreshExpiry()), + credsHelperCmdDigest: cPb.GetCredsHelperCmdDigest(), + } + log.Infof("Loaded cached credentials of type %v, expires at %v", c.authSource, exp) + return c, nil +} + +func saveToDisk(c cachedCredentials, tf string) error { + if tf == "" { + return nil + } + cPb := &cpb.Credentials{} + cPb.AuthSource = c.authSource + if c.token != nil { + cPb.Token = c.token.AccessToken + cPb.Expiry = TimeToProto(c.token.Expiry) + cPb.CredsHelperCmdDigest = c.credsHelperCmdDigest + } + if !c.refreshExp.IsZero() { + cPb.RefreshExpiry = TimeToProto(c.refreshExp) + } + f, err := os.Create(tf) + if err != nil { + return fmt.Errorf("creds_file: %s could not be created: %v", tf, err) + } + // Only owner can read/write the credential cache. + // This is consistent with gcloud's credentials.db. + // os.OpenFile(..., 0600) is not used because it does not properly set ACLs on windows. + if err := acl.Chmod(tf, 0600); err != nil { + return fmt.Errorf("could not set ACLs on creds_file: %v", err) + } + defer f.Close() + _, err = f.WriteString(prototext.Format(cPb)) + if err != nil { + return fmt.Errorf("could not write creds to file: %v", err) + } + log.Infof("Saved cached credentials of type %v, expires at %v to %v", c.authSource, cPb.Expiry, tf) + return nil +} + +// TimeToProto converts a valid time.Time into a proto Timestamp. +func TimeToProto(t time.Time) *tspb.Timestamp { + if t.IsZero() { + return nil + } + return tspb.New(t) +} + +// TimeFromProto converts a valid Timestamp proto into a time.Time. +func TimeFromProto(tPb *tspb.Timestamp) time.Time { + if tPb == nil { + return time.Time{} + } + return tPb.AsTime() +} diff --git a/go/pkg/credshelper/credshelper.go b/go/pkg/credshelper/credshelper.go new file mode 100644 index 00000000..31fe052f --- /dev/null +++ b/go/pkg/credshelper/credshelper.go @@ -0,0 +1,275 @@ +// Package credshelper implements functionality to authenticate using an external credentials helper. +package credshelper + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "sort" + "strings" + "sync" + "time" + + "github.com/bazelbuild/remote-apis-sdks/go/pkg/digest" + + log "github.com/golang/glog" + "golang.org/x/oauth2" + grpcOauth "google.golang.org/grpc/credentials/oauth" +) + +const ( + // CredentialsHelper is using an externally provided binary to get credentials. + CredentialsHelper = "CredentialsHelper" + // CredshelperPathFlag is the path to the credentials helper binary. + CredshelperPathFlag = "credentials_helper" + // CredshelperArgsFlag is the flag used to pass in the arguments to the credentials helper binary. + CredshelperArgsFlag = "credentials_helper_args" + // CredsFileFlag is the flag used to pass in the path of the file where credentials should be cached. + CredsFileFlag = "creds_file" +) + +var nowFn = time.Now + +// Error is an error occured during authenticating or initializing credentials. +type Error struct { + error + // ExitCode is the exit code for the error. + ExitCode int +} + +type reusableCmd struct { + path string + args []string + digestOnce sync.Once + digest digest.Digest +} + +func newReusableCmd(binary string, args []string) *reusableCmd { + cmd := exec.Command(binary, args...) + return &reusableCmd{ + path: cmd.Path, + args: args, + } +} + +func (r *reusableCmd) String() string { + return fmt.Sprintf("%s %v", r.path, strings.Join(r.args, " ")) +} + +func (r *reusableCmd) Cmd() *exec.Cmd { + return exec.Command(r.path, r.args...) +} + +func (r *reusableCmd) Digest() digest.Digest { + r.digestOnce.Do(func() { + chCmd := append(r.args, r.path) + sort.Strings(chCmd) + cmdStr := strings.Join(chCmd, ",") + r.digest = digest.NewFromBlob([]byte(cmdStr)) + }) + return r.digest +} + +// Credentials provides auth functionalities using an external credentials helper +type Credentials struct { + credsFile string + refreshExp time.Time + tokenSource *grpcOauth.TokenSource + credsHelperCmd *reusableCmd +} + +// externaltokenSource uses a credentialsHelper to obtain gcp oauth tokens. +// This should be wrapped in a "golang.org/x/oauth2".ReuseTokenSource +// to avoid obtaining new tokens each time. +type externalTokenSource struct { + credsHelperCmd *reusableCmd +} + +func buildExternalCredentials(baseCreds cachedCredentials, credsFile string, credsHelperCmd *reusableCmd) *Credentials { + c := &Credentials{ + credsHelperCmd: credsHelperCmd, + credsFile: credsFile, + refreshExp: baseCreds.refreshExp, + } + baseTS := &externalTokenSource{ + credsHelperCmd: credsHelperCmd, + } + c.tokenSource = &grpcOauth.TokenSource{ + // Wrap the base token source with a ReuseTokenSource so that we only + // generate new credentials when the current one is about to expire. + // This is needed because retrieving the token is expensive and some + // token providers have per hour rate limits. + TokenSource: oauth2.ReuseTokenSourceWithExpiry( + baseCreds.token, + baseTS, + // Refresh tokens 5 mins early to be safe + 5*time.Minute, + ), + } + return c +} + +func loadCredsFromDisk(credsFile string, credsHelperCmd *reusableCmd) (*Credentials, error) { + cc, err := loadFromDisk(credsFile) + if err != nil { + return nil, err + } + cmdDigest := credsHelperCmd.Digest() + if cc.credsHelperCmdDigest != cmdDigest.String() { + return nil, fmt.Errorf("cached credshelper command digest: %s is not the same as requested credshelper command digest: %s", + cc.credsHelperCmdDigest, cmdDigest.String()) + } + isExpired := cc.token != nil && cc.token.Expiry.Before(nowFn()) + if isExpired { + return nil, fmt.Errorf("cached token is expired at %v", cc.token.Expiry) + } + return buildExternalCredentials(cc, credsFile, credsHelperCmd), nil +} + +// SaveToDisk saves credentials to disk. +func (c *Credentials) SaveToDisk() { + if c == nil { + return + } + cc := cachedCredentials{authSource: CredentialsHelper, refreshExp: c.refreshExp} + // Since c.tokenSource is always wrapped in a oauth2.ReuseTokenSourceWithExpiry + // this will return a cached credential if one exists. + t, err := c.tokenSource.Token() + if err != nil { + log.Errorf("Failed to get token to persist to disk: %v", err) + return + } + cc.token = t + if c.credsHelperCmd != nil { + cc.credsHelperCmdDigest = c.credsHelperCmd.Digest().String() + } + if err := saveToDisk(cc, c.credsFile); err != nil { + log.Errorf("Failed to save credentials to disk: %v", err) + } +} + +// RemoveFromDisk deletes the credentials cache on disk. +func (c *Credentials) RemoveFromDisk() { + if c == nil { + return + } + if err := os.Remove(c.credsFile); err != nil { + log.Errorf("Failed to remove credentials from disk: %v", err) + } +} + +// Token retrieves an oauth2 token from the external tokensource. +func (ts *externalTokenSource) Token() (*oauth2.Token, error) { + if ts == nil { + return nil, fmt.Errorf("empty tokensource") + } + tk, _, err := runCredsHelperCmd(ts.credsHelperCmd) + if err == nil { + log.Infof("'%s' credentials refreshed at %v, expires at %v", ts.credsHelperCmd, time.Now(), tk.Expiry) + } + return tk, err +} + +// NewExternalCredentials creates credentials obtained from a credshelper. +func NewExternalCredentials(credshelper string, credshelperArgs []string, credsFile string) (*Credentials, error) { + if credshelper == "execrel://" { + credshelperPath, err := binaryRelToAbs("credshelper") + if err != nil { + log.Fatalf("Specified %s=execrel:// but `credshelper` was not found in the same directory as `bootstrap` or `reproxy`: %v", CredshelperPathFlag, err) + } + credshelper = credshelperPath + } + credsHelperCmd := newReusableCmd(credshelper, credshelperArgs) + if credsFile != "" { + creds, err := loadCredsFromDisk(credsFile, credsHelperCmd) + if err == nil { + return creds, nil + } + log.Warningf("Failed to use cached credentials: %v", err) + } + tk, rexp, err := runCredsHelperCmd(credsHelperCmd) + if err != nil { + return nil, err + } + return buildExternalCredentials(cachedCredentials{token: tk, refreshExp: rexp}, credsFile, credsHelperCmd), nil +} + +func runCredsHelperCmd(credsHelperCmd *reusableCmd) (*oauth2.Token, time.Time, error) { + log.V(2).Infof("Running %v", credsHelperCmd) + var stdout, stderr bytes.Buffer + cmd := credsHelperCmd.Cmd() + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + out := stdout.String() + if stderr.String() != "" { + log.Errorf("Credentials helper warnings and errors: %v", stderr.String()) + } + if err != nil { + return nil, time.Time{}, err + } + token, expiry, refreshExpiry, err := parseTokenExpiryFromOutput(out) + return &oauth2.Token{ + AccessToken: token, + Expiry: expiry, + }, refreshExpiry, err +} + +// CredsHelperOut is the struct to record the json output from the credshelper. +type CredsHelperOut struct { + Token string `json:"token"` + Expiry string `json:"expiry"` + RefreshExpiry string `json:"refresh_expiry"` +} + +func parseTokenExpiryFromOutput(out string) (string, time.Time, time.Time, error) { + var ( + tk string + exp, rexp time.Time + chOut CredsHelperOut + ) + if err := json.Unmarshal([]byte(out), &chOut); err != nil { + return tk, exp, rexp, + fmt.Errorf("error while decoding credshelper output:%v", err) + } + tk = chOut.Token + if tk == "" { + return tk, exp, rexp, + fmt.Errorf("no token was printed by the credentials helper") + } + if chOut.Expiry != "" { + expiry, err := time.Parse(time.UnixDate, chOut.Expiry) + if err != nil { + return tk, exp, rexp, fmt.Errorf("invalid expiry format: %v (Expected time.UnixDate format)", chOut.Expiry) + } + exp = expiry + rexp = expiry + } + if chOut.RefreshExpiry != "" { + rexpiry, err := time.Parse(time.UnixDate, chOut.RefreshExpiry) + if err != nil { + return tk, exp, rexp, fmt.Errorf("invalid refresh expiry format: %v (Expected time.UnixDate format)", chOut.RefreshExpiry) + } + rexp = rexpiry + } + return tk, exp, rexp, nil +} + +// binaryRelToAbs converts a path that is relative to the current executable +// to absolyte path. If the executable is a symlink then the symlink is +// resolved before generating the path. +func binaryRelToAbs(relPath string) (string, error) { + executable, err := os.Executable() + if err != nil { + return "", err + } + executable, err = filepath.EvalSymlinks(executable) + if err != nil { + return "", err + } + binary := filepath.Join(filepath.Dir(executable), relPath) + return binary, nil +} diff --git a/go/pkg/credshelper/credshelper_test.go b/go/pkg/credshelper/credshelper_test.go new file mode 100644 index 00000000..3f84e4b4 --- /dev/null +++ b/go/pkg/credshelper/credshelper_test.go @@ -0,0 +1,254 @@ +package credshelper + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "testing" + "time" + + "golang.org/x/oauth2" + grpcOauth "google.golang.org/grpc/credentials/oauth" +) + +func TestCredentialsHelperCache(t *testing.T) { + dir, err := os.MkdirTemp("", "test") + if err != nil { + t.Errorf("failed to create the temp directory: %v", err) + } + t.Cleanup(func() { os.RemoveAll(dir) }) + cf := filepath.Join(dir, "reproxy.creds") + err = os.MkdirAll(filepath.Dir(cf), 0755) + if err != nil { + t.Errorf("failed to create dir for credentials file %q: %v", cf, err) + } + credsHelperCmd := newReusableCmd("echo", []string{`{"token":"testToken", "expiry":"", "refresh_expiry":""}`}) + ts := &grpcOauth.TokenSource{ + TokenSource: oauth2.ReuseTokenSourceWithExpiry( + &oauth2.Token{}, + &externalTokenSource{credsHelperCmd: credsHelperCmd}, + 5*time.Minute, + ), + } + creds := Credentials{ + refreshExp: time.Time{}, + tokenSource: ts, + credsFile: cf, + credsHelperCmd: credsHelperCmd, + } + creds.SaveToDisk() + _, err = loadCredsFromDisk(cf, credsHelperCmd) + if err != nil { + t.Errorf("LoadCredsFromDisk failed: %v", err) + } +} + +func TestExternalToken(t *testing.T) { + expiry := time.Now().Truncate(time.Second) + exp := expiry.Format(time.UnixDate) + tk := "testToken" + var ( + credshelper string + credshelperArgs []string + ) + if runtime.GOOS == "windows" { + tf, err := os.CreateTemp("", "testexternaltoken.json") + if err != nil { + t.Fatalf("Unable to create temporary file: %v", err) + } + chJSON := fmt.Sprintf(`{"token":"%v","expiry":"%s","refresh_expiry":""}`, tk, exp) + if _, err := tf.Write([]byte(chJSON)); err != nil { + t.Fatalf("Unable to write to file %v: %v", tf.Name(), err) + } + credshelper = "cmd" + credshelperArgs = []string{ + "/c", + "cat", + tf.Name(), + } + } else { + credshelper = "echo" + credshelperArgs = []string{fmt.Sprintf(`{"token":"%v","expiry":"%s","refresh_expiry":""}`, tk, exp)} + } + + credsHelperCmd := newReusableCmd(credshelper, credshelperArgs) + ts := &externalTokenSource{ + credsHelperCmd: credsHelperCmd, + } + oauth2tk, err := ts.Token() + if err != nil { + t.Errorf("externalTokenSource.Token() returned an error: %v", err) + } + if oauth2tk.AccessToken != tk { + t.Errorf("externalTokenSource.Token() returned token=%s, want=%s", oauth2tk.AccessToken, tk) + } + if !oauth2tk.Expiry.Equal(expiry) { + t.Errorf("externalTokenSource.Token() returned expiry=%s, want=%s", oauth2tk.Expiry, exp) + } +} + +func TestExternalTokenRefresh(t *testing.T) { + tmp := t.TempDir() + tokenFile := filepath.Join(tmp, "reproxy.creds") + var ( + credshelper string + credshelperArgs []string + ) + if runtime.GOOS == "windows" { + credshelper = "cmd" + credshelperArgs = []string{ + "/c", + "cat", + tokenFile, + } + } else { + credshelper = "cat" + credshelperArgs = []string{ + tokenFile, + } + } + credsHelperCmd := newReusableCmd(credshelper, credshelperArgs) + ts := &externalTokenSource{ + credsHelperCmd: credsHelperCmd, + } + for _, token := range []string{"testToken", "testTokenRefresh"} { + expiry := time.Now().Truncate(time.Second) + writeTokenFile(t, tokenFile, token, expiry) + + oauth2tk, err := ts.Token() + if err != nil { + t.Errorf("externalTokenSource.Token() returned an error: %v", err) + } + if oauth2tk.AccessToken != token { + t.Errorf("externalTokenSource.Token() returned token=%s, want=%s", oauth2tk.AccessToken, token) + } + if !oauth2tk.Expiry.Equal(expiry) { + t.Errorf("externalTokenSource.Token() returned expiry=%s, want=%s", oauth2tk.Expiry, expiry) + } + } +} + +func writeTokenFile(t *testing.T, path, token string, expiry time.Time) { + t.Helper() + f, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) + if err != nil { + t.Fatalf("Unable to open file %v: %v", path, err) + } + defer f.Close() + chJSON := fmt.Sprintf(`{"token":"%v","expiry":"%s","refresh_expiry":""}`, token, expiry.Format(time.UnixDate)) + if _, err := f.Write([]byte(chJSON)); err != nil { + t.Fatalf("Unable to write to file %v: %v", f.Name(), err) + } +} + +func TestNewExternalCredentials(t *testing.T) { + testToken := "token" + exp := time.Now().Add(time.Hour).Truncate(time.Second) + expStr := exp.String() + unixExp := exp.Format(time.UnixDate) + tests := []struct { + name string + wantErr bool + checkExp bool + credshelperOut string + }{{ + name: "No Token", + wantErr: true, + credshelperOut: `{"token":"","expiry":"","refresh_expiry":""}`, + }, { + name: "Credshelper Command Passed - No Expiry", + credshelperOut: fmt.Sprintf(`{"token":"%v","expiry":"","refresh_expiry":""}`, testToken), + }, { + name: "Credshelper Command Passed - Expiry", + checkExp: true, + credshelperOut: fmt.Sprintf(`{"token":"%v","expiry":"%v","refresh_expiry":""}`, testToken, unixExp), + }, { + name: "Credshelper Command Passed - Refresh Expiry", + checkExp: true, + credshelperOut: fmt.Sprintf(`{"token":"%v","expiry":"%v","refresh_expiry":"%v"}`, testToken, unixExp, unixExp), + }, { + name: "Wrong Expiry Format", + wantErr: true, + credshelperOut: fmt.Sprintf(`{"token":"%v","expiry":"%v","refresh_expiry":"%v"}`, testToken, expStr, expStr), + }} + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var ( + credshelper string + credshelperArgs []string + ) + if runtime.GOOS == "windows" { + tf, err := os.CreateTemp("", "testnewexternalcreds.json") + if err != nil { + t.Fatalf("Unable to create temporary file: %v", err) + } + if _, err := tf.Write([]byte(test.credshelperOut)); err != nil { + t.Fatalf("Unable to write to file %v: %v", tf.Name(), err) + } + credshelper = "cmd" + credshelperArgs = []string{ + "/c", + "cat", + tf.Name(), + } + } else { + credshelper = "echo" + credshelperArgs = []string{test.credshelperOut} + } + + creds, err := NewExternalCredentials(credshelper, credshelperArgs, "") + if test.wantErr && err == nil { + t.Fatalf("NewExternalCredentials did not return an error.") + } + if !test.wantErr { + if err != nil { + t.Fatalf("NewExternalCredentials returned an error: %v", err) + } + if creds.tokenSource == nil { + t.Fatalf("NewExternalCredentials returned credentials with a nil tokensource.") + } + tk, err := creds.tokenSource.Token() + if err != nil { + t.Fatalf("tokensource.Token() call failed: %v", err) + } + if tk.AccessToken != testToken { + t.Fatalf("tokensource.Token() gave token=%s, want=%s", + tk.AccessToken, testToken) + } + if test.checkExp && !exp.Equal(tk.Expiry) { + t.Fatalf("tokensource.Token() gave expiry=%v, want=%v", + tk.Expiry, exp) + } + } + }) + } +} + +func TestReusableCmd(t *testing.T) { + binary := "echo" + args := []string{"hello"} + cmd := newReusableCmd(binary, args) + + output, err := cmd.Cmd().CombinedOutput() + if err != nil { + t.Errorf("Command failed: %v", err) + } else if string(output) != "hello\n" { + t.Errorf("Command returned unexpected output: %s", output) + } + + output, err = cmd.Cmd().CombinedOutput() + if err != nil { + t.Errorf("Command failed second time: %v", err) + } else if string(output) != "hello\n" { + t.Errorf("Command returned unexpected output second time: %s", output) + } +} + +func TestReusableCmdDigest(t *testing.T) { + cmd1 := newReusableCmd("echo", []string{"Hello"}) + cmd2 := newReusableCmd("echo", []string{"Bye"}) + if cmd1.Digest() == cmd2.Digest() { + t.Errorf("`%s` and `%s` have the same digest", cmd1, cmd2) + } +} diff --git a/go_deps.bzl b/go_deps.bzl index 417225f5..7d6deda9 100644 --- a/go_deps.bzl +++ b/go_deps.bzl @@ -61,6 +61,12 @@ def remote_apis_sdks_go_deps(): sum = "h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=", version = "v0.1.0", ) + go_repository( + name = "com_github_hectane_go_acl", + importpath = "github.com/hectane/go-acl", + sum = "h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM=", + version = "v0.0.0-20230122075934-ca0b05cb1adb", + ) go_repository( name = "com_github_golang_glog", importpath = "github.com/golang/glog", @@ -889,12 +895,6 @@ def remote_apis_sdks_go_deps(): sum = "h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=", version = "v0.7.0", ) - go_repository( - name = "org_golang_x_oauth2", - importpath = "golang.org/x/oauth2", - sum = "h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk=", - version = "v0.0.0-20221014153046-6fdb5e3db783", - ) go_repository( name = "org_golang_x_sync", importpath = "golang.org/x/sync",