Skip to content

Commit

Permalink
cogito: add support for configuring Github API endpoint
Browse files Browse the repository at this point in the history
- adds support for Github Enterprise API endpoints
  • Loading branch information
aliculPix4D committed Oct 19, 2023
1 parent 3d091a4 commit cff0c2a
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 126 deletions.
3 changes: 1 addition & 2 deletions cmd/cogito/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/hashicorp/go-hclog"

"github.com/Pix4D/cogito/cogito"
"github.com/Pix4D/cogito/github"
"github.com/Pix4D/cogito/sets"
)

Expand Down Expand Up @@ -56,7 +55,7 @@ func mainErr(in io.Reader, out io.Writer, logOut io.Writer, args []string) error
case "in":
return cogito.Get(log, input, out, args[1:])
case "out":
putter := cogito.NewPutter(github.API, log)
putter := cogito.NewPutter(log)
return cogito.Put(input, out, args[1:], putter)
default:
return fmt.Errorf("cli wiring error; please report")
Expand Down
11 changes: 8 additions & 3 deletions cmd/cogito/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ func TestRunPutSuccess(t *testing.T) {
chatReply := googlechat.MessageReply{}
var gchatUrl *url.URL
googleChatSpy := testhelp.SpyHttpServer(&chatMsg, chatReply, &gchatUrl, http.StatusOK)
gitHubSpyDomain, err := url.Parse(gitHubSpy.URL)
if err != nil {
t.Fatalf("error parsing SpyHttpServer URL: %s", err)
}

in := bytes.NewReader(testhelp.ToJSON(t, cogito.PutRequest{
Source: cogito.Source{
Owner: "the-owner",
Expand All @@ -81,9 +86,9 @@ func TestRunPutSuccess(t *testing.T) {
var out bytes.Buffer
var logOut bytes.Buffer
inputDir := testhelp.MakeGitRepoFromTestdata(t, "../../cogito/testdata/one-repo/a-repo",
testhelp.HttpsRemote("the-owner", "the-repo"), "dummySHA", wantGitRef)
testhelp.HttpsRemote(gitHubSpyDomain.Host, "the-owner", "the-repo"), "dummySHA", wantGitRef)

err := mainErr(in, &out, &logOut, []string{"out", inputDir})
err = mainErr(in, &out, &logOut, []string{"out", inputDir})

assert.NilError(t, err, "\nout: %s\nlogOut: %s", out.String(), logOut.String())
//
Expand Down Expand Up @@ -115,7 +120,7 @@ func TestRunPutSuccessIntegration(t *testing.T) {
var out bytes.Buffer
var logOut bytes.Buffer
inputDir := testhelp.MakeGitRepoFromTestdata(t, "../../cogito/testdata/one-repo/a-repo",
testhelp.HttpsRemote(gitHubCfg.Owner, gitHubCfg.Repo), gitHubCfg.SHA,
testhelp.HttpsRemote("github.com", gitHubCfg.Owner, gitHubCfg.Repo), gitHubCfg.SHA,
"ref: refs/heads/a-branch-FIXME")
t.Setenv("BUILD_JOB_NAME", "TestRunPutSuccessIntegration")
t.Setenv("ATC_EXTERNAL_URL", "https://cogito.invalid")
Expand Down
4 changes: 1 addition & 3 deletions cogito/ghcommitsink.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const (
// GitHubCommitStatusSink is an implementation of [Sinker] for the Cogito resource.
type GitHubCommitStatusSink struct {
Log hclog.Logger
GhAPI string
GitRef string
Request PutRequest
}
Expand All @@ -42,7 +41,7 @@ func (sink GitHubCommitStatusSink) Send() error {
context := ghMakeContext(sink.Request)

target := &github.Target{
Server: sink.GhAPI,
Server: sink.Request.Source.GithubApiEndpoint,
Retry: retry.Retry{
FirstDelay: retryFirstDelay,
BackoffLimit: retryBackoffLimit,
Expand All @@ -58,7 +57,6 @@ func (sink GitHubCommitStatusSink) Send() error {
"state", ghState, "owner", sink.Request.Source.Owner,
"repo", sink.Request.Source.Repo, "git-ref", sink.GitRef,
"context", context, "buildURL", buildURL, "description", description)

if err := commitStatus.Add(sink.GitRef, ghState, buildURL, description); err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions cogito/ghcommitsink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ func TestSinkGitHubCommitStatusSendSuccess(t *testing.T) {
ts := testhelp.SpyHttpServer(&ghReq, nil, &URL, http.StatusCreated)
sink := cogito.GitHubCommitStatusSink{
Log: hclog.NewNullLogger(),
GhAPI: ts.URL,
GitRef: wantGitRef,
Request: cogito.PutRequest{
Source: cogito.Source{GithubApiEndpoint: ts.URL},
Params: cogito.PutParams{State: wantState},
Env: cogito.Environment{BuildJobName: jobName},
},
Expand All @@ -50,9 +50,9 @@ func TestSinkGitHubCommitStatusSendFailure(t *testing.T) {
defer ts.Close()
sink := cogito.GitHubCommitStatusSink{
Log: hclog.NewNullLogger(),
GhAPI: ts.URL,
GitRef: "deadbeefdeadbeef",
Request: cogito.PutRequest{
Source: cogito.Source{GithubApiEndpoint: ts.URL},
Params: cogito.PutParams{State: cogito.StatePending},
},
}
Expand Down
12 changes: 9 additions & 3 deletions cogito/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"strings"

"github.com/Pix4D/cogito/github"
"github.com/Pix4D/cogito/sets"
)

Expand Down Expand Up @@ -165,14 +166,14 @@ type Source struct {
//
// Optional
//
GithubApiEndpoint string `json:"github_api_endpoint"`
GChatWebHook string `json:"gchat_webhook"` // SENSITIVE
LogLevel string `json:"log_level"`
LogUrl string `json:"log_url"` // DEPRECATED
ContextPrefix string `json:"context_prefix"`
ChatAppendSummary bool `json:"chat_append_summary"`
ChatNotifyOnStates []BuildState `json:"chat_notify_on_states"`
Sinks []string `json:"sinks"`
GithubApiEndpoint string `json:"github_api_endpoint"`
}

// String renders Source, redacting the sensitive fields.
Expand All @@ -181,15 +182,15 @@ func (src Source) String() string {

fmt.Fprintf(&bld, "owner: %s\n", src.Owner)
fmt.Fprintf(&bld, "repo: %s\n", src.Repo)
fmt.Fprintf(&bld, "github_api_endpoint: %s\n", src.GithubApiEndpoint)
fmt.Fprintf(&bld, "access_token: %s\n", redact(src.AccessToken))
fmt.Fprintf(&bld, "gchat_webhook: %s\n", redact(src.GChatWebHook))
fmt.Fprintf(&bld, "log_level: %s\n", src.LogLevel)
fmt.Fprintf(&bld, "context_prefix: %s\n", src.ContextPrefix)
fmt.Fprintf(&bld, "chat_append_summary: %t\n", src.ChatAppendSummary)
fmt.Fprintf(&bld, "chat_notify_on_states: %s\n", src.ChatNotifyOnStates)
// Last one: no newline.
fmt.Fprintf(&bld, "sinks: %s\n", src.Sinks)
fmt.Fprintf(&bld, "github_api_endpoint: %s", src.GithubApiEndpoint)
fmt.Fprintf(&bld, "sinks: %s", src.Sinks)

return bld.String()
}
Expand Down Expand Up @@ -257,7 +258,9 @@ func (src *Source) Validate() error {
return fmt.Errorf("source: missing keys: %s", strings.Join(mandatory, ", "))
}

//
// Validate optional fields.
//
if src.GithubApiEndpoint != "" {
u, err := url.ParseRequestURI(src.GithubApiEndpoint)
if err != nil || u.Host == "" {
Expand All @@ -274,6 +277,9 @@ func (src *Source) Validate() error {
if len(src.ChatNotifyOnStates) == 0 {
src.ChatNotifyOnStates = defaultNotifyStates
}
if len(src.GithubApiEndpoint) == 0 {
src.GithubApiEndpoint = github.API
}

return nil
}
Expand Down
9 changes: 5 additions & 4 deletions cogito/protocol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ func TestSourcePrintLogRedaction(t *testing.T) {
source := cogito.Source{
Owner: "the-owner",
Repo: "the-repo",
GithubApiEndpoint: "dummy-api",
AccessToken: "sensitive-the-access-token",
GChatWebHook: "sensitive-gchat-webhook",
LogLevel: "debug",
Expand All @@ -224,14 +225,14 @@ func TestSourcePrintLogRedaction(t *testing.T) {
t.Run("fmt.Print redacts fields", func(t *testing.T) {
want := `owner: the-owner
repo: the-repo
github_api_endpoint: dummy-api
access_token: ***REDACTED***
gchat_webhook: ***REDACTED***
log_level: debug
context_prefix: the-prefix
chat_append_summary: true
chat_notify_on_states: [success failure]
sinks: []
github_api_endpoint: `
sinks: []`

have := fmt.Sprint(source)

Expand All @@ -244,14 +245,14 @@ github_api_endpoint: `
}
want := `owner: the-owner
repo:
github_api_endpoint:
access_token:
gchat_webhook:
log_level:
context_prefix:
chat_append_summary: false
chat_notify_on_states: []
sinks: []
github_api_endpoint: `
sinks: []`

have := fmt.Sprint(input)

Expand Down
77 changes: 13 additions & 64 deletions cogito/put_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cogito_test
import (
"errors"
"fmt"
"github.com/Pix4D/cogito/github"
"io"
"path/filepath"
"testing"
Expand Down Expand Up @@ -124,7 +123,7 @@ func TestPutFailure(t *testing.T) {

func TestPutterLoadConfigurationSuccess(t *testing.T) {
in := testhelp.ToJSON(t, basePutRequest)
putter := cogito.NewPutter("dummy-API", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())

err := putter.LoadConfiguration(in, []string{"dummy-dir"})

Expand All @@ -143,7 +142,7 @@ func TestPutterLoadConfigurationSinksOverrideSuccess(t *testing.T) {
},
"params": {"sinks": ["gchat"]}
}`)
putter := cogito.NewPutter("dummy-API", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())
inputDir := []string{""}
err := putter.LoadConfiguration(in, inputDir)
assert.NilError(t, err)
Expand All @@ -154,22 +153,6 @@ func TestPutterLoadConfigurationSinksOverrideSuccess(t *testing.T) {
assert.NilError(t, err)
}

func TestPutterLoadConfigurationGhApiEndpointOverrideSuccess(t *testing.T) {
in := []byte(`
{
"source": {
"owner": "the-owner",
"repo": "the-repo",
"access_token": "the-token",
"github_api_endpoint": "https://ghe.company.com"
}
}`)
putter := cogito.NewPutter("dummy-API", hclog.NewNullLogger())
inputDir := []string{""}
err := putter.LoadConfiguration(in, inputDir)
assert.NilError(t, err)
}

func TestPutterLoadConfigurationFailure(t *testing.T) {
type testCase struct {
name string
Expand All @@ -180,7 +163,7 @@ func TestPutterLoadConfigurationFailure(t *testing.T) {

test := func(t *testing.T, tc testCase) {
in := testhelp.ToJSON(t, tc.putInput)
putter := cogito.NewPutter("dummy-API", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())

err := putter.LoadConfiguration(in, tc.args)

Expand All @@ -207,20 +190,6 @@ func TestPutterLoadConfigurationFailure(t *testing.T) {
args: []string{},
wantErr: "put: concourse resource protocol violation: missing input directory",
},
{
name: "invalid GH endpoint url in source",
putInput: cogito.PutRequest{
Source: cogito.Source{
Owner: "owner",
Repo: "repo",
AccessToken: "token",
GithubApiEndpoint: "invalid-api-endpoint",
},
Params: cogito.PutParams{State: cogito.StateSuccess},
},
args: []string{},
wantErr: "put: source: github_api_endpoint 'invalid-api-endpoint' is an invalid api endpoint",
},
}

for _, tc := range testCases {
Expand All @@ -235,7 +204,7 @@ func TestPutterLoadConfigurationInvalidParamsFailure(t *testing.T) {
"params": {"pizza": "margherita"}
}`)
wantErr := `put: parsing request: json: unknown field "pizza"`
putter := cogito.NewPutter("dummy-API", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())

err := putter.LoadConfiguration(in, nil)

Expand All @@ -249,7 +218,7 @@ func TestPutterLoadConfigurationMissingGchatwebHook(t *testing.T) {
"params": {}
}`)
wantErr := `put: source: missing keys: gchat_webhook`
putter := cogito.NewPutter("dummy-API", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())

err := putter.LoadConfiguration(in, nil)

Expand All @@ -263,7 +232,7 @@ func TestPutterLoadConfigurationUnknownSink(t *testing.T) {
"params": {}
}`)
wantErr := `put: source: invalid sink(s): [pizza]`
putter := cogito.NewPutter("dummy-API", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())

err := putter.LoadConfiguration(in, nil)

Expand All @@ -277,7 +246,7 @@ func TestPutterLoadConfigurationUnknownSinkPutParams(t *testing.T) {
"params": {"sinks": ["pizza"]}
}`)
wantErr := `put: arguments: unsupported sink(s): [pizza]`
putter := cogito.NewPutter("dummy-API", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())

err := putter.LoadConfiguration(in, nil)

Expand All @@ -293,7 +262,7 @@ func TestPutterProcessInputDirSuccess(t *testing.T) {
}

test := func(t *testing.T, tc testCase) {
putter := cogito.NewPutter("dummy-API", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())
tmpDir := testhelp.MakeGitRepoFromTestdata(t, tc.inputDir,
"https://github.com/dummy-owner/dummy-repo", "dummySHA", "banana")
putter.InputDir = filepath.Join(tmpDir, filepath.Base(tc.inputDir))
Expand All @@ -303,7 +272,6 @@ func TestPutterProcessInputDirSuccess(t *testing.T) {
}

err := putter.ProcessInputDir()

assert.NilError(t, err)
}

Expand Down Expand Up @@ -348,7 +316,7 @@ func TestPutterProcessInputDirFailure(t *testing.T) {
test := func(t *testing.T, tc testCase) {
tmpDir := testhelp.MakeGitRepoFromTestdata(t, tc.inputDir,
"https://github.com/dummy-owner/dummy-repo", "dummySHA", "banana mango")
putter := cogito.NewPutter("dummy-api", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())
putter.Request = cogito.PutRequest{
Source: cogito.Source{Owner: "dummy-owner", Repo: "dummy-repo"},
Params: tc.params,
Expand Down Expand Up @@ -425,7 +393,7 @@ func TestPutterProcessInputDirNonExisting(t *testing.T) {
}

func TestPutterSinks(t *testing.T) {
putter := cogito.NewPutter("dummy-API", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())

sinks := putter.Sinks()
assert.Assert(t, len(sinks) == 2)
Expand All @@ -436,27 +404,8 @@ func TestPutterSinks(t *testing.T) {
assert.Assert(t, ok2)
}

func TestGitHubCommitStatusSinkApiEndpointOverrideFromSource(t *testing.T) {
// default case
defaultApiEndpoint := github.API
defaultPutter := cogito.NewPutter(defaultApiEndpoint, hclog.NewNullLogger())
sinks := defaultPutter.Sinks()
ghSink := sinks[1].(cogito.GitHubCommitStatusSink)
assert.Assert(t, ghSink.GhAPI == defaultApiEndpoint)

// override
customEndpointPutter := cogito.NewPutter(defaultApiEndpoint, hclog.NewNullLogger())
customEndpoint := "https://ghe.company.com"
customEndpointPutter.Request = cogito.PutRequest{
Source: cogito.Source{GithubApiEndpoint: customEndpoint},
}
customPutterSinks := customEndpointPutter.Sinks()
customPutterGhSink := customPutterSinks[1].(cogito.GitHubCommitStatusSink)
assert.Assert(t, customPutterGhSink.GhAPI == customEndpoint)
}

func TestPutterCustomSinks(t *testing.T) {
putter := cogito.NewPutter("dummy-api", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())
putter.Request = cogito.PutRequest{
Params: cogito.PutParams{Sinks: []string{"gchat"}},
}
Expand All @@ -467,15 +416,15 @@ func TestPutterCustomSinks(t *testing.T) {
}

func TestPutterOutputSuccess(t *testing.T) {
putter := cogito.NewPutter("dummy-API", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())

err := putter.Output(io.Discard)

assert.NilError(t, err)
}

func TestPutterOutputFailure(t *testing.T) {
putter := cogito.NewPutter("dummy-API", hclog.NewNullLogger())
putter := cogito.NewPutter(hclog.NewNullLogger())

err := putter.Output(&testhelp.FailingWriter{})

Expand Down
Loading

0 comments on commit cff0c2a

Please sign in to comment.