Skip to content

Commit

Permalink
Add support for AR, allow running locally
Browse files Browse the repository at this point in the history
  • Loading branch information
sethvargo committed Oct 29, 2020
1 parent b0f5317 commit 183b80d
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 106 deletions.
11 changes: 5 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.

FROM golang:1.14 AS builder
FROM golang:1.15 AS builder

ARG SERVICE

RUN apt-get -qq update && apt-get -yqq install upx

Expand All @@ -28,14 +30,11 @@ RUN go build \
-a \
-trimpath \
-ldflags "-s -w -extldflags '-static'" \
-installsuffix cgo \
-tags netgo \
-mod vendor \
-tags 'osusergo netgo static_build' \
-o /bin/gcrcleaner \
.
./cmd/${SERVICE}

RUN strip /bin/gcrcleaner

RUN upx -q -9 /bin/gcrcleaner


Expand Down
26 changes: 19 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# GCR Cleaner

GCR Cleaner deletes untagged images in Google Container Registry. This can help
reduce costs and keep your container images list in order.
GCR Cleaner deletes untagged images in Google Cloud [Container
Registry][container-registry] or Google Cloud [Artifact
Registry][artifact-registry]. This can help reduce costs and keep your container
images list in order.

GCR Cleaner is designed to be deployed as a [Cloud Run][cloud-run] service and
invoked periodically via [Cloud Scheduler][cloud-scheduler].
Expand Down Expand Up @@ -59,7 +61,7 @@ invoked periodically via [Cloud Scheduler][cloud-scheduler].
--project ${PROJECT_ID} \
--platform "managed" \
--service-account "gcr-cleaner@${PROJECT_ID}.iam.gserviceaccount.com" \
--image "gcr.io/gcr-cleaner/gcr-cleaner" \
--image "us-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner" \
--region "us-central1" \
--timeout "60s"
```
Expand Down Expand Up @@ -105,8 +107,9 @@ invoked periodically via [Cloud Scheduler][cloud-scheduler].

```sh
# Replace this with the full name of the repository for which you
# want to cleanup old references.
# want to cleanup old references, for example:
export REPO="gcr.io/${PROJECT_ID}/my-image"
export REPO="us-docker-pkg.dev/${PROJECT_ID}/my-repo/my-image"
```

```sh
Expand Down Expand Up @@ -157,6 +160,14 @@ The payload is expected to be JSON with the following fields:
- `keep` - If an integer is provided, it will always keep that minimum number
of images. Note that it will not consider images inside the `grace` duration.

## Running locally

In addition to the server, you can also run GCR Cleaner locally for one-off tasks using `cmd/gcr-cleaner-cli`:

```text
docker run -it us-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner-cli
```

## I just want the container!

