Skip to content

Commit

Permalink
add support for remote execution APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
malt3 committed Feb 20, 2025
1 parent 4108d0a commit 893531b
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 0 deletions.
15 changes: 15 additions & 0 deletions authenticate/remoteapis/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
load("@rules_go//go:def.bzl", "go_library")

go_library(
name = "remoteapis",
srcs = ["remoteapis.go"],
importpath = "github.com/tweag/credential-helper/authenticate/remoteapis",
visibility = ["//visibility:public"],
deps = ["//api"],
)

filegroup(
name = "all_files",
srcs = glob(["*"]),
visibility = ["//:__subpackages__"],
)
83 changes: 83 additions & 0 deletions authenticate/remoteapis/remoteapis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package remoteapis

import (
"context"
"errors"
"net/url"
"strings"

"github.com/tweag/credential-helper/api"
)

// well-known grpc names (name of the Java package and the name of the service in the .proto file)
const (
GOOGLE_BYTESTREAM_BYTESTREAM = "google.bytestream.ByteStream"
REMOTE_ASSET_V1_FETCH = "build.bazel.remote.asset.v1.Fetch"
REMOTE_ASSET_V1_PUSH = "build.bazel.remote.asset.v1.Push"
REMOTE_EXECUTION_V2_ACTIONCACHE = "build.bazel.remote.execution.v2.ActionCache"
REMOTE_EXECUTION_V2_CAPABILITIES = "build.bazel.remote.execution.v2.Capabilities"
REMOTE_EXECUTION_V2_CONTENTADDRESSABLESTORAGE = "build.bazel.remote.execution.v2.ContentAddressableStorage"
REMOTE_EXECUTION_V2_EXECUTION = "build.bazel.remote.execution.v2.Execution"
)

type RemoteAPIs struct{}

// CacheKey returns a cache key for the given request.
// For remote apis, the full URI is a good cache key.
func (g *RemoteAPIs) CacheKey(req api.GetCredentialsRequest) string {
return req.URI
}

func (RemoteAPIs) Resolver(ctx context.Context) (api.Resolver, error) {
return &RemoteAPIs{}, nil
}

