mirror of
https://github.com/imjasonh/kontain.me
synced 2026-07-18 14:47:48 +00:00
add cmd/mirror, revert cmd/api changes
This commit is contained in:
parent
57955884c2
commit
340ca89f5a
9 changed files with 256 additions and 31 deletions
|
|
@ -26,7 +26,6 @@ import (
|
|||
"github.com/google/uuid"
|
||||
"github.com/imjasonh/kontain.me/pkg/run"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
gcb "google.golang.org/api/cloudbuild/v1"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
|
@ -283,32 +282,11 @@ func (s *server) fetchAndBuild(src, tok string, req *gcb.Build) error {
|
|||
s.error.Printf("Closing GCS log: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
ts, err := google.DefaultTokenSource(context.Background(), "https://www.googleapis.com/auth/cloud-platform")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("google.DefaultTokenSource: %v", err)
|
||||
}
|
||||
tok, err := ts.Token()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("credentials.Token: %v", err)
|
||||
}
|
||||
|
||||
for _, cmd := range []string{
|
||||
fmt.Sprintf("mkdir -p /tmp/layers"),
|
||||
fmt.Sprintf("chown -R %d:%d %s", os.Geteuid(), os.Getgid(), src),
|
||||
fmt.Sprintf("chown -R %d:%d /tmp/layers", os.Geteuid(), os.Getgid()),
|
||||
fmt.Sprintf("curl -fsSL %s | tar xz -C %s", source, src),
|
||||
fmt.Sprintf(`
|
||||
mkdir -p ~/.docker/ && cat > ~/.docker/config.json << EOF
|
||||
{
|
||||
"auths": {
|
||||
"gcr.io": {
|
||||
"username": "oauth2accesstoken",
|
||||
"password": "%s"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF`, tok.AccessToken),
|
||||
fmt.Sprintf("wget -qO- %s | tar xz -C %s", source, src),
|
||||
fmt.Sprintf("/lifecycle/detector -app=%s -group=/tmp/layers/group.toml -plan=/tmp/layers/plan.toml", src),
|
||||
fmt.Sprintf("/lifecycle/analyzer -layers=/tmp/layers -helpers=false -group=/tmp/layers/group.toml %s", image),
|
||||
fmt.Sprintf("/lifecycle/builder -layers=/tmp/layers -app=%s -group=/tmp/layers/group.toml -plan=/tmp/layers/plan.toml", src),
|
||||
|
|
|
|||
|
|
@ -53,6 +53,18 @@ serves an image fetched from source on GitHub and built using <a
|
|||
<p>The image tag can be a commit, branch or tag. The default,
|
||||
<code>:latest</code>, pulls the master branch.</p>
|
||||
|
||||
<h1><code>mirror.kontain.me</code></h1>
|
||||
|
||||
<p><code>docker pull mirror.kontain.me/[image]</code> will pull the an image
|
||||
(if it can) and cache the manifest and layers. Subsequent pulls will, if
|
||||
possible, serve from the cache.</p>
|
||||
|
||||
<p>This acts as a simple <a
|
||||
href="https://docs.docker.com/registry/recipes/mirror/">registry
|
||||
mirror</a> which can reduce the number of pulls from the original
|
||||
registry, in case they impose request limits or exorbitant bandwidth costs or
|
||||
latencies.</p>
|
||||
|
||||
<h1>Caveats</h1>
|
||||
|
||||
<p>The registry does not accept pushes of any kind. This is a silly hack and if
|
||||
|
|
|
|||
106
cmd/mirror/main.go
Normal file
106
cmd/mirror/main.go
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright 2020 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 (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/imjasonh/kontain.me/pkg/serve"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.Handle("/v2/", &server{
|
||||
info: log.New(os.Stdout, "I ", log.Ldate|log.Ltime|log.Lshortfile),
|
||||
error: log.New(os.Stderr, "E ", log.Ldate|log.Ltime|log.Lshortfile),
|
||||
})
|
||||
|
||||
log.Println("Starting...")
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8080"
|
||||
log.Printf("Defaulting to port %s", port)
|
||||
}
|
||||
log.Printf("Listening on port %s", port)
|
||||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
|
||||
}
|
||||
|
||||
type server struct {
|
||||
info, error *log.Logger
|
||||
}
|
||||
|
||||
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
s.info.Println("handler:", r.Method, r.URL)
|
||||
path := strings.TrimPrefix(r.URL.String(), "/v2/")
|
||||
|
||||
switch {
|
||||
case path == "":
|
||||
// API Version check.
|
||||
w.Header().Set("Docker-Distribution-API-Version", "registry/2.0")
|
||||
return
|
||||
case strings.Contains(path, "/blobs/"),
|
||||
strings.Contains(path, "/manifests/sha256:"):
|
||||
// Extract requested blob digest and redirect to serve it from GCS.
|
||||
// If it doesn't exist, this will return 404.
|
||||
parts := strings.Split(r.URL.Path, "/")
|
||||
digest := parts[len(parts)-1]
|
||||
serve.Blob(w, r, digest)
|
||||
case strings.Contains(path, "/manifests/"):
|
||||
s.serveMirrorManifest(w, r)
|
||||
default:
|
||||
serve.Error(w, serve.ErrNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
// mirror.kontain.me/ubuntu -> mirror ubuntu and serve
|
||||
func (s *server) serveMirrorManifest(w http.ResponseWriter, r *http.Request) {
|
||||
path := strings.TrimPrefix(r.URL.Path, "/v2/")
|
||||
parts := strings.Split(path, "/")
|
||||
ref, err := name.ParseReference(strings.Join(parts[:len(parts)-2], "/"))
|
||||
if err != nil {
|
||||
s.error.Printf("ERROR (ParseReference): %v", err)
|
||||
serve.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the original image's digest, and check if we have that manifest
|
||||
// blob.
|
||||
d, err := remote.Head(ref)
|
||||
if err != nil {
|
||||
s.error.Printf("ERROR (remote.Head): %v", err)
|
||||
serve.Error(w, err)
|
||||
return
|
||||
}
|
||||
if err := serve.BlobExists(d.Digest); err != nil {
|
||||
// Blob doesn't exist yet. Try to get the image manifest+layers
|
||||
// and cache them.
|
||||
img, err := remote.Image(ref)
|
||||
if err != nil {
|
||||
s.error.Printf("ERROR (remote.Image): %v", err)
|
||||
serve.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
serve.Manifest(w, r, img)
|
||||
} else {
|
||||
serve.Blob(w, r, d.Digest.String())
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue