From 8855e0a735c91a7c490a71b5e08c97ab30da8627 Mon Sep 17 00:00:00 2001 From: Christopher Mancini Date: Sun, 23 Feb 2020 11:45:22 -0500 Subject: [PATCH] initial setup --- Makefile | 3 ++ github/github.go | 88 ++++++++++++++++++++++++++++++++++++++++++ go.mod | 11 ++++++ go.sum | 18 +++++++++ log/clean.go | 29 ++++++++++++++ log/dir.go | 7 ++++ log/dir_windows.go | 5 +++ log/functional_test.go | 28 ++++++++++++++ log/log.go | 65 +++++++++++++++++++++++++++++++ log/tmp/.gitkeep | 0 resource/resource.go | 58 ++++++++++++++++++++++++++++ 11 files changed, 312 insertions(+) create mode 100644 Makefile create mode 100644 github/github.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 log/clean.go create mode 100644 log/dir.go create mode 100644 log/dir_windows.go create mode 100644 log/functional_test.go create mode 100644 log/log.go create mode 100644 log/tmp/.gitkeep create mode 100644 resource/resource.go diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e5dfbc5 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +mkfile := $(abspath $(lastword $(MAKEFILE_LIST))) +dir := $(dir $(ci_mkfile)) + diff --git a/github/github.go b/github/github.go new file mode 100644 index 0000000..9b18eb8 --- /dev/null +++ b/github/github.go @@ -0,0 +1,88 @@ +package github + +import ( + "context" + "crypto/tls" + "fmt" + "log" + "net/http" + "net/url" + + "github.com/google/go-github/github" + "github.com/shurcooL/githubv4" + "golang.org/x/oauth2" +) + +// GithubClient for handling requests to the Github V3 and V4 APIs. +type GithubClient struct { + V4 *githubv4.Client + Repository string + Owner string +} + +// NewGithubClient ... +func NewGithubClient(s *Source) (*GithubClient, error) { + owner, repository, err := parseRepository(s.Repository) + if err != nil { + return nil, err + } + + ctx := context.TODO() + httpClient := http.Client{} + + // Skip SSL verification for self-signed certificates + // source: https://github.com/google/go-github/pull/598#issuecomment-333039238 + if s.SkipSSLVerification { + log.Println("disabling SSL verification") + httpClient.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + ctx = context.WithValue(ctx, oauth2.HTTPClient, &httpClient) + } + + client := oauth2.NewClient(ctx, oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: s.AccessToken}, + )) + + if s.PreviewSchema { + log.Println("attaching preview schema transport to client") + client.Transport = &PreviewSchemaTransport{ + oauthTransport: client.Transport, + } + } + + var v3 *github.Client + if s.V3Endpoint != "" { + endpoint, err := url.Parse(s.V3Endpoint) + if err != nil { + return nil, fmt.Errorf("failed to parse v3 endpoint: %s", err) + } + v3, err = github.NewEnterpriseClient(endpoint.String(), endpoint.String(), client) + if err != nil { + return nil, err + } + } else { + v3 = github.NewClient(client) + } + + var v4 *githubv4.Client + if s.V4Endpoint != "" { + endpoint, err := url.Parse(s.V4Endpoint) + if err != nil { + return nil, fmt.Errorf("failed to parse v4 endpoint: %s", err) + } + v4 = githubv4.NewEnterpriseClient(endpoint.String(), client) + if err != nil { + return nil, err + } + } else { + v4 = githubv4.NewClient(client) + } + + return &GithubClient{ + V3: v3, + V4: v4, + Owner: owner, + Repository: repository, + }, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3a05d45 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/digitalocean/concourse-resource-library + +go 1.13 + +require ( + github.com/google/go-github v17.0.0+incompatible + github.com/google/go-querystring v1.0.0 // indirect + github.com/shurcooL/githubv4 v0.0.0-20191127044304-8f68eb5628d0 + github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f // indirect + golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..34baf82 --- /dev/null +++ b/go.sum @@ -0,0 +1,18 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/shurcooL/githubv4 v0.0.0-20191127044304-8f68eb5628d0 h1:T9uus1QvcPgeLShS30YOnnzk3r9Vvygp45muhlrufgY= +github.com/shurcooL/githubv4 v0.0.0-20191127044304-8f68eb5628d0/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo= +github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f h1:tygelZueB1EtXkPI6mQ4o9DQ0+FKW41hTbunoXZCTqk= +github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/log/clean.go b/log/clean.go new file mode 100644 index 0000000..a419e0a --- /dev/null +++ b/log/clean.go @@ -0,0 +1,29 @@ +package log + +import ( + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func cleanStaleLogs(dir string) error { + tmpfiles, err := ioutil.ReadDir(dir) + if err != nil { + return err + } + + for _, file := range tmpfiles { + if strings.HasPrefix(file.Name(), "resource-") && file.Mode().IsRegular() { + if time.Now().Sub(file.ModTime()) > 72*time.Hour { + err = os.Remove(filepath.Join(dir, file.Name())) + if err != nil { + return err + } + } + } + } + + return nil +} diff --git a/log/dir.go b/log/dir.go new file mode 100644 index 0000000..5bbc089 --- /dev/null +++ b/log/dir.go @@ -0,0 +1,7 @@ +// +build !windows + +package log + +const ( + defaultDir = "/tmp" +) diff --git a/log/dir_windows.go b/log/dir_windows.go new file mode 100644 index 0000000..a8a3f18 --- /dev/null +++ b/log/dir_windows.go @@ -0,0 +1,5 @@ +package log + +const ( + defaultDir = "." +) diff --git a/log/functional_test.go b/log/functional_test.go new file mode 100644 index 0000000..7135700 --- /dev/null +++ b/log/functional_test.go @@ -0,0 +1,28 @@ +package log_test + +import ( + "log" + "os" + "testing" + + rlog "github.com/digitalocean/concourse-resource-library/log" +) + +func TestLog(t *testing.T) { + os.Setenv("LOG_DIRECTORY", "./tmp") + + input := rlog.WriteStdin() + defer rlog.Close() + + // TODO: write stdin + log.Println(input) + + // TODO: read file contents + //f, err := os.OpenFile(fmt.Sprintf(rlog.LogFilePattern, dir, time.Now().Format("2006-01-02")), os.O_RD, 0666) + //if err != nil { + // log.Fatalf("error opening file: %v", err) + //} + + // TODO: assert file contents + +} diff --git a/log/log.go b/log/log.go new file mode 100644 index 0000000..382ca94 --- /dev/null +++ b/log/log.go @@ -0,0 +1,65 @@ +package log + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "time" +) + +const ( + // LogFilePattern string format pattern for the log file + LogFilePattern = "%s/resource-%s.log" +) + +var ( + dir string + f *os.File + debug bool +) + +func init() { + dir = defaultDir + if os.Getenv("LOG_DIRECTORY") != "" { + dir = os.Getenv("LOG_DIRECTORY") + } + + if os.Getenv("LOG_DEBUG") != "" { + debug = true + } + + cleanStaleLogs(dir) + + f, err := os.OpenFile(fmt.Sprintf(LogFilePattern, dir, time.Now().Format("2006-01-02")), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + log.Fatalf("error opening file: %v", err) + } + + log.SetFlags(log.Ldate | log.Ltime | log.Llongfile) + log.SetOutput(f) +} + +// WriteStdin will write the contents of stdin to the log, then return the contents if env var `LOG_DEBUG` != "" +func WriteStdin() []byte { + input, err := ioutil.ReadAll(os.Stdin) + if err != nil { + log.Fatal(err) + } + + if debug { + Write(string(input)) + } + + return input +} + +// Write writes a message to a log file in `/tmp` +func Write(msg string) { + log.Println(msg) +} + +// Close the *os.File connection for the logger +func Close() { + f.Close() +} diff --git a/log/tmp/.gitkeep b/log/tmp/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/resource/resource.go b/resource/resource.go new file mode 100644 index 0000000..5d1d713 --- /dev/null +++ b/resource/resource.go @@ -0,0 +1,58 @@ +package resource + +// Version communicated with Concourse. +type Version interface { +} + +// Source is the configuration for the resource +type Source interface { +} + +// Parameters is the configuration for a resource step +type Parameters interface { +} + +// CheckRequest is the data struct received from Concoruse by the resource check operation +type CheckRequest struct { + Source Source `json:"source"` + Version Version `json:"version"` +} + +// GetRequest is the data struct received from Concoruse by the resource get operation +type GetRequest struct { + Source Source `json:"source"` + Params Parameters `json:"params"` + Version Version `json:"version"` +} + +// PutRequest is the data struct received from Concoruse by the resource put operation +type PutRequest struct { + Source Source `json:"source"` + Params Parameters `json:"params"` +} + +// Metadata output from get/put steps. +type Metadata []*MetadataField + +// Add a MetadataField to the Metadata. +func (m *Metadata) Add(name, value string) { + *m = append(*m, &MetadataField{Name: name, Value: value}) +} + +// MetadataField ... +type MetadataField struct { + Name string `json:"name"` + Value string `json:"value"` +} + +// CheckResponse is the data struct returned to Concourse by the resource check operation +type CheckResponse struct { + Version Version `json:"version"` + Metadata Metadata `json:"metadata,omitempty"` +} + +// GetPutResponse is the data struct returned to Concourse by the resource get & put operations +type GetPutResponse struct { + Version Version `json:"version"` + Metadata Metadata `json:"metadata,omitempty"` +}