-
Notifications
You must be signed in to change notification settings - Fork 156
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cmd/makemac: keep-alive service for MacService leases
makemac is a tiny service which renews existing MacService leases on a periodic basis to ensure that they stay alive. This is a temporary holdover until we get proper support for MacService somewhere like Robocrop. For golang/go#60440. Change-Id: Id52a39dda6bd3858e243a7ae0469ec8a85356309 Reviewed-on: https://go-review.googlesource.com/c/build/+/546495 Auto-Submit: Michael Pratt <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Carlos Amedee <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
- Loading branch information
Showing
9 changed files
with
322 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,7 +37,7 @@ COPY . /go/src/golang.org/x/build/ | |
RUN go install golang.org/x/build/cmd/gerritbot | ||
|
||
FROM alpine | ||
LABEL maintainer "[email protected]" | ||
LABEL maintainer="[email protected]" | ||
# See https://github.com/golang/go/issues/23705 for why tini is needed | ||
RUN apk add --no-cache git tini | ||
RUN git config --global user.email "[email protected]" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Copyright 2023 The Go Authors. All rights reserved. | ||
# Use of this source code is governed by a BSD-style | ||
# license that can be found in the LICENSE file. | ||
|
||
FROM golang:1.21-bookworm AS build | ||
LABEL maintainer="[email protected]" | ||
|
||
COPY go.mod /go/src/golang.org/x/build/go.mod | ||
COPY go.sum /go/src/golang.org/x/build/go.sum | ||
|
||
WORKDIR /go/src/golang.org/x/build | ||
|
||
# Download module dependencies to improve speed of re-building the | ||
# Docker image during minor code changes. | ||
RUN go mod download | ||
|
||
COPY . /go/src/golang.org/x/build/ | ||
|
||
RUN go install golang.org/x/build/cmd/makemac | ||
|
||
FROM debian:bookworm | ||
LABEL maintainer="[email protected]" | ||
|
||
# netbase and ca-certificates are needed for dialing TLS. | ||
# The rest are useful for debugging if somebody needs to exec into the container. | ||
RUN apt-get update && apt-get install -y \ | ||
--no-install-recommends \ | ||
netbase \ | ||
ca-certificates \ | ||
curl \ | ||
strace \ | ||
procps \ | ||
lsof \ | ||
psmisc \ | ||
&& rm -rf /var/lib/apt/lists/* | ||
|
||
COPY --from=build /go/bin/makemac / | ||
ENTRYPOINT ["/makemac"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Copyright 2023 The Go Authors. All rights reserved. | ||
# Use of this source code is governed by a BSD-style | ||
# license that can be found in the LICENSE file. | ||
|
||
MUTABLE_VERSION ?= latest | ||
VERSION ?= $(shell git rev-parse --short HEAD) | ||
|
||
IMAGE_PROD := gcr.io/symbolic-datum-552/makemac | ||
|
||
docker-prod: | ||
docker build -f Dockerfile --force-rm --tag=$(IMAGE_PROD):$(VERSION) ../.. | ||
docker tag $(IMAGE_PROD):$(VERSION) $(IMAGE_PROD):$(MUTABLE_VERSION) | ||
|
||
push-prod: docker-prod | ||
docker push $(IMAGE_PROD):$(MUTABLE_VERSION) | ||
docker push $(IMAGE_PROD):$(VERSION) | ||
|
||
deploy-prod: push-prod | ||
go install golang.org/x/build/cmd/xb | ||
xb --prod kubectl --namespace prod set image deployment/makemac-deployment makemac=$(IMAGE_PROD):$(VERSION) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# Copyright 2023 The Go Authors. All rights reserved. | ||
# Use of this source code is governed by a BSD-style | ||
# license that can be found in the LICENSE file. | ||
|
||
apiVersion: apps/v1 | ||
kind: Deployment | ||
metadata: | ||
namespace: prod | ||
name: makemac-deployment | ||
spec: | ||
selector: | ||
matchLabels: | ||
app: makemac | ||
template: | ||
metadata: | ||
labels: | ||
app: makemac | ||
spec: | ||
serviceAccountName: makemac | ||
containers: | ||
- name: makemac | ||
image: gcr.io/symbolic-datum-552/makemac:latest | ||
imagePullPolicy: Always | ||
command: ["/makemac", "-api-key=secret:macservice-api-key"] | ||
resources: | ||
requests: | ||
cpu: "1" | ||
memory: "1Gi" | ||
limits: | ||
cpu: "2" | ||
memory: "2Gi" | ||
--- | ||
apiVersion: v1 | ||
kind: ServiceAccount | ||
metadata: | ||
namespace: prod | ||
name: makemac | ||
annotations: | ||
iam.gke.io/gcp-service-account: [email protected] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// Copyright 2023 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// Command makemac ensures that MacService instances continue running. | ||
// Currently, it simply renews any existing leases. | ||
package main | ||
|
||
import ( | ||
"context" | ||
"flag" | ||
"log" | ||
"time" | ||
|
||
"golang.org/x/build/internal/macservice" | ||
"golang.org/x/build/internal/secret" | ||
) | ||
|
||
var ( | ||
apiKey = secret.Flag("api-key", "MacService API key") | ||
period = flag.Duration("period", 2*time.Hour, "How often to check leases. As a special case, -period=0 checks exactly once and then exits") | ||
) | ||
|
||
const renewDuration = "86400s" // 24h | ||
|
||
func main() { | ||
secret.InitFlagSupport(context.Background()) | ||
flag.Parse() | ||
|
||
c := macservice.NewClient(*apiKey) | ||
|
||
// Always check once at startup. | ||
checkAndRenewLeases(c) | ||
|
||
if *period == 0 { | ||
// User only wants a single check. We're done. | ||
return | ||
} | ||
|
||
t := time.NewTicker(*period) | ||
for range t.C { | ||
checkAndRenewLeases(c) | ||
} | ||
} | ||
|
||
func checkAndRenewLeases(c *macservice.Client) { | ||
log.Printf("Renewing leases...") | ||
|
||
resp, err := c.Find(macservice.FindRequest{ | ||
VMResourceNamespace: macservice.Namespace{ | ||
CustomerName: "golang", | ||
}, | ||
}) | ||
if err != nil { | ||
log.Printf("Error finding leases: %v", err) | ||
return | ||
} | ||
|
||
if len(resp.Instances) == 0 { | ||
log.Printf("No leases found") | ||
return | ||
} | ||
|
||
for _, i := range resp.Instances { | ||
log.Printf("Renewing lease ID: %s; currently expires: %v...", i.Lease.LeaseID, i.Lease.Expires) | ||
|
||
rr, err := c.Renew(macservice.RenewRequest{ | ||
LeaseID: i.Lease.LeaseID, | ||
Duration: renewDuration, | ||
}) | ||
if err == nil { | ||
// Extra spaces to make fields line up with the message above. | ||
log.Printf("Renewed lease ID: %s; now expires: %v", i.Lease.LeaseID, rr.Expires) | ||
} else { | ||
log.Printf("Error renewing lease ID: %s: %v", i.Lease.LeaseID, err) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
# license that can be found in the LICENSE file. | ||
|
||
FROM golang:1.20-bookworm AS build | ||
LABEL maintainer "[email protected]" | ||
LABEL maintainer="[email protected]" | ||
|
||
RUN mkdir /gocache | ||
ENV GOCACHE /gocache | ||
|
@@ -27,7 +27,7 @@ COPY . /go/src/golang.org/x/build/ | |
RUN go install golang.org/x/build/cmd/pubsubhelper | ||
|
||
FROM debian:bookworm | ||
LABEL maintainer "[email protected]" | ||
LABEL maintainer="[email protected]" | ||
|
||
# netbase and ca-certificates are needed for dialing TLS. | ||
# The rest are useful for debugging if somebody needs to exec into the container. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copyright 2023 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// Package macservice defines the client API for MacService. | ||
package macservice | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
) | ||
|
||
const baseURL = "https://macservice-pa.googleapis.com/v1alpha1/" | ||
|
||
// Client is a MacService client. | ||
type Client struct { | ||
apiKey string | ||
|
||
client *http.Client | ||
} | ||
|
||
// NewClient creates a MacService client, authenticated with the provided API | ||
// key. | ||
func NewClient(apiKey string) *Client { | ||
return &Client{ | ||
apiKey: apiKey, | ||
client: http.DefaultClient, | ||
} | ||
} | ||
|
||
func (c *Client) do(method, endpoint string, input, output any) error { | ||
var buf bytes.Buffer | ||
enc := json.NewEncoder(&buf) | ||
if err := enc.Encode(input); err != nil { | ||
return fmt.Errorf("error encoding request: %w", err) | ||
} | ||
|
||
req, err := http.NewRequest(method, baseURL+endpoint, &buf) | ||
if err != nil { | ||
return fmt.Errorf("error building request: %w", err) | ||
} | ||
req.Header.Add("Content-Type", "application/json") | ||
req.Header.Add("x-goog-api-key", c.apiKey) | ||
|
||
resp, err := c.client.Do(req) | ||
if err != nil { | ||
return fmt.Errorf("error sending request: %w", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return fmt.Errorf("error reading response body: %w", err) | ||
} | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
return fmt.Errorf("response error %s: %s", resp.Status, body) | ||
} | ||
|
||
if json.Unmarshal(body, output); err != nil { | ||
return fmt.Errorf("error decoding response: %w; body: %s", err, body) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Renew updates the expiration time of a lease. Note that | ||
// RenewRequest.Duration is the lease duration from now, not from the current | ||
// lease expiration time. | ||
func (c *Client) Renew(req RenewRequest) (RenewResponse, error) { | ||
var resp RenewResponse | ||
if err := c.do("POST", "leases:renew", req, &resp); err != nil { | ||
return RenewResponse{}, fmt.Errorf("error sending request: %w", err) | ||
} | ||
return resp, nil | ||
} | ||
|
||
// Find searches for leases. | ||
func (c *Client) Find(req FindRequest) (FindResponse, error) { | ||
var resp FindResponse | ||
if err := c.do("POST", "leases:find", req, &resp); err != nil { | ||
return FindResponse{}, fmt.Errorf("error sending request: %w", err) | ||
} | ||
return resp, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Copyright 2023 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package macservice | ||
|
||
import ( | ||
"time" | ||
) | ||
|
||
// These are minimal definitions. Many fields have been omitted since we don't | ||
// need them yet. | ||
|
||
type RenewRequest struct { | ||
LeaseID string `json:"leaseId"` | ||
|
||
// Duration is ultimately a Duration protobuf message. | ||
// | ||
// https://pkg.go.dev/google.golang.org/[email protected]/types/known/durationpb#hdr-JSON_Mapping: | ||
// "In JSON format, the Duration type is encoded as a string rather | ||
// than an object, where the string ends in the suffix "s" (indicating | ||
// seconds) and is preceded by the number of seconds, with nanoseconds | ||
// expressed as fractional seconds." | ||
Duration string `json:"duration"` | ||
} | ||
|
||
type RenewResponse struct { | ||
Expires time.Time `json:"expires"` | ||
} | ||
|
||
type FindRequest struct { | ||
VMResourceNamespace Namespace `json:"vmResourceNamespace"` | ||
} | ||
|
||
type FindResponse struct { | ||
Instances []Instance `json:"instances"` | ||
} | ||
|
||
type Namespace struct { | ||
CustomerName string `json:"customerName"` | ||
ProjectName string `json:"projectName"` | ||
SubCustomerName string `json:"subCustomerName"` | ||
} | ||
|
||
type Instance struct { | ||
Lease Lease `json:"lease"` | ||
} | ||
|
||
type Lease struct { | ||
LeaseID string `json:"leaseId"` | ||
|
||
Expires time.Time `json:"expires"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters