Only ever run one scan at the same time

Also use a logging library
This commit is contained in:
2020-11-10 20:14:43 +01:00
parent e8afa72dfb
commit 28c9f61569
9 changed files with 155 additions and 72 deletions

View File

@ -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
View File

@ -0,0 +1,5 @@
package main
const (
ENV_ADDR = "LOOKBUILDING_ADDR"
)

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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 {

View File

@ -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)

View File

@ -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
View 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")
}