// Get implements the get command of the credential-helper spec:
//
// https://github.com/EngFlow/credential-helper-spec/blob/main/spec.md#get
func (g *RemoteAPIs) Get(ctx context.Context, req api.GetCredentialsRequest) (api.GetCredentialsResponse, error) {
parsedURL, error := url.Parse(req.URI)
if error != nil {
return api.GetCredentialsResponse{}, error
}

// the scheme for remote APIs appears to be https for all of the following:
// - https://
// - grpc://
// - grpcs://
//
// only unencrypted http:// uses a different scheme
// TOOD: check what we do for different schemes

// the following only works for grpc (and not HTTP/1.1)
rpcName, hasPrefix := strings.CutPrefix(parsedURL.Path, "/")
if !hasPrefix {
return api.GetCredentialsResponse{}, errors.New("remote execution API path must start with /")
}
switch rpcName {
default:
return api.GetCredentialsResponse{}, errors.New("unknown remote execution API")
case GOOGLE_BYTESTREAM_BYTESTREAM:
case REMOTE_ASSET_V1_FETCH:
case REMOTE_ASSET_V1_PUSH:
case REMOTE_EXECUTION_V2_ACTIONCACHE:
case REMOTE_EXECUTION_V2_CAPABILITIES:
case REMOTE_EXECUTION_V2_CONTENTADDRESSABLESTORAGE:
case REMOTE_EXECUTION_V2_EXECUTION:
}

// bazel-remote only supports basic auth.
// It tries to read the standard grpc metadata key ":authority" to get the username and password.
// This is special header that the credential helper cannot provide.
// As a fallback for proxies, bazel-remote also reads the grpc metadata key "authorization" to get the username and password encoded as a base64 string.
return api.GetCredentialsResponse{
// Expires: expires,
Headers: map[string][]string{
// this doesn't work
// ":authority": {"user:pass"},
// this does work for bazel-remote
// "authorization": {"Basic dXNlcjpwYXNz"},
// TODO: read config file to learn secret source (env var, secret store, etc.), and the method (basic auth, auth header, etc.)
},
}, nil
}
1 change: 1 addition & 0 deletions bzl/private/source_files/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ release_files = [
"//authenticate/internal/helperconfig:all_files",
"//authenticate/null:all_files",
"//authenticate/oci:all_files",
"//authenticate/remoteapis:all_files",
"//authenticate/s3:all_files",
"//bzl:all_files",
"//bzl/config:all_files",
Expand Down
12 changes: 12 additions & 0 deletions cmd/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,20 @@ func foreground(ctx context.Context, helperFactory api.HelperFactory, cache api.
logging.Fatalf("%v", err)
}

logging.Debugf("writing request to log at %s", filepath.Join(locate.Run(), "log"))
log, err := os.OpenFile(filepath.Join(locate.Run(), "log"), os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.ModePerm)
// log, err := os.Create(filepath.Join(locate.Run(), "log"))
if err != nil {
logging.Fatalf("opening log file: %v", err)
}
defer log.Close()
if _, err := log.WriteString(fmt.Sprintf("%v\n", req.URI)); err != nil {
logging.Fatalf("writing request to log: %v", err)
}

cfg, err := configReader.Read()
if err == nil {
logging.Debugf("found config file and choosing helper from it")
helperFactory = func(uri string) (api.Helper, error) {
helper, helperConfig, err := cfg.FindHelper(uri)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions config/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ go_library(
"//agent/locate",
"//api",
"//helperfactory/string",
"//logging",
],
)

Expand Down
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/tweag/credential-helper/agent/locate"
"github.com/tweag/credential-helper/api"
helperstringfactory "github.com/tweag/credential-helper/helperfactory/string"
"github.com/tweag/credential-helper/logging"
)

var ErrConfigNotFound = errors.New("config file not found")
Expand Down Expand Up @@ -54,6 +55,7 @@ func (c Config) FindHelper(uri string) (api.Helper, []byte, error) {
}
helper := helperstringfactory.HelperFromString(urlConfig.Helper)
if helper != nil {
logging.Debugf("selected helper %s from config", urlConfig.Helper)
return helper, urlConfig.Config, nil
}
return nil, nil, fmt.Errorf("unknown helper: %s", urlConfig.Helper)
Expand Down
1 change: 1 addition & 0 deletions helperfactory/string/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ go_library(
"//authenticate/github",
"//authenticate/null",
"//authenticate/oci",
"//authenticate/remoteapis",
"//authenticate/s3",
],
)
Expand Down
3 changes: 3 additions & 0 deletions helperfactory/string/helperstring.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
authenticateGitHub "github.com/tweag/credential-helper/authenticate/github"
authenticateNull "github.com/tweag/credential-helper/authenticate/null"
authenticateOCI "github.com/tweag/credential-helper/authenticate/oci"
authenticateRemoteAPIs "github.com/tweag/credential-helper/authenticate/remoteapis"
authenticateS3 "github.com/tweag/credential-helper/authenticate/s3"
)

Expand All @@ -20,6 +21,8 @@ func HelperFromString(s string) api.Helper {
return &authenticateGitHub.GitHub{}
case "oci":
return authenticateOCI.NewFallbackOCI()
case "remoteapis":
return &authenticateRemoteAPIs.RemoteAPIs{}
case "null":
return &authenticateNull.Null{}
}
Expand Down
1 change: 1 addition & 0 deletions testdata/remote_execution_v2_capabilities.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"uri": "https://192.168.178.43:9092/build.bazel.remote.execution.v2.Capabilities"}

0 comments on commit 893531b

Please sign in to comment.