-
Notifications
You must be signed in to change notification settings - Fork 237
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Delete unused code These fields are never read from, so we can delete them. * Delete code that doesn't do anything This code has no effect (the endpoint variable is never used, nor the value being written back to the struct it came from). * Allow creating rest clients directly not from conf Split the New into two different functions, one that takes exactly what is needed to create the struct, and another that is able to assemble these values from the config. * Create a client for speaking to dl.circleci.com - Also, add code for calling the DLC purge endpoint * Add the command to purge DLC --------- Co-authored-by: Abraham Tewa <[email protected]> Co-authored-by: JulesFaucherre <[email protected]>
- Loading branch information
1 parent
199d46b
commit 1e8b8c7
Showing
16 changed files
with
481 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package dl | ||
|
||
// ProjectClient is the interface to interact with dl | ||
type DlClient interface { | ||
PurgeDLC(projectid string) error | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package dl | ||
|
||
import ( | ||
"fmt" | ||
"net/url" | ||
"time" | ||
|
||
"github.com/CircleCI-Public/circleci-cli/api/rest" | ||
"github.com/CircleCI-Public/circleci-cli/settings" | ||
) | ||
|
||
const defaultDlHost = "https://dl.circleci.com" | ||
|
||
type dlRestClient struct { | ||
client *rest.Client | ||
} | ||
|
||
// NewDlRestClient returns a new dlRestClient instance initialized with the | ||
// values from the config. | ||
func NewDlRestClient(config settings.Config) (*dlRestClient, error) { // | ||
// We don't want the user to use this with Server as that's nor supported | ||
// at them moment. In order to detect this we look if there's a config file | ||
// or cli option that sets "host" to anything different than the default | ||
if config.Host != "" && config.Host != "https://circleci.com" { | ||
// Only error if there's no custom DlHost set. Since the end user can't | ||
// a custom value set this in the config file, this has to have been | ||
// manually been set in the code, presumably by the test suite to allow | ||
// talking to a mock server, and we want to allow that. | ||
if config.DlHost == "" { | ||
return nil, &CloudOnlyErr{} | ||
} | ||
} | ||
|
||
// what's the base URL? | ||
unparsedURL := defaultDlHost | ||
if config.DlHost != "" { | ||
unparsedURL = config.DlHost | ||
} | ||
|
||
baseURL, err := url.Parse(unparsedURL) | ||
if err != nil { | ||
return nil, fmt.Errorf("cannot parse dl host URL '%s'", unparsedURL) | ||
} | ||
|
||
httpclient := config.HTTPClient | ||
httpclient.Timeout = 10 * time.Second | ||
|
||
// the dl endpoint is hardcoded to https://dl.circleci.com, since currently | ||
// this implementation always refers to the cloud dl service | ||
return &dlRestClient{ | ||
client: rest.New( | ||
baseURL, | ||
config.Token, | ||
httpclient, | ||
), | ||
}, nil | ||
} | ||
|
||
func (c dlRestClient) PurgeDLC(projectid string) error { | ||
// this calls a private circleci endpoint. We make no guarantees about | ||
// this still existing in the future. | ||
path := fmt.Sprintf("private/output/project/%s/dlc", projectid) | ||
req, err := c.client.NewRequest("DELETE", &url.URL{Path: path}, nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
status, err := c.client.DoRequest(req, nil) | ||
|
||
// Futureproofing: If CircleCI ever removes the private backend endpoint | ||
// this call uses, by having the endpoint return a 410 status code CircleCI | ||
// can get everyone running an outdated client to display a helpful error | ||
// telling them to upgrade (presumably by this point a version without this | ||
// logic will have been released) | ||
if status == 410 { | ||
return &GoneErr{} | ||
} | ||
|
||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package dl | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"net/http/httptest" | ||
"testing" | ||
|
||
"gotest.tools/v3/assert" | ||
|
||
"github.com/CircleCI-Public/circleci-cli/settings" | ||
"github.com/CircleCI-Public/circleci-cli/version" | ||
) | ||
|
||
// getDlRestClient returns a dlRestClient hooked up to the passed server | ||
func getDlRestClient(server *httptest.Server) (*dlRestClient, error) { | ||
return NewDlRestClient(settings.Config{ | ||
DlHost: server.URL, | ||
HTTPClient: http.DefaultClient, | ||
Token: "token", | ||
}) | ||
} | ||
|
||
func Test_DLCPurge(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
handler http.HandlerFunc | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "Should handle a successful request", | ||
handler: func(w http.ResponseWriter, r *http.Request) { | ||
assert.Equal(t, r.Header.Get("circle-token"), "token") | ||
assert.Equal(t, r.Header.Get("accept"), "application/json") | ||
assert.Equal(t, r.Header.Get("user-agent"), version.UserAgent()) | ||
|
||
assert.Equal(t, r.Method, "DELETE") | ||
assert.Equal(t, r.URL.Path, fmt.Sprintf("/private/output/project/%s/dlc", "projectid")) | ||
|
||
// check the request was made with an empty body | ||
br := r.Body | ||
b, err := io.ReadAll(br) | ||
assert.NilError(t, err) | ||
assert.Equal(t, string(b), "") | ||
assert.NilError(t, br.Close()) | ||
|
||
// send response as empty 200 | ||
w.Header().Set("Content-Type", "application/json") | ||
w.WriteHeader(http.StatusOK) | ||
_, err = w.Write([]byte(``)) | ||
assert.NilError(t, err) | ||
}, | ||
}, | ||
{ | ||
name: "Should handle an error request", | ||
handler: func(w http.ResponseWriter, _ *http.Request) { | ||
w.Header().Set("content-type", "application/json") | ||
w.WriteHeader(http.StatusInternalServerError) | ||
_, err := w.Write([]byte(`{"message": "error"}`)) | ||
assert.NilError(t, err) | ||
}, | ||
wantErr: true, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
server := httptest.NewServer(tt.handler) | ||
defer server.Close() | ||
|
||
c, err := getDlRestClient(server) | ||
assert.NilError(t, err) | ||
|
||
err = c.PurgeDLC("projectid") | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("PurgeDLC() error = %#v (%s), wantErr %v", err, err, tt.wantErr) | ||
return | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package dl | ||
|
||
type CloudOnlyErr struct{} | ||
|
||
func (e *CloudOnlyErr) Error() string { | ||
return "Misconfiguration.\n" + | ||
"You have configured a custom API endpoint host for the circleci CLI.\n" + | ||
"However, this functionality is only supported on circleci.com API endpoints." | ||
} | ||
|
||
func IsCloudOnlyErr(err error) bool { | ||
_, ok := err.(*CloudOnlyErr) | ||
return ok | ||
} | ||
|
||
type GoneErr struct{} | ||
|
||
func (e *GoneErr) Error() string { | ||
return "No longer supported.\n" + | ||
"This functionality is no longer supported by this version of the circleci CLI.\n" + | ||
"Please upgrade to the latest version of the circleci CLI." | ||
} | ||
|
||
func IsGoneErr(err error) bool { | ||
_, ok := err.(*GoneErr) | ||
return ok | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.