Only ever run one scan at the same time
Also use a logging library
This commit is contained in:
@ -12,3 +12,7 @@ lookbuilding.mode = semver_major
|
||||
lookbuilding.mode = semver_minor
|
||||
lookbuilding.mode = semver_patch
|
||||
```
|
||||
|
||||
#### Environment variables:
|
||||
|
||||
- `LOOKBUILDING_ADDR` - set the http bind address, default "0.0.0.0:8000"
|
||||
5
constants.go
Normal file
5
constants.go
Normal file
@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
const (
|
||||
ENV_ADDR = "LOOKBUILDING_ADDR"
|
||||
)
|
||||
27
docker.go
27
docker.go
@ -43,6 +43,15 @@ func (lc LabeledContainer) SplitImageParts() (*string, string, *string) {
|
||||
return owner, repository, tag
|
||||
}
|
||||
|
||||
func (lc LabeledContainer) GetName() string {
|
||||
if len(lc.Container.Names) >= 0 {
|
||||
// trim prefixed "/"
|
||||
return lc.Container.Names[0][1:]
|
||||
} else {
|
||||
return lc.Container.ID[:10]
|
||||
}
|
||||
}
|
||||
|
||||
func combineImageParts(owner *string, repository string, tag *string) string {
|
||||
image := repository
|
||||
if owner != nil {
|
||||
@ -63,15 +72,15 @@ func getLabeledContainers(cli *client.Client) []LabeledContainer {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("scanning running container labels")
|
||||
Logger.Infof("scanning running container labels")
|
||||
for _, container := range containers {
|
||||
fmt.Printf("- %s %s\n", container.ID[:10], container.Image)
|
||||
Logger.Debugf("checking %s %s", container.ID[:10], container.Image)
|
||||
for k, v := range container.Labels {
|
||||
fmt.Printf(" - \"%s\": \"%s\"\n", k, v)
|
||||
Logger.Debugf(` - "%s": "%s"`, k, v)
|
||||
if k == versioningModeLabel {
|
||||
mode := ParseVersioningMode(v)
|
||||
if mode == nil {
|
||||
fmt.Printf("Failed to parse '%s' as a versioning mode\n", v)
|
||||
Logger.Errorf(`Failed to parse "%s" as a versioning mode`, v)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -95,7 +104,7 @@ func (lc LabeledContainer) UpdateTo(cli *client.Client, tag Tag) error {
|
||||
owner, repository, _ := lc.SplitImageParts()
|
||||
image := combineImageParts(owner, repository, &tag.Name)
|
||||
canonicalImage := fmt.Sprintf("docker.io/%s", image)
|
||||
fmt.Printf("Pulling image \"%s\"\n", canonicalImage)
|
||||
Logger.Infof(`pulling image "%s"`, canonicalImage)
|
||||
|
||||
//containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{})
|
||||
imageReader, err := cli.ImagePull(ctx, canonicalImage, types.ImagePullOptions{})
|
||||
@ -132,13 +141,13 @@ func (lc LabeledContainer) UpdateTo(cli *client.Client, tag Tag) error {
|
||||
hostConfig := oldContainer.HostConfig
|
||||
hostConfig.VolumesFrom = []string{tmpOldName}
|
||||
|
||||
fmt.Printf("Renaming container %s\n", lc.Container.ID)
|
||||
Logger.Infof(`renaming container %s`, lc.Container.ID)
|
||||
err = cli.ContainerRename(ctx, lc.Container.ID, tmpOldName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Creating new container\n")
|
||||
Logger.Infof("creating new container")
|
||||
new, err := cli.ContainerCreate(ctx, oldContainer.Config, hostConfig, &network.NetworkingConfig{
|
||||
EndpointsConfig: oldContainer.NetworkSettings.Networks,
|
||||
}, name)
|
||||
@ -147,13 +156,13 @@ func (lc LabeledContainer) UpdateTo(cli *client.Client, tag Tag) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Starting new container id: %s\n", new.ID)
|
||||
Logger.Infof("starting new container id: %s", new.ID)
|
||||
err = cli.ContainerStart(ctx, new.ID, types.ContainerStartOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Removing old container\n")
|
||||
Logger.Infof("removing old container")
|
||||
err = cli.ContainerRemove(ctx, oldContainer.ID, types.ContainerRemoveOptions{
|
||||
RemoveVolumes: false,
|
||||
RemoveLinks: false,
|
||||
|
||||
1
go.mod
1
go.mod
@ -16,4 +16,5 @@ require (
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 // indirect
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
|
||||
)
|
||||
|
||||
3
go.sum
3
go.sum
@ -142,7 +142,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
||||
72
main.go
72
main.go
@ -3,78 +3,32 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func checkForUpdates() {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
hub, err := anonymousClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
labeledContainers := getLabeledContainers(cli)
|
||||
fmt.Println()
|
||||
|
||||
for _, lc := range labeledContainers {
|
||||
owner, repository, tag := lc.SplitImageParts()
|
||||
|
||||
fmt.Printf("container image: %s\n", combineImageParts(owner, repository, nil))
|
||||
fmt.Printf(" versioning: %+v\n", lc.Mode.Label())
|
||||
fmt.Printf(" id: %s\n", lc.Container.ImageID)
|
||||
|
||||
if tag != nil {
|
||||
fmt.Printf(" current tag: %s\n", *tag)
|
||||
} else {
|
||||
fmt.Printf(" no current tag, skipping\n")
|
||||
continue
|
||||
}
|
||||
|
||||
repoTags, err := getDockerRepoTags(hub, owner, repository)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(" tags in registry:")
|
||||
for _, tag := range repoTags {
|
||||
fmt.Printf(" - \"%s\" %s\n", tag.Name, tag.Digest)
|
||||
svt := parseTagAsSemVer(tag.Name)
|
||||
if svt != nil {
|
||||
fmt.Printf(" semver: true\n")
|
||||
}
|
||||
}
|
||||
|
||||
shouldUpdateTo := lc.Mode.ShouldUpdate(*tag, repoTags)
|
||||
if shouldUpdateTo != nil {
|
||||
fmt.Printf(" updating to: %s\n", shouldUpdateTo.Name)
|
||||
err = lc.UpdateTo(cli, *shouldUpdateTo)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
fmt.Println(" no update available")
|
||||
}
|
||||
}
|
||||
fmt.Println("All done")
|
||||
}
|
||||
var (
|
||||
Logger logrus.Logger = *logrus.New()
|
||||
)
|
||||
|
||||
func main() {
|
||||
addr := "0.0.0.0:8000"
|
||||
addr, isPresent := os.LookupEnv(ENV_ADDR)
|
||||
if !isPresent {
|
||||
addr = "0.0.0.0:8000"
|
||||
}
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
go checkForUpdates()
|
||||
TriggerScan()
|
||||
fmt.Fprintf(w, "OK")
|
||||
})
|
||||
|
||||
fs := http.FileServer(http.Dir("static/"))
|
||||
http.Handle("/static/", http.StripPrefix("/static/", fs))
|
||||
|
||||
fmt.Printf("Listening on %s\n", addr)
|
||||
Logger.Infof(`listening on %s`, addr)
|
||||
|
||||
go Worker()
|
||||
|
||||
err := http.ListenAndServe(addr, nil)
|
||||
if err != nil {
|
||||
|
||||
11
registry.go
11
registry.go
@ -20,7 +20,14 @@ func anonymousClient() (*registry.Registry, error) {
|
||||
username := "" // anonymous
|
||||
password := "" // anonymous
|
||||
|
||||
return registry.New(url, username, password)
|
||||
registry, err := registry.New(url, username, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registry.Logf = Logger.Infof
|
||||
|
||||
return registry, nil
|
||||
}
|
||||
|
||||
func getDockerRepoTags(hub *registry.Registry, maybe_owner *string, repository string) ([]Tag, error) {
|
||||
@ -33,7 +40,7 @@ func getDockerRepoTags(hub *registry.Registry, maybe_owner *string, repository s
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := []Tag{}
|
||||
var out []Tag
|
||||
|
||||
for _, tag := range tags {
|
||||
digest, err := hub.ManifestDigest(repository, tag)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
//"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
@ -16,7 +15,7 @@ type SemVerTag struct {
|
||||
version semver.Version
|
||||
}
|
||||
|
||||
// Return a
|
||||
// Return a
|
||||
// Returns nil if the tag did not parse as semver
|
||||
func parseTagAsSemVer(tag string) *SemVerTag {
|
||||
var prefix string
|
||||
|
||||
101
worker.go
Normal file
101
worker.go
Normal file
@ -0,0 +1,101 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
var (
|
||||
triggerCh = make(chan struct{})
|
||||
)
|
||||
|
||||
func TriggerScan() {
|
||||
triggerCh <- struct{}{}
|
||||
}
|
||||
|
||||
func Worker() {
|
||||
Logger.Debugf("background worker starting")
|
||||
|
||||
responseCh := make(chan struct{})
|
||||
|
||||
workerRunning := false
|
||||
triggerWaiting := false
|
||||
|
||||
for true {
|
||||
select {
|
||||
case _ = <-triggerCh:
|
||||
if workerRunning {
|
||||
triggerWaiting = true
|
||||
} else {
|
||||
workerRunning = true
|
||||
go func() {
|
||||
checkAndDoUpdate()
|
||||
responseCh <- struct{}{}
|
||||
}()
|
||||
}
|
||||
case _ = <-responseCh:
|
||||
if triggerWaiting {
|
||||
triggerWaiting = false
|
||||
go func() {
|
||||
checkAndDoUpdate()
|
||||
responseCh <- struct{}{}
|
||||
}()
|
||||
} else {
|
||||
workerRunning = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkAndDoUpdate() {
|
||||
Logger.Infof("starting scan")
|
||||
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
hub, err := anonymousClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
labeledContainers := getLabeledContainers(cli)
|
||||
|
||||
Logger.Infof("found %d valid containers", len(labeledContainers))
|
||||
|
||||
for _, lc := range labeledContainers {
|
||||
owner, repository, tag := lc.SplitImageParts()
|
||||
name := lc.GetName()
|
||||
imageName := combineImageParts(owner, repository, nil)
|
||||
|
||||
if tag == nil {
|
||||
Logger.Errorf(`no tag specified for container "%s", ignoring`, name)
|
||||
continue
|
||||
}
|
||||
|
||||
Logger.Infof(`container "%s" image="%s" mode=%s tag="%s"`, name, imageName, lc.Mode.Label(), *tag)
|
||||
|
||||
repoTags, err := getDockerRepoTags(hub, owner, repository)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Logger.Infof(`tags in registry for "%s": %d`, name, len(repoTags))
|
||||
for _, tag := range repoTags {
|
||||
svt := parseTagAsSemVer(tag.Name)
|
||||
Logger.Infof(`tag_name="%s" semver=%t digest=%s`, tag.Name, svt != nil, tag.Digest)
|
||||
}
|
||||
|
||||
shouldUpdateTo := lc.Mode.ShouldUpdate(*tag, repoTags)
|
||||
if shouldUpdateTo != nil {
|
||||
Logger.Infof(`updating %s from %s to: %s`, name, *tag, shouldUpdateTo.Name)
|
||||
err = lc.UpdateTo(cli, *shouldUpdateTo)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
Logger.Infof("no update available for container %s", name)
|
||||
}
|
||||
}
|
||||
Logger.Infof("all done")
|
||||
}
|
||||
Reference in New Issue
Block a user