blob: e2f88331fd103d59c522aa822deb67c98cea0b85 [file] [log] [blame]
// Copyright 2019 The LUCI Authors.
//
// 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 host
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"time"
"cloud.google.com/go/compute/metadata"
"golang.org/x/oauth2"
"go.chromium.org/luci/auth"
"go.chromium.org/luci/auth/integration/authtest"
"go.chromium.org/luci/auth/integration/localauth"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/common/logging"
"go.chromium.org/luci/common/logging/gologger"
"go.chromium.org/luci/lucictx"
. "github.com/smartystreets/goconvey/convey"
)
// Use this instead of ShouldStartWith to avoid logging a real token if the
// assertion fails.
//
// If the fake auth and/or this package don't work as intended, it's possible
// that the real ambient Swarming auth could leak through to the test. Using
// ShouldStartWith would, on an assertion failure, print the 'actual' value
// which would be a real authentication token, should the context leak.
func tokenShouldStartWith(actual any, expected ...any) string {
if len(expected) != 1 {
panic(errors.Reason("tokenShouldStartWith takes one expected value, got %d", len(expected)).Err())
}
if expectedStr := expected[0].(string); !strings.HasPrefix(actual.(string), expectedStr) {
return fmt.Sprintf("Expected <token> to start with %q", expectedStr)
}
return ""
}
func testCtx() (context.Context, func()) {
ctx := context.Background()
if testing.Verbose() {
ctx = logging.SetLevel(gologger.StdConfig.Use(ctx), logging.Debug)
}
// Setup a fake local auth. It is a real localhost server, it needs real
// clock, so set it up before mocking time.
fakeAuth := localauth.Server{
TokenGenerators: map[string]localauth.TokenGenerator{
"task": &authtest.FakeTokenGenerator{
Email: "task@example.com",
Prefix: "task_token_",
},
},
DefaultAccountID: "task",
}
la, err := fakeAuth.Start(ctx)
So(err, ShouldBeNil)
ctx = lucictx.SetLocalAuth(ctx, la)
return ctx, func() { fakeAuth.Stop(ctx) }
}
func TestAuth(t *testing.T) {
Convey(`test auth environment`, t, func() {
ctx, closer := testCtx()
defer closer()
Convey(`default`, func(c C) {
ch, err := Run(ctx, nil, func(ctx context.Context, _ Options, _ <-chan lucictx.DeadlineEvent, _ func()) {
c.Convey("Task account is available", func() {
a := auth.NewAuthenticator(ctx, auth.SilentLogin, auth.Options{
Method: auth.LUCIContextMethod,
})
email, err := a.GetEmail()
So(err, ShouldBeNil)
So(email, ShouldEqual, "task@example.com")
tok, err := a.GetAccessToken(time.Minute)
So(err, ShouldBeNil)
So(tok.AccessToken, tokenShouldStartWith, "task_token_")
})
c.Convey("Git config is set", func() {
gitHome := os.Getenv("INFRA_GIT_WRAPPER_HOME")
So(gitHome, ShouldNotEqual, "")
cfg, err := os.ReadFile(filepath.Join(gitHome, ".gitconfig"))
So(err, ShouldBeNil)
So(string(cfg), ShouldContainSubstring, "email = task@example.com")
So(string(cfg), ShouldContainSubstring, "helper = luci")
})
c.Convey("GCE metadata server is faked", func() {
// Note: metadata.OnGCE() and other top-level functions in `metadata`
// package are unreliable in this test since their values may be
// cached in the process global memory the first time they are called
// and luciexe call them prior to the test.
//
// metadata.NewClient doesn't use any process-global caches and picks
// up the GCE_METADATA_HOST env var change done by the test.
md := metadata.NewClient(nil)
email, err := md.Email("default")
So(err, ShouldBeNil)
So(email, ShouldEqual, "task@example.com")
tokBody, err := md.Get("instance/service-accounts/default/token")
So(err, ShouldBeNil)
tok := &oauth2.Token{}
So(json.Unmarshal([]byte(tokBody), &tok), ShouldBeNil)
So(tok.AccessToken, tokenShouldStartWith, "task_token_")
})
})
So(err, ShouldBeNil)
for range ch {
// TODO(iannucci): check for Build object contents
}
})
})
}