-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add goauth
binary for GOAUTH
environment support
#22
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// Copyright 2022 Google LLC All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
package main | ||
|
||
import ( | ||
"context" | ||
"encoding/base64" | ||
"flag" | ||
"fmt" | ||
"log" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
"github.com/GoogleCloudPlatform/artifact-registry-go-tools/pkg/auth" | ||
) | ||
|
||
const help = ` | ||
Handle Go authentication with Google Cloud Artifact Registry Go Repositories. | ||
|
||
Add to your GOAUTH environment variable: | ||
|
||
export GOAUTH="sh -c 'GOPROXY=direct go run github.com/GoogleCloudPlatform/artifact-registry-go-tools/cmd/goauth@latest <location>'" | ||
|
||
To support multiple locations, add the command multiple times to the GOAUTH variable (semicolon-separated). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add an example for using this together with a non Artifact Registry repo? e.g.,
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not against it, but your example won't work, see below. Please feel free to do some testing and verification on your side. |
||
|
||
For more details, see https://pkg.go.dev/cmd/go@master#hdr-GOAUTH_environment_variable` | ||
|
||
const defaultHostPattern = "%s-go.pkg.dev" | ||
|
||
func main() { | ||
jsonKey := flag.String("json_key", "", "path to the json key of the service account used for this location. Leave empty to use the oauth token instead.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was wondering if we could reuse the same program entry point. The main motivation is to avoid duplicate code (the two flags here), or at least make it harder for future developers to miss one another when adding more features to goauth or netrc. maybe
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's better to have it separate, because the application behavior is dictated by Go and will have to be different from the Even if they seem to be similar now, they probably will divert more in the future. |
||
hostPattern := flag.String("host_pattern", defaultHostPattern, "Artifact Registry server host pattern, where %s will be replaced by a location string.") | ||
|
||
flag.Parse() | ||
|
||
location := flag.Arg(0) | ||
if location == "" { | ||
fmt.Fprintln(os.Stderr, help) | ||
return | ||
} | ||
if strings.HasPrefix(location, "https://") { | ||
log.Println("Location has to be a Google Cloud region, e.g. 'us-central1'.") | ||
os.Exit(2) | ||
} | ||
|
||
// generate the authentication header | ||
urlLine := locationURL(location, *hostPattern) | ||
authHeader, err := keyAuthHeader(*jsonKey) | ||
if err != nil { | ||
log.Println(err) | ||
os.Exit(3) | ||
} | ||
|
||
// send the Go authentication information | ||
fmt.Printf("%s\n\nAuthorization: %s\n\n", urlLine, authHeader) | ||
} | ||
|
||
func locationURL(location string, hostPattern string) string { | ||
host := fmt.Sprintf(hostPattern, location) | ||
|
||
return fmt.Sprintf("https://%s", host) | ||
} | ||
|
||
func keyAuthHeader(jsonKeyPath string) (string, error) { | ||
ctx := context.Background() | ||
ctx, cancel := context.WithTimeout(ctx, 30*time.Second) | ||
defer cancel() | ||
|
||
if jsonKeyPath != "" { | ||
key, err := auth.EncodeJsonKey(jsonKeyPath) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to encode JSON key: %w", err) | ||
} | ||
|
||
return basicAuthHeader("_json_key_base64", key), nil | ||
} else { | ||
token, err := auth.Token(ctx) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to get oauth token: %w", err) | ||
} | ||
|
||
return basicAuthHeader("oauth2accesstoken", token), nil | ||
} | ||
} | ||
|
||
func basicAuthHeader(username, password string) string { | ||
a := fmt.Sprintf("%s:%s", username, password) | ||
b := base64.StdEncoding.EncodeToString([]byte(a)) | ||
|
||
return fmt.Sprintf("Basic %s", b) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/base64" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestLocationURL(t *testing.T) { | ||
url := locationURL("us-central1", defaultHostPattern) | ||
|
||
if url != "https://us-central1-go.pkg.dev" { | ||
t.Fatalf("unexpected url: %s", url) | ||
} | ||
} | ||
|
||
func TestAuthHeader_DefaultCredentials(t *testing.T) { | ||
header, err := keyAuthHeader("") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
t.Logf("header: %s", header) | ||
|
||
if !strings.HasPrefix(header, "Basic ") { | ||
t.Fatal(err) | ||
} | ||
|
||
decoded, err := base64.StdEncoding.DecodeString(header[6:]) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
t.Logf("decoded: %s", decoded) | ||
|
||
if !strings.HasPrefix(string(decoded), "oauth2accesstoken:") { | ||
t.Fatal(err) | ||
} | ||
} | ||
|
||
func TestAuthHeader_JSONKey(t *testing.T) { | ||
header, err := keyAuthHeader("./test/dummy.json") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
t.Logf("header: %s", header) | ||
|
||
if !strings.HasPrefix(header, "Basic ") { | ||
t.Fatal(err) | ||
} | ||
|
||
decoded, err := base64.StdEncoding.DecodeString(header[6:]) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
t.Logf("decoded: %s", decoded) | ||
|
||
if !strings.HasPrefix(string(decoded), "_json_key_base64:") { | ||
t.Fatal(err) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{ "comment": "the content of this file is irrelevant" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we can leave out
sh -c
. Without it the command should still work but not sure ifsh -c
works on Windows?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It won't work on Windows and I doubt sh -c is also needed.
must work allright
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it does not. Please feel free to do some testing and verification.
I already checked these scenarios:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the Windows support: Feel free to suggest an example for Windows, after testing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the instructions must change to ask users to run go install first instead of go run
If you install the binary first like this:
GOPROXY=direct go install github.com/GoogleCloudPlatform/artifact-registry-go-tools/cmd/goauth@latest
Then you can use the installed binary directly in GOAUTH without having to use GOPROXY
GOAUTH="goauth europe-west1"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even better would be for this to be distributed with the gcloud SDK like the docker Auth does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if going with @cagataygurturk , it needs to be renamed, e.g.
go-auth-google-artifact-registry
orgo-auth-gar
or sth. like this.Besides that, I kind of agree with @owenhaynes , that this should become part of the Google Cloud SDK.
On our side, we solved this easily, because we run a mono repository, so we already have a place to put team-wide tooling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had the same thought - but the original rationale of implementing auth helpers in the same language as the repository format is that the users won't have a dependency on gcloud SDK in their build flow, and some customers do care about this mostly because gcloud is huge.
That being said I think implementing it within gcloud is indeed simpler; I'll go ahead and do that first to hopefully unblock folks. I would like to eventually add support for it in this auth tool, but I think it would require some design review among the team so I'll get back to it later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI after spending a lot of time debugging what's described in golang/go#71889 I finally have made it to work locally. Need some extra time adding tests and stuff and this should be available very soon.