You can build the container yourself using the included Dockerfile.
Expand All @@ -170,7 +181,6 @@ europe-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner
us-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner
```


## FAQ

**How do I clean up multiple Google Container Registry repos at once?**
Expand All @@ -193,11 +203,13 @@ service.
This library is licensed under Apache 2.0. Full license text is available in
[LICENSE](https://github.com/sethvargo/gcr-cleaner/tree/master/LICENSE).
[artifact-registry]: https://cloud.google.com/artifact-registry
[cloud-build]: https://cloud.google.com/build/
[cloud-pubsub]: https://cloud.google.com/pubsub/
[cloud-run]: https://cloud.google.com/run/
[cloud-scheduler]: https://cloud.google.com/scheduler/
[cloud-shell]: https://cloud.google.com/shell
[cloud-sdk]: https://cloud.google.com/sdk
[gcrgc.sh]: https://gist.github.com/ahmetb/7ce6d741bd5baa194a3fac6b1fec8bb7
[cloud-shell]: https://cloud.google.com/shell
[container-registry]: https://cloud.google.com/container-registry
[gcr-cleaner-godoc]: https://godoc.org/github.com/sethvargo/gcr-cleaner/pkg/gcrcleaner
[gcrgc.sh]: https://gist.github.com/ahmetb/7ce6d741bd5baa194a3fac6b1fec8bb7
86 changes: 0 additions & 86 deletions cache.go

This file was deleted.

38 changes: 34 additions & 4 deletions cloudbuild/cloudbuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,48 @@
# limitations under the License.

steps:
- id: 'build'
name: 'docker:18'
- id: 'build-cli'
name: 'docker:19'
args: [
'build',
'--build-arg', 'SERVICE=gcr-cleaner-cli',
'--tag', 'gcr.io/${PROJECT_ID}/gcr-cleaner-cli',
'--tag', 'asia-docker.pkg.dev/${PROJECT_ID}/gcr-cleaner/gcr-cleaner-cli',
'--tag', 'europe-docker.pkg.dev/${PROJECT_ID}/gcr-cleaner/gcr-cleaner-cli',
'--tag', 'us-docker.pkg.dev/${PROJECT_ID}/gcr-cleaner/gcr-cleaner-cli',
'.'
]
waitFor: ['-']

- id: 'build-server'
name: 'docker:19'
args: [
'build',
'--build-arg', 'SERVICE=gcr-cleaner-server',
'--tag', 'gcr.io/${PROJECT_ID}/gcr-cleaner',
'--tag', 'asia-docker.pkg.dev/${PROJECT_ID}/gcr-cleaner/gcr-cleaner',
'--tag', 'europe-docker.pkg.dev/${PROJECT_ID}/gcr-cleaner/gcr-cleaner',
'--tag', 'us-docker.pkg.dev/${PROJECT_ID}/gcr-cleaner/gcr-cleaner',
'.'
]
waitFor: ['-']

- id: 'push-cli'
name: 'docker:19'
entrypoint: '/bin/sh'
args:
- '-euo'
- 'pipefail'
- '-c'
- |-
docker push gcr.io/${PROJECT_ID}/gcr-cleaner-cli
docker push asia-docker.pkg.dev/${PROJECT_ID}/gcr-cleaner/gcr-cleaner-cli
docker push europe-docker.pkg.dev/${PROJECT_ID}/gcr-cleaner/gcr-cleaner-cli
docker push us-docker.pkg.dev/${PROJECT_ID}/gcr-cleaner/gcr-cleaner-cli
waitFor: ['build-cli']

- id: 'push'
name: 'docker:18'
- id: 'push-server'
name: 'docker:19'
entrypoint: '/bin/sh'
args:
- '-euo'
Expand All @@ -36,3 +65,4 @@ steps:
docker push asia-docker.pkg.dev/${PROJECT_ID}/gcr-cleaner/gcr-cleaner
docker push europe-docker.pkg.dev/${PROJECT_ID}/gcr-cleaner/gcr-cleaner
docker push us-docker.pkg.dev/${PROJECT_ID}/gcr-cleaner/gcr-cleaner
waitFor: ['build-server']
90 changes: 90 additions & 0 deletions cmd/gcr-cleaner-cli/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright 2019 The GCR Cleaner 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 main defines the CLI interface for GCR Cleaner.
package main

import (
"flag"
"fmt"
"os"
"runtime"
"time"

gcrauthn "github.com/google/go-containerregistry/pkg/authn"
gcrgoogle "github.com/google/go-containerregistry/pkg/v1/google"
"github.com/sethvargo/gcr-cleaner/pkg/gcrcleaner"
)

var (
stdout = os.Stdout
stderr = os.Stderr

tokenPtr = flag.String("token", os.Getenv("GCRCLEANER_TOKEN"), "Authentication token")
repoPtr = flag.String("repo", "", "Repository name")
gracePtr = flag.Duration("grace", 0, "Grace period")
allowTaggedPtr = flag.Bool("allow-tagged", false, "Delete tagged images")
keepPtr = flag.Int("keep", 0, "Minimum to keep")
)

func main() {
flag.Parse()

if err := realMain(); err != nil {
fmt.Fprintf(stderr, "%s\n", err)
os.Exit(1)
}
}

func realMain() error {
if *repoPtr == "" {
return fmt.Errorf("missing -repo")
}

// Try to find the "best" authentication.
var auther gcrauthn.Authenticator
if *tokenPtr != "" {
auther = &gcrauthn.Bearer{Token: *tokenPtr}
} else {
var err error
auther, err = gcrgoogle.NewEnvAuthenticator()
if err != nil {
return fmt.Errorf("failed to setup auther: %w", err)
}
}

concurrency := runtime.NumCPU()
cleaner, err := gcrcleaner.NewCleaner(auther, concurrency)
if err != nil {
return fmt.Errorf("failed to create cleaner: %w", err)
}

// Convert duration to a negative value, since we're about to "add" it to the
// since time.
sub := time.Duration(*gracePtr)
if *gracePtr > 0 {
sub = sub * -1
}
since := time.Now().UTC().Add(sub)

// Do the deletion.
fmt.Fprintf(stdout, "%s: deleting refs since %s\n", *repoPtr, since)
deleted, err := cleaner.Clean(*repoPtr, since, *allowTaggedPtr, *keepPtr)
if err != nil {
return err
}
fmt.Fprintf(stdout, "%s: successfully deleted %d refs", *repoPtr, len(deleted))

return nil
}
7 changes: 4 additions & 3 deletions main.go → cmd/gcr-cleaner-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package main defines the server interface for GCR Cleaner.
package main

import (
Expand All @@ -36,7 +37,7 @@ func main() {
if port == "" {
port = "8080"
}
addr := "0.0.0.0:" + port
addr := ":" + port

var auther gcrauthn.Authenticator
if token := os.Getenv("GCRCLEANER_TOKEN"); token != "" {
Expand All @@ -60,7 +61,7 @@ func main() {
log.Fatalf("failed to create server: %s", err)
}

cache := newTimerCache(30 * time.Minute)
cache := gcrcleaner.NewTimerCache(5 * time.Minute)

mux := http.NewServeMux()
mux.Handle("/http", cleanerServer.HTTPHandler())
Expand All @@ -73,7 +74,7 @@ func main() {

go func() {
log.Printf("server is listening on %s\n", port)
if err := server.ListenAndServe(); err != http.ErrServerClosed {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("server exited: %s", err)
}
}()
Expand Down
Loading

0 comments on commit 183b80d

Please sign in to comment.