mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-23 00:56:20 +00:00
.github,cmd/cigocacher: add flags --version --stats --cigocached-host
Add flags: * --cigocached-host to support alternative host resolution in other environments, like the corp repo. * --stats to reduce the amount of bash script we need. * --version to support a caching tool/cigocacher script that will download from GitHub releases. Updates tailscale/corp#10808 Change-Id: Ib2447bc5f79058669a70f2c49cef6aedd7afc049 Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
This commit is contained in:
55
.github/actions/go-cache/action.sh
vendored
55
.github/actions/go-cache/action.sh
vendored
@@ -7,6 +7,7 @@
|
|||||||
# Usage: ./action.sh
|
# Usage: ./action.sh
|
||||||
# Inputs:
|
# Inputs:
|
||||||
# URL: The cigocached server URL.
|
# URL: The cigocached server URL.
|
||||||
|
# HOST: The cigocached server host to dial.
|
||||||
# Outputs:
|
# Outputs:
|
||||||
# success: Whether cigocacher was set up successfully.
|
# success: Whether cigocacher was set up successfully.
|
||||||
|
|
||||||
@@ -22,57 +23,17 @@ if [ -z "${URL:-}" ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
curl_and_parse() {
|
BIN_PATH="${RUNNER_TEMP:-/tmp}/cigocacher$(go env GOEXE)"
|
||||||
local jq_filter="$1"
|
go build -o "${BIN_PATH}" ./cmd/cigocacher
|
||||||
local step="$2"
|
|
||||||
shift 2
|
|
||||||
|
|
||||||
local response
|
CIGOCACHER_TOKEN="$("${BIN_PATH}" --auth --cigocached-url "${URL}" --cigocached-host "${HOST}" )"
|
||||||
local curl_exit
|
if [ -z "${CIGOCACHER_TOKEN:-}" ]; then
|
||||||
response="$(curl -sSL "$@" 2>&1)" || curl_exit="$?"
|
echo "Failed to fetch cigocacher token, skipping cigocacher setup"
|
||||||
if [ "${curl_exit:-0}" -ne "0" ]; then
|
exit 0
|
||||||
echo "${step}: ${response}" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local parsed
|
|
||||||
local jq_exit
|
|
||||||
parsed=$(echo "${response}" | jq -e -r "${jq_filter}" 2>&1) || jq_exit=$?
|
|
||||||
if [ "${jq_exit:-0}" -ne "0" ]; then
|
|
||||||
echo "${step}: Failed to parse JSON response:" >&2
|
|
||||||
echo "${response}" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "${parsed}"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
JWT="$(curl_and_parse ".value" "Fetching GitHub identity JWT" \
|
|
||||||
-H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
|
|
||||||
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=gocached")" || exit 0
|
|
||||||
|
|
||||||
# cigocached serves a TLS cert with an FQDN, but DNS is based on VM name.
|
|
||||||
HOST_AND_PORT="${URL#http*://}"
|
|
||||||
FIRST_LABEL="${HOST_AND_PORT/.*/}"
|
|
||||||
# Save CONNECT_TO for later steps to use.
|
|
||||||
echo "CONNECT_TO=${HOST_AND_PORT}:${FIRST_LABEL}:" >> "${GITHUB_ENV}"
|
|
||||||
BODY="$(jq -n --arg jwt "$JWT" '{"jwt": $jwt}')"
|
|
||||||
CIGOCACHER_TOKEN="$(curl_and_parse ".access_token" "Exchanging token with cigocached" \
|
|
||||||
--connect-to "${HOST_AND_PORT}:${FIRST_LABEL}:" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
"$URL/auth/exchange-token" \
|
|
||||||
-d "$BODY")" || exit 0
|
|
||||||
|
|
||||||
# Wait until we successfully auth before building cigocacher to ensure we know
|
|
||||||
# it's worth building.
|
|
||||||
# TODO(tomhjp): bake cigocacher into runner image and use it for auth.
|
|
||||||
echo "Fetched cigocacher token successfully"
|
echo "Fetched cigocacher token successfully"
|
||||||
echo "::add-mask::${CIGOCACHER_TOKEN}"
|
echo "::add-mask::${CIGOCACHER_TOKEN}"
|
||||||
echo "CIGOCACHER_TOKEN=${CIGOCACHER_TOKEN}" >> "${GITHUB_ENV}"
|
|
||||||
|
|
||||||
BIN_PATH="${RUNNER_TEMP:-/tmp}/cigocacher$(go env GOEXE)"
|
echo "GOCACHEPROG=${BIN_PATH} --cache-dir ${CACHE_DIR} --cigocached-url ${URL} --cigocached-host ${HOST} --token ${CIGOCACHER_TOKEN}" >> "${GITHUB_ENV}"
|
||||||
|
|
||||||
go build -o "${BIN_PATH}" ./cmd/cigocacher
|
|
||||||
echo "GOCACHEPROG=${BIN_PATH} --cache-dir ${CACHE_DIR} --cigocached-url ${URL} --token ${CIGOCACHER_TOKEN}" >> "${GITHUB_ENV}"
|
|
||||||
echo "success=true" >> "${GITHUB_OUTPUT}"
|
echo "success=true" >> "${GITHUB_OUTPUT}"
|
||||||
|
|||||||
4
.github/actions/go-cache/action.yml
vendored
4
.github/actions/go-cache/action.yml
vendored
@@ -5,6 +5,9 @@ inputs:
|
|||||||
cigocached-url:
|
cigocached-url:
|
||||||
description: URL of the cigocached server
|
description: URL of the cigocached server
|
||||||
required: true
|
required: true
|
||||||
|
cigocached-host:
|
||||||
|
description: Host to dial for the cigocached server
|
||||||
|
required: true
|
||||||
checkout-path:
|
checkout-path:
|
||||||
description: Path to cloned repository
|
description: Path to cloned repository
|
||||||
required: true
|
required: true
|
||||||
@@ -25,6 +28,7 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
URL: ${{ inputs.cigocached-url }}
|
URL: ${{ inputs.cigocached-url }}
|
||||||
|
HOST: ${{ inputs.cigocached-host }}
|
||||||
CACHE_DIR: ${{ inputs.cache-dir }}
|
CACHE_DIR: ${{ inputs.cache-dir }}
|
||||||
working-directory: ${{ inputs.checkout-path }}
|
working-directory: ${{ inputs.checkout-path }}
|
||||||
run: .github/actions/go-cache/action.sh
|
run: .github/actions/go-cache/action.sh
|
||||||
|
|||||||
7
.github/workflows/test.yml
vendored
7
.github/workflows/test.yml
vendored
@@ -263,6 +263,7 @@ jobs:
|
|||||||
checkout-path: ${{ github.workspace }}/src
|
checkout-path: ${{ github.workspace }}/src
|
||||||
cache-dir: ${{ github.workspace }}/cigocacher
|
cache-dir: ${{ github.workspace }}/cigocacher
|
||||||
cigocached-url: ${{ vars.CIGOCACHED_AZURE_URL }}
|
cigocached-url: ${{ vars.CIGOCACHED_AZURE_URL }}
|
||||||
|
cigocached-host: ${{ vars.CIGOCACHED_AZURE_HOST }}
|
||||||
|
|
||||||
- name: test
|
- name: test
|
||||||
if: matrix.key != 'win-bench' # skip on bench builder
|
if: matrix.key != 'win-bench' # skip on bench builder
|
||||||
@@ -278,10 +279,12 @@ jobs:
|
|||||||
run: go test ./... -bench . -benchtime 1x -run "^$"
|
run: go test ./... -bench . -benchtime 1x -run "^$"
|
||||||
|
|
||||||
- name: Print stats
|
- name: Print stats
|
||||||
shell: bash
|
shell: pwsh
|
||||||
if: steps.cigocacher-setup.outputs.success == 'true'
|
if: steps.cigocacher-setup.outputs.success == 'true'
|
||||||
|
env:
|
||||||
|
GOCACHEPROG: ${{ env.GOCACHEPROG }}
|
||||||
run: |
|
run: |
|
||||||
curl -sSL --connect-to "${CONNECT_TO}" -H "Authorization: Bearer ${CIGOCACHER_TOKEN}" "${{ vars.CIGOCACHED_AZURE_URL }}/session/stats" | jq .
|
Invoke-Expression "$env:GOCACHEPROG --stats" | jq .
|
||||||
|
|
||||||
win-tool-go:
|
win-tool-go:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|||||||
@@ -22,8 +22,11 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime/debug"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -34,20 +37,56 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
|
version = flag.Bool("version", false, "print version and exit")
|
||||||
auth = flag.Bool("auth", false, "auth with cigocached and exit, printing the access token as output")
|
auth = flag.Bool("auth", false, "auth with cigocached and exit, printing the access token as output")
|
||||||
|
stats = flag.Bool("stats", false, "fetch and print cigocached stats and exit")
|
||||||
token = flag.String("token", "", "the cigocached access token to use, as created using --auth")
|
token = flag.String("token", "", "the cigocached access token to use, as created using --auth")
|
||||||
cigocachedURL = flag.String("cigocached-url", "", "optional cigocached URL (scheme, host, and port). empty means to not use one.")
|
srvURL = flag.String("cigocached-url", "", "optional cigocached URL (scheme, host, and port). Empty means to not use one.")
|
||||||
|
srvHostDial = flag.String("cigocached-host", "", "optional cigocached host to dial instead of the host in the provided --cigocached-url. Useful for public TLS certs on private addresses.")
|
||||||
dir = flag.String("cache-dir", "", "cache directory; empty means automatic")
|
dir = flag.String("cache-dir", "", "cache directory; empty means automatic")
|
||||||
verbose = flag.Bool("verbose", false, "enable verbose logging")
|
verbose = flag.Bool("verbose", false, "enable verbose logging")
|
||||||
)
|
)
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if *version {
|
||||||
|
info, ok := debug.ReadBuildInfo()
|
||||||
|
if !ok {
|
||||||
|
log.Fatal("no build info")
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
rev string
|
||||||
|
dirty bool
|
||||||
|
)
|
||||||
|
for _, s := range info.Settings {
|
||||||
|
switch s.Key {
|
||||||
|
case "vcs.revision":
|
||||||
|
rev = s.Value
|
||||||
|
case "vcs.modified":
|
||||||
|
dirty, _ = strconv.ParseBool(s.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dirty {
|
||||||
|
rev += "-dirty"
|
||||||
|
}
|
||||||
|
fmt.Println(rev)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var srvHost string
|
||||||
|
if *srvHostDial != "" && *srvURL != "" {
|
||||||
|
u, err := url.Parse(*srvURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
srvHost = u.Hostname()
|
||||||
|
}
|
||||||
|
|
||||||
if *auth {
|
if *auth {
|
||||||
if *cigocachedURL == "" {
|
if *srvURL == "" {
|
||||||
log.Print("--cigocached-url is empty, skipping auth")
|
log.Print("--cigocached-url is empty, skipping auth")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tk, err := fetchAccessToken(httpClient(), os.Getenv("ACTIONS_ID_TOKEN_REQUEST_URL"), os.Getenv("ACTIONS_ID_TOKEN_REQUEST_TOKEN"), *cigocachedURL)
|
tk, err := fetchAccessToken(httpClient(srvHost, *srvHostDial), os.Getenv("ACTIONS_ID_TOKEN_REQUEST_URL"), os.Getenv("ACTIONS_ID_TOKEN_REQUEST_TOKEN"), *srvURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error fetching access token, skipping auth: %v", err)
|
log.Printf("error fetching access token, skipping auth: %v", err)
|
||||||
return
|
return
|
||||||
@@ -56,6 +95,28 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *stats {
|
||||||
|
if *srvURL == "" {
|
||||||
|
log.Fatal("--cigocached-url is empty; cannot fetch stats")
|
||||||
|
}
|
||||||
|
tk := *token
|
||||||
|
if tk == "" {
|
||||||
|
log.Fatal("--token is empty; cannot fetch stats")
|
||||||
|
}
|
||||||
|
c := &gocachedClient{
|
||||||
|
baseURL: *srvURL,
|
||||||
|
cl: httpClient(srvHost, *srvHostDial),
|
||||||
|
accessToken: tk,
|
||||||
|
verbose: *verbose,
|
||||||
|
}
|
||||||
|
stats, err := c.fetchStats()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error fetching gocached stats: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Println(stats)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if *dir == "" {
|
if *dir == "" {
|
||||||
d, err := os.UserCacheDir()
|
d, err := os.UserCacheDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -75,13 +136,13 @@ func main() {
|
|||||||
},
|
},
|
||||||
verbose: *verbose,
|
verbose: *verbose,
|
||||||
}
|
}
|
||||||
if *cigocachedURL != "" {
|
if *srvURL != "" {
|
||||||
if *verbose {
|
if *verbose {
|
||||||
log.Printf("Using cigocached at %s", *cigocachedURL)
|
log.Printf("Using cigocached at %s", *srvURL)
|
||||||
}
|
}
|
||||||
c.gocached = &gocachedClient{
|
c.gocached = &gocachedClient{
|
||||||
baseURL: *cigocachedURL,
|
baseURL: *srvURL,
|
||||||
cl: httpClient(),
|
cl: httpClient(srvHost, *srvHostDial),
|
||||||
accessToken: *token,
|
accessToken: *token,
|
||||||
verbose: *verbose,
|
verbose: *verbose,
|
||||||
}
|
}
|
||||||
@@ -104,18 +165,18 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpClient() *http.Client {
|
func httpClient(srvHost, srvHostDial string) *http.Client {
|
||||||
|
if srvHost == "" || srvHostDial == "" {
|
||||||
|
return http.DefaultClient
|
||||||
|
}
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
host, port, err := net.SplitHostPort(addr)
|
if host, port, err := net.SplitHostPort(addr); err == nil && host == srvHost {
|
||||||
if err == nil {
|
// This allows us to serve a publicly trusted TLS cert
|
||||||
// This does not run in a tailnet. We serve corp.ts.net
|
// while also minimising latency by explicitly using a
|
||||||
// TLS certs, and override DNS resolution to lookup the
|
// private network address.
|
||||||
// private IP for the VM by its hostname.
|
addr = net.JoinHostPort(srvHostDial, port)
|
||||||
if vm, ok := strings.CutSuffix(host, ".corp.ts.net"); ok {
|
|
||||||
addr = net.JoinHostPort(vm, port)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var d net.Dialer
|
var d net.Dialer
|
||||||
return d.DialContext(ctx, network, addr)
|
return d.DialContext(ctx, network, addr)
|
||||||
|
|||||||
@@ -32,12 +32,6 @@ func tryReadErrorMessage(res *http.Response) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *gocachedClient) get(ctx context.Context, actionID string) (outputID string, resp *http.Response, err error) {
|
func (c *gocachedClient) get(ctx context.Context, actionID string) (outputID string, resp *http.Response, err error) {
|
||||||
// TODO(tomhjp): make sure we timeout if cigocached disappears, but for some
|
|
||||||
// reason, this seemed to tank network performance.
|
|
||||||
// // Set a generous upper limit on the time we'll wait for a response. We'll
|
|
||||||
// // shorten this deadline later once we know the content length.
|
|
||||||
// ctx, cancel := context.WithTimeout(ctx, time.Minute)
|
|
||||||
// defer cancel()
|
|
||||||
req, _ := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/action/"+actionID, nil)
|
req, _ := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/action/"+actionID, nil)
|
||||||
req.Header.Set("Want-Object", "1") // opt in to single roundtrip protocol
|
req.Header.Set("Want-Object", "1") // opt in to single roundtrip protocol
|
||||||
if c.accessToken != "" {
|
if c.accessToken != "" {
|
||||||
|
|||||||
Reference in New Issue
Block a user