From 36bbfe5ef3734ec3d81bf974990cc55854cf30f6 Mon Sep 17 00:00:00 2001 From: Gaius Date: Thu, 12 Sep 2024 21:17:45 +0800 Subject: [PATCH] test: add dragonfly p2p tests Signed-off-by: Gaius --- src/pkg/p2p/preheat/provider/dragonfly.go | 86 ++++++----- .../p2p/preheat/provider/dragonfly_test.go | 67 +++------ src/pkg/p2p/preheat/provider/mock.go | 137 +++++++++--------- 3 files changed, 134 insertions(+), 156 deletions(-) diff --git a/src/pkg/p2p/preheat/provider/dragonfly.go b/src/pkg/p2p/preheat/provider/dragonfly.go index 1b5940d9f88..edb2a162fee 100644 --- a/src/pkg/p2p/preheat/provider/dragonfly.go +++ b/src/pkg/p2p/preheat/provider/dragonfly.go @@ -116,8 +116,8 @@ type dragonflyJobResponse struct { // SuccessTasks is the success tasks. SuccessTasks []*struct { - // TaskID is the task id. - TaskID string `json:"task_id"` + // URL is the url of the task, which is the blob url. + URL string `json:"url"` // Hostname is the hostname of the task. Hostname string `json:"hostname"` @@ -129,8 +129,8 @@ type dragonflyJobResponse struct { // FailureTasks is the failure tasks. FailureTasks []*struct { - // TaskID is the task id. - TaskID string `json:"task_id"` + // URL is the url of the task, which is the blob url. + URL string `json:"url"` // Hostname is the hostname of the task. Hostname string `json:"hostname"` @@ -223,6 +223,35 @@ func (dd *DragonflyDriver) Preheat(preheatingImage *PreheatImage) (*PreheatingSt return nil, err } + return &PreheatingStatus{ + TaskID: resp.ID, + Status: provider.PreheatingStatusPending, + StartTime: resp.CreatedAt.Format(time.RFC3339), + FinishTime: resp.UpdatedAt.Format(time.RFC3339), + }, nil +} + +// CheckProgress implements @Driver.CheckProgress. +func (dd *DragonflyDriver) CheckProgress(taskID string) (*PreheatingStatus, error) { + if dd.instance == nil { + return nil, errors.New("missing instance metadata") + } + + if taskID == "" { + return nil, errors.New("no task ID") + } + + url := fmt.Sprintf("%s%s/%s", strings.TrimSuffix(dd.instance.Endpoint, "/"), dragonflyJobPath, taskID) + data, err := client.GetHTTPClient(dd.instance.Insecure).Get(url, dd.getCred(), nil, nil) + if err != nil { + return nil, err + } + + resp := &dragonflyJobResponse{} + if err := json.Unmarshal(data, resp); err != nil { + return nil, err + } + var ( successMessage string errorMessage string @@ -236,30 +265,37 @@ func (dd *DragonflyDriver) Preheat(preheatingImage *PreheatImage) (*PreheatingSt var buffer bytes.Buffer table := tablewriter.NewWriter(&buffer) - table.SetHeader([]string{"Hostname", "IP", "Cluster ID", "State"}) + table.SetHeader([]string{"Blob URL", "Hostname", "IP", "Cluster ID", "State", "Error Message"}) for _, jobState := range resp.Result.JobStates { for _, result := range jobState.Results { // Write the success tasks records to the table. for _, successTask := range result.SuccessTasks { - table.Append([]string{successTask.Hostname, successTask.IP, string(result.SchedulerClusterID), dragonflyJobSuccessState}) + table.Append([]string{successTask.URL, successTask.Hostname, successTask.IP, fmt.Sprint(result.SchedulerClusterID), dragonflyJobSuccessState, ""}) } // Write the failure tasks records to the table. for _, failureTask := range result.FailureTasks { - table.Append([]string{failureTask.Hostname, failureTask.IP, string(result.SchedulerClusterID), dragonflyJobFailureState}) + table.Append([]string{failureTask.URL, failureTask.Hostname, failureTask.IP, fmt.Sprint(result.SchedulerClusterID), dragonflyJobFailureState, failureTask.Description}) } } } + table.Render() successMessage = buffer.String() case dragonflyJobFailureState: - var errs error state = provider.PreheatingStatusFail + + var buffer bytes.Buffer + table := tablewriter.NewWriter(&buffer) + table.SetHeader([]string{"Error Message"}) for _, jobState := range resp.Result.JobStates { - errs = errors.Join(errs, errors.New(jobState.Error)) + table.Append([]string{jobState.Error}) } - errorMessage = errs.Error() + table.Render() + if len(resp.Result.JobStates) > 0 { + errorMessage = buffer.String() + } default: state = provider.PreheatingStatusFail errorMessage = fmt.Sprintf("unknown state: %s", resp.State) @@ -275,36 +311,6 @@ func (dd *DragonflyDriver) Preheat(preheatingImage *PreheatImage) (*PreheatingSt }, nil } -// CheckProgress implements @Driver.CheckProgress. -func (dd *DragonflyDriver) CheckProgress(taskID string) (*PreheatingStatus, error) { - if dd.instance == nil { - return nil, errors.New("missing instance metadata") - } - - if taskID == "" { - return nil, errors.New("no task ID") - } - - url := fmt.Sprintf("%s%s/%s", strings.TrimSuffix(dd.instance.Endpoint, "/"), dragonflyJobPath, taskID) - data, err := client.GetHTTPClient(dd.instance.Insecure).Get(url, dd.getCred(), nil, nil) - if err != nil { - return nil, err - } - - resp := &dragonflyJobResponse{} - if err := json.Unmarshal(data, resp); err != nil { - return nil, err - } - - return &PreheatingStatus{ - TaskID: resp.ID, - Status: resp.State, - Error: resp.Result.JobStates[0].Error, - StartTime: resp.CreatedAt.Format(time.RFC3339), - FinishTime: resp.UpdatedAt.Format(time.RFC3339), - }, nil -} - func (dd *DragonflyDriver) getCred() *auth.Credential { return &auth.Credential{ Mode: dd.instance.AuthMode, diff --git a/src/pkg/p2p/preheat/provider/dragonfly_test.go b/src/pkg/p2p/preheat/provider/dragonfly_test.go index 1657ea73483..b4ce23b4097 100644 --- a/src/pkg/p2p/preheat/provider/dragonfly_test.go +++ b/src/pkg/p2p/preheat/provider/dragonfly_test.go @@ -79,7 +79,6 @@ func (suite *DragonflyTestSuite) TestGetHealth() { // TestPreheat tests Preheat method. func (suite *DragonflyTestSuite) TestPreheat() { - // preheat first time st, err := suite.driver.Preheat(&PreheatImage{ Type: "image", ImageName: "busybox", @@ -89,56 +88,32 @@ func (suite *DragonflyTestSuite) TestPreheat() { Scope: "single_peer", }) require.NoError(suite.T(), err, "preheat image") - suite.Equal("dragonfly-id", st.TaskID, "preheat image result") - - // preheat the same image second time - st, err = suite.driver.Preheat(&PreheatImage{ - Type: "image", - ImageName: "busybox", - Tag: "latest", - URL: "https://harbor.com", - Digest: "sha256:f3c97e3bd1e27393eb853a5c90b1132f2cda84336d5ba5d100c720dc98524c82", - Scope: "all_peers", - }) - require.NoError(suite.T(), err, "preheat image") - suite.Equal("", st.TaskID, "preheat image result") - - // preheat image digest is empty - st, err = suite.driver.Preheat(&PreheatImage{ - ImageName: "", - }) - require.Error(suite.T(), err, "preheat image") + suite.Equal(provider.PreheatingStatusPending, st.Status, "preheat status") + suite.Equal("1", st.TaskID, "task id") + suite.NotEmptyf(st.StartTime, "start time") + suite.NotEmptyf(st.FinishTime, "finish time") } // TestCheckProgress tests CheckProgress method. func (suite *DragonflyTestSuite) TestCheckProgress() { - st, err := suite.driver.CheckProgress("dragonfly-id") - require.NoError(suite.T(), err, "get preheat status") + st, err := suite.driver.CheckProgress("1") + require.NoError(suite.T(), err, "get image") + suite.Equal(provider.PreheatingStatusRunning, st.Status, "preheat status") + suite.Equal("1", st.TaskID, "task id") + suite.NotEmptyf(st.StartTime, "start time") + suite.NotEmptyf(st.FinishTime, "finish time") + + st, err = suite.driver.CheckProgress("2") + require.NoError(suite.T(), err, "get image") suite.Equal(provider.PreheatingStatusSuccess, st.Status, "preheat status") + suite.Equal("2", st.TaskID, "task id") + suite.NotEmptyf(st.StartTime, "start time") + suite.NotEmptyf(st.FinishTime, "finish time") - // preheat job exit but returns no id - st, err = suite.driver.CheckProgress("preheat-job-exist-with-no-id") - require.Error(suite.T(), err, "get preheat status") - - // preheat job exit returns id but get info with that failed - st, err = suite.driver.CheckProgress("preheat-job-exist-with-id-1") - require.Error(suite.T(), err, "get preheat status") - - // preheat job normal failed - st, err = suite.driver.CheckProgress("preheat-job-normal-failed") - require.NoError(suite.T(), err, "get preheat status") + st, err = suite.driver.CheckProgress("3") + require.NoError(suite.T(), err, "get image") suite.Equal(provider.PreheatingStatusFail, st.Status, "preheat status") - - // instance is empty - testDriver := &DragonflyDriver{} - st, err = testDriver.CheckProgress("") - require.Error(suite.T(), err, "get preheat status") - - // preheat job with no task id - st, err = suite.driver.CheckProgress("") - require.Error(suite.T(), err, "get preheat status") - - // preheat job with err json response - st, err = suite.driver.CheckProgress("preheat-job-err-body-json") - require.Error(suite.T(), err, "get preheat status") + suite.Equal("3", st.TaskID, "task id") + suite.NotEmptyf(st.StartTime, "start time") + suite.NotEmptyf(st.FinishTime, "finish time") } diff --git a/src/pkg/p2p/preheat/provider/mock.go b/src/pkg/p2p/preheat/provider/mock.go index 3ff3973bbe6..aa38f894c43 100644 --- a/src/pkg/p2p/preheat/provider/mock.go +++ b/src/pkg/p2p/preheat/provider/mock.go @@ -16,10 +16,10 @@ package provider import ( "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" - "strings" "time" "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/notification" @@ -32,124 +32,121 @@ var preheatMap = make(map[string]struct{}) func MockDragonflyProvider() *httptest.Server { return httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.RequestURI { - case healthCheckEndpoint: + case dragonflyHealthPath: if r.Method != http.MethodGet { w.WriteHeader(http.StatusNotImplemented) return } w.WriteHeader(http.StatusOK) - case preheatEndpoint: + case dragonflyJobPath: if r.Method != http.MethodPost { w.WriteHeader(http.StatusNotImplemented) return } - data, err := io.ReadAll(r.Body) + var resp = &dragonflyJobResponse{ + ID: "1", + State: dragonflyJobPendingState, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + bytes, err := json.Marshal(resp) if err != nil { w.WriteHeader(http.StatusInternalServerError) _, _ = w.Write([]byte(err.Error())) return } - image := &PreheatImage{} - if err := json.Unmarshal(data, image); err != nil { + if _, err := w.Write(bytes); err != nil { w.WriteHeader(http.StatusInternalServerError) _, _ = w.Write([]byte(err.Error())) return } - if image.ImageName == "" { - w.WriteHeader(http.StatusBadRequest) + w.WriteHeader(http.StatusOK) + case fmt.Sprintf("%s/%s", dragonflyJobPath, "1"): + if r.Method != http.MethodGet { + w.WriteHeader(http.StatusNotImplemented) return } - if _, ok := preheatMap[image.Digest]; ok { - w.WriteHeader(http.StatusAlreadyReported) - _, _ = w.Write([]byte(`{"ID":""}`)) - return + var resp = &dragonflyJobResponse{ + ID: "1", + State: dragonflyJobPendingState, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), } - preheatMap[image.Digest] = struct{}{} - - if image.Type == "image" && - image.URL == "https://harbor.com" && - image.ImageName == "busybox" && - image.Tag == "latest" { - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(`{"ID":"dragonfly-id"}`)) + bytes, err := json.Marshal(resp) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(err.Error())) return } - w.WriteHeader(http.StatusBadRequest) - case strings.Replace(preheatTaskEndpoint, "{task_id}", "dragonfly-id", 1): - if r.Method != http.MethodGet { - w.WriteHeader(http.StatusNotImplemented) + if _, err := w.Write(bytes); err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(err.Error())) return } - status := &dragonflyPreheatInfo{ - ID: "dragonfly-id", - StartTime: time.Now().UTC().String(), - FinishTime: time.Now().Add(5 * time.Minute).UTC().String(), - Status: "SUCCESS", - } - bytes, _ := json.Marshal(status) - _, _ = w.Write(bytes) - case strings.Replace(preheatTaskEndpoint, "{task_id}", "preheat-job-exist-with-no-id", 1): + + w.WriteHeader(http.StatusOK) + case fmt.Sprintf("%s/%s", dragonflyJobPath, "2"): if r.Method != http.MethodGet { w.WriteHeader(http.StatusNotImplemented) return } - status := &dragonflyPreheatInfo{ - ID: "preheat-exist-with-no-id", - StartTime: time.Now().UTC().String(), - FinishTime: time.Now().Add(5 * time.Minute).UTC().String(), - Status: "FAILED", - ErrorMsg: "{\"Code\":208,\"Msg\":\"preheat task already exists, id:\"}", + + var resp = &dragonflyJobResponse{ + ID: "2", + State: dragonflyJobSuccessState, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), } - bytes, _ := json.Marshal(status) - _, _ = w.Write(bytes) - case strings.Replace(preheatTaskEndpoint, "{task_id}", "preheat-job-normal-failed", 1): - if r.Method != http.MethodGet { - w.WriteHeader(http.StatusNotImplemented) + + bytes, err := json.Marshal(resp) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(err.Error())) return } - status := &dragonflyPreheatInfo{ - ID: "preheat-job-exist-with-id-1", - StartTime: time.Now().UTC().String(), - FinishTime: time.Now().Add(5 * time.Minute).UTC().String(), - Status: "FAILED", - ErrorMsg: "{\"Code\":208,\"Msg\":\"some msg\"}", + + if _, err := w.Write(bytes); err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(err.Error())) + return } - bytes, _ := json.Marshal(status) - _, _ = w.Write(bytes) - case strings.Replace(preheatTaskEndpoint, "{task_id}", "preheat-job-exist-with-id-1", 1): + + w.WriteHeader(http.StatusOK) + case fmt.Sprintf("%s/%s", dragonflyJobPath, "3"): if r.Method != http.MethodGet { w.WriteHeader(http.StatusNotImplemented) return } - status := &dragonflyPreheatInfo{ - ID: "preheat-job-exist-with-id-1", - StartTime: time.Now().UTC().String(), - FinishTime: time.Now().Add(5 * time.Minute).UTC().String(), - Status: "FAILED", - ErrorMsg: "{\"Code\":208,\"Msg\":\"preheat task already exists, id:preheat-job-exist-with-id-1-1\"}", + + var resp = &dragonflyJobResponse{ + ID: "3", + State: dragonflyJobFailureState, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), } - bytes, _ := json.Marshal(status) - _, _ = w.Write(bytes) - case strings.Replace(preheatTaskEndpoint, "{task_id}", "preheat-job-exist-with-id-1-1", 1): - if r.Method != http.MethodGet { - w.WriteHeader(http.StatusNotImplemented) + + bytes, err := json.Marshal(resp) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(err.Error())) return } - w.WriteHeader(http.StatusInternalServerError) - case strings.Replace(preheatTaskEndpoint, "{task_id}", "preheat-job-err-body-json", 1): - if r.Method != http.MethodGet { - w.WriteHeader(http.StatusNotImplemented) + + if _, err := w.Write(bytes); err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(err.Error())) return } - bodyStr := "\"err body\"" - _, _ = w.Write([]byte(bodyStr)) + + w.WriteHeader(http.StatusOK) default: w.WriteHeader(http.StatusNotImplemented) }