From 30253d34cf22d5dca6af18b84bbf5d831d83e1c9 Mon Sep 17 00:00:00 2001 From: Mark van Holsteijn Date: Sun, 7 Nov 2021 21:41:51 +0100 Subject: [PATCH] added serve option --- committer.go | 18 ++++---- main.go | 56 ++++++++++++++---------- ref/ref.go | 2 +- ref/resolve_test.go | 2 +- ref/update_test.go | 6 +-- serve.go | 104 ++++++++++++++++++++++++++++++++++++++++++++ tag/tag.go | 13 +++--- 7 files changed, 159 insertions(+), 42 deletions(-) create mode 100644 serve.go diff --git a/committer.go b/committer.go index 4c29637..dc1615a 100644 --- a/committer.go +++ b/committer.go @@ -2,7 +2,9 @@ package main import ( "bytes" + "fmt" "gopkg.in/src-d/go-git.v4" + "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/object" "io" "log" @@ -10,16 +12,16 @@ import ( "time" ) -func (c *Cru) Commit() error { +func (c *Cru) Commit() (hash plumbing.Hash, err error) { if c.CommitMsg == "" { - return nil + return plumbing.ZeroHash, fmt.Errorf("a commit message is required") } for _, path := range c.updatedFiles { - _, err := c.workTree.Add(c.RelPath(path)) + _, err = c.workTree.Add(c.RelPath(path)) if err != nil { - return err + return plumbing.ZeroHash, err } if c.Verbose { @@ -28,7 +30,7 @@ func (c *Cru) Commit() error { } if !c.DryRun { - hash, err := c.workTree.Commit(c.CommitMsg, &git.CommitOptions{ + hash, err = c.workTree.Commit(c.CommitMsg, &git.CommitOptions{ Author: &object.Signature{ Name: "cru", Email: "cru@binx.io", @@ -38,15 +40,15 @@ func (c *Cru) Commit() error { if err != nil { log.Printf("ERROR: failed to commit changes, %s\n", err) c.workTree.Reset(nil) - return err } if c.Verbose { log.Printf("INFO: changes committed with %s", hash.String()[0:7]) } + } else { + hash = plumbing.ZeroHash } - - return nil + return } func (c *Cru) Push() error { diff --git a/main.go b/main.go index cf949d6..8017e1b 100644 --- a/main.go +++ b/main.go @@ -20,6 +20,8 @@ type Cru struct { Path []string List bool Update bool + Serve bool + Port string Bump bool NoFilename bool DryRun bool @@ -119,56 +121,57 @@ func Update(c *Cru, filename string) error { return nil } -func (cru *Cru) ConnectToRepository() { - if cru.Url != "" { +func (c *Cru) ConnectToRepository() error { + if c.Url != "" { var progressReporter io.Writer = os.Stderr - if !cru.Verbose { + if !c.Verbose { progressReporter = &bytes.Buffer{} } - repository, err := Clone(cru.Url, progressReporter) + repository, err := Clone(c.Url, progressReporter) if err != nil { - log.Fatal(err) + return err } - cru.repository = repository + c.repository = repository wt, err := repository.Worktree() if err != nil { - log.Fatal(err) + return err } - cru.workTree = wt - if cru.Branch != "" { + c.workTree = wt + if c.Branch != "" { var branch *plumbing.Reference if branches, err := repository.Branches(); err == nil { branches.ForEach(func(ref *plumbing.Reference) error { - if ref.Name().Short() == cru.Branch { + if ref.Name().Short() == c.Branch { branch = ref } return nil }) } if err != nil { - log.Fatal(err) + return err } if branch == nil { - log.Fatalf("ERROR: branch %s not found", cru.Branch) + return fmt.Errorf("ERROR: branch %s not found", c.Branch) } err = wt.Checkout(&git.CheckoutOptions{Branch: branch.Name()}) if err != nil { - log.Fatal(err) + return err } } - cru.filesystem = &wt.Filesystem - cru.cwd = "/" + c.filesystem = &wt.Filesystem + c.cwd = "/" } else { cwd, err := filepath.Abs(".") if err != nil { - log.Fatal(err) + return err } - cru.cwd = cwd + c.cwd = cwd fs := osfs.New("/") - cru.filesystem = &fs + c.filesystem = &fs } + return nil } func main() { @@ -177,6 +180,7 @@ func main() { Usage: cru list [--verbose] [--no-filename] [--repository=URL [--branch=BRANCH]] [PATH] ... cru update [--verbose] [--dry-run] [(--resolve-digest|--resolve-tag)] [--repository=URL [--branch=BRANCH] [--commit=MESSAGE]] (--all | --image-reference=REFERENCE ...) [PATH] ... + cru serve [--verbose] [--dry-run] [--port=PORT] --repository=URL --branch=BRANCH [PATH] ... Options: --no-filename do not print the filename. @@ -189,7 +193,7 @@ Options: --commit=MESSAGE commit the changes with the specified message. --repository=URL to read and/or update. --branch=BRANCH to update. - +--port=PORT to listen on, defaults to 8080 or PORT environment variable. ` cru := Cru{} @@ -202,10 +206,18 @@ Options: log.Fatal(err) } - cru.ConnectToRepository() + if err = cru.ConnectToRepository(); err != nil { + log.Fatal(err) + } + cru.AssertPathsExists() cru.imageRefs = make(ref.ContainerImageReferences, 0) - cru.AssertPathsExists() + if cru.Serve { + if cru.Url == "" { + log.Fatalf("cru as a service requires an git url.") + } + cru.ListenAndServe() + } if cru.All { if cru.Verbose { @@ -260,7 +272,7 @@ Options: if len(cru.updatedFiles) > 0 { log.Printf("INFO: updated a total of %d files", len(cru.updatedFiles)) if cru.CommitMsg != "" { - if err = cru.Commit(); err != nil { + if _, err = cru.Commit(); err != nil { log.Fatal(err) } if !IsLocalEndpoint(cru.Url) { diff --git a/ref/ref.go b/ref/ref.go index 1fc5580..1182bad 100644 --- a/ref/ref.go +++ b/ref/ref.go @@ -226,7 +226,7 @@ func (a ContainerImageReferences) ResolveTag() (ContainerImageReferences, error) for _, tag := range tags { if tag != r.Tag { log.Printf("resolving repository %s tag '%s' to '%s'\n", r.Name, r.Tag, tag) - result = append(result, ContainerImageReference{Tag: tag, Name:r.Name}) + result = append(result, ContainerImageReference{Tag: tag, Name: r.Name}) } } } diff --git a/ref/resolve_test.go b/ref/resolve_test.go index f89ce08..e089216 100644 --- a/ref/resolve_test.go +++ b/ref/resolve_test.go @@ -68,7 +68,7 @@ func TestImageResolves(t *testing.T) { func TestFindAlternateTags(t *testing.T) { latest := MustNewContainerImageReference("gcr.io/binx-io-public/paas-monitor:latest") - tags, err :=latest.FindAlternateTags() + tags, err := latest.FindAlternateTags() if err != nil { t.Fatal(err) } diff --git a/ref/update_test.go b/ref/update_test.go index 97650ab..3b695b6 100644 --- a/ref/update_test.go +++ b/ref/update_test.go @@ -49,7 +49,7 @@ does that work? references := []ContainerImageReference{*MustNewContainerImageReference(`gcr.io/binx-io-public/paas-monitor:v1.0.0`), *MustNewContainerImageReference(`mvanholsteijn/paas-monitor:3.1.0`)} - result, updated := UpdateReferences(input, references,"myfile.txt", true) + result, updated := UpdateReferences(input, references, "myfile.txt", true) if !updated { t.Errorf("expected the references to be updated\n") } @@ -100,11 +100,11 @@ resource "google_cloud_run_service" "app" { } `) ref, _ := NewContainerImageReference(`gcr.io/binx-io-public/paas-monitor:v0.3.2`) - result, updated := UpdateReference(input, *ref,"myfile.txt", true) + result, updated := UpdateReference(input, *ref, "myfile.txt", true) if !updated { t.Errorf("expected the reference to be updated\n") } if bytes.Compare(expect, result) != 0 { t.Errorf("expected %s, got %s\n", string(expect), string(result)) } -} \ No newline at end of file +} diff --git a/serve.go b/serve.go new file mode 100644 index 0000000..fbdc28c --- /dev/null +++ b/serve.go @@ -0,0 +1,104 @@ +package main + +import ( + "encoding/json" + "github.com/binxio/cru/ref" + "gopkg.in/src-d/go-git.v4/plumbing" + "log" + "net/http" + "os" +) + +type ContainerReferenceUpdateRequest struct { + CommitMessage string `json:"commit-message"` + ImageReferences []string `json:"image-references"` +} + +type ContainerReferenceUpdateResponse struct { + Files []string `json:"files,omitempty"` + Hash string `json:"commit-sha,omitempty"` +} + +func (c Cru) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + var hash plumbing.Hash + var request ContainerReferenceUpdateRequest + + err := json.NewDecoder(r.Body).Decode(&request) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + c.CommitMsg = request.CommitMessage + if c.CommitMsg == "" { + http.Error(w, "commit message is empty", http.StatusBadRequest) + return + } + + c.imageRefs = make(ref.ContainerImageReferences, 0) + for _, r := range request.ImageReferences { + r, err := ref.NewContainerImageReference(r) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + c.imageRefs = append(c.imageRefs, *r) + } + if len(c.imageRefs) == 0 { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + if err = c.ConnectToRepository(); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if err = c.Walk(Update); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if len(c.updatedFiles) > 0 { + log.Printf("INFO: updated a total of %d files", len(c.updatedFiles)) + if hash, err = c.Commit(); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err = c.Push(); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } else { + log.Println("INFO: no files were updated by cru") + c.updatedFiles = make([]string, 0) + } + if body, err := json.Marshal(ContainerReferenceUpdateResponse{ + c.updatedFiles, hash.String()}); err == nil { + w.Header().Set("Content-Type", "application/json") + w.Write(body) + return + } else { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (c *Cru) ListenAndServe() { + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + + if c.Port == "" { + c.Port = os.Getenv("PORT") + } + if c.Port == "" { + c.Port = "8080" + } + + log.Printf("Listening on port %s", c.Port) + if err := http.ListenAndServe(":"+c.Port, c); err != nil { + log.Fatal(err) + } +} diff --git a/tag/tag.go b/tag/tag.go index b7b867b..c5c5e3c 100644 --- a/tag/tag.go +++ b/tag/tag.go @@ -1,12 +1,12 @@ package tag import ( -"fmt" -"github.com/google/go-containerregistry/pkg/crane" -"github.com/google/go-containerregistry/pkg/name" -"regexp" -"sort" -"strconv" + "fmt" + "github.com/google/go-containerregistry/pkg/crane" + "github.com/google/go-containerregistry/pkg/name" + "regexp" + "sort" + "strconv" ) type Tag struct { @@ -142,4 +142,3 @@ func MakeTagCategories(tags TagList) TagCategories { } return result } -