diff --git a/.gitignore b/.gitignore index c23aff8fc6ed..612657139375 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,7 @@ src/portal/typings/ .vscode/ **/node_modules **/ssl/ -**/proxy.config.json +**/proxy.config.mjs src/portal/src/**/*.js src/portal/src/**/*.js.map diff --git a/Makefile b/Makefile index d98cb8db696f..da855c56bceb 100644 --- a/Makefile +++ b/Makefile @@ -325,7 +325,7 @@ gen_apis: lint_apis MOCKERY_IMAGENAME=$(IMAGENAMESPACE)/mockery -MOCKERY_VERSION=v2.14.0 +MOCKERY_VERSION=v2.22.1 MOCKERY=$(RUNCONTAINER) ${MOCKERY_IMAGENAME}:${MOCKERY_VERSION} MOCKERY_IMAGE_BUILD_CMD=${DOCKERBUILD} -f ${TOOLSPATH}/mockery/Dockerfile --build-arg GOLANG=${GOBUILDIMAGE} --build-arg MOCKERY_VERSION=${MOCKERY_VERSION} -t ${MOCKERY_IMAGENAME}:$(MOCKERY_VERSION) . diff --git a/api/v2.0/swagger.yaml b/api/v2.0/swagger.yaml index 7cc1efd73312..4fe1aa99ce92 100644 --- a/api/v2.0/swagger.yaml +++ b/api/v2.0/swagger.yaml @@ -2665,8 +2665,128 @@ paths: $ref: '#/responses/404' '500': $ref: '#/responses/500' + '/projects/{project_name_or_id}/webhook/policies/{webhook_policy_id}/executions': + get: + summary: List executions for a specific webhook policy + description: | + This endpoint returns the executions of a specific webhook policy. + tags: + - webhook + operationId: ListExecutionsOfWebhookPolicy + parameters: + - $ref: '#/parameters/requestId' + - $ref: '#/parameters/isResourceName' + - $ref: '#/parameters/projectNameOrId' + - $ref: '#/parameters/webhookPolicyId' + - $ref: '#/parameters/page' + - $ref: '#/parameters/pageSize' + - $ref: '#/parameters/query' + - $ref: '#/parameters/sort' + responses: + '200': + description: List webhook executions success + headers: + X-Total-Count: + description: The total count of executions + type: integer + Link: + description: Link refers to the previous page and next page + type: string + schema: + type: array + items: + $ref: '#/definitions/Execution' + '400': + $ref: '#/responses/400' + '401': + $ref: '#/responses/401' + '404': + $ref: '#/responses/404' + '403': + $ref: '#/responses/403' + '500': + $ref: '#/responses/500' + '/projects/{project_name_or_id}/webhook/policies/{webhook_policy_id}/executions/{execution_id}/tasks': + get: + summary: List tasks for a specific webhook execution + description: | + This endpoint returns the tasks of a specific webhook execution. + tags: + - webhook + operationId: ListTasksOfWebhookExecution + parameters: + - $ref: '#/parameters/requestId' + - $ref: '#/parameters/isResourceName' + - $ref: '#/parameters/projectNameOrId' + - $ref: '#/parameters/webhookPolicyId' + - $ref: '#/parameters/executionId' + - $ref: '#/parameters/page' + - $ref: '#/parameters/pageSize' + - $ref: '#/parameters/query' + - $ref: '#/parameters/sort' + responses: + '200': + description: List tasks of webhook executions success + headers: + X-Total-Count: + description: The total count of tasks + type: integer + Link: + description: Link refers to the previous page and next page + type: string + schema: + type: array + items: + $ref: '#/definitions/Task' + '400': + $ref: '#/responses/400' + '401': + $ref: '#/responses/401' + '404': + $ref: '#/responses/404' + '403': + $ref: '#/responses/403' + '500': + $ref: '#/responses/500' + '/projects/{project_name_or_id}/webhook/policies/{webhook_policy_id}/executions/{execution_id}/tasks/{task_id}/log': + get: + summary: Get logs for a specific webhook task + description: | + This endpoint returns the logs of a specific webhook task. + tags: + - webhook + operationId: GetLogsOfWebhookTask + produces: + - text/plain + parameters: + - $ref: '#/parameters/requestId' + - $ref: '#/parameters/isResourceName' + - $ref: '#/parameters/projectNameOrId' + - $ref: '#/parameters/webhookPolicyId' + - $ref: '#/parameters/executionId' + - $ref: '#/parameters/taskId' + responses: + '200': + description: Get log success + headers: + Content-Type: + description: Content type of response + type: string + schema: + type: string + '400': + $ref: '#/responses/400' + '401': + $ref: '#/responses/401' + '404': + $ref: '#/responses/404' + '403': + $ref: '#/responses/403' + '500': + $ref: '#/responses/500' '/projects/{project_name_or_id}/webhook/lasttrigger': get: + deprecated: true summary: Get project webhook policy last trigger info description: | This endpoint returns last trigger information of project webhook policy. @@ -2694,6 +2814,7 @@ paths: $ref: '#/responses/500' '/projects/{project_name_or_id}/webhook/jobs': get: + deprecated: true summary: List project webhook jobs description: | This endpoint returns webhook jobs of a project. @@ -2746,7 +2867,7 @@ paths: '/projects/{project_name_or_id}/webhook/events': get: summary: Get supported event types and notify types. - description: Get supportted event types and notify types. + description: Get supported event types and notify types. tags: - webhook operationId: GetSupportedEventTypes @@ -4593,6 +4714,39 @@ paths: $ref: '#/responses/404' '500': $ref: '#/responses/500' + /jobservice/jobs/{job_id}/log: + get: + operationId: actionGetJobLog + summary: Get job log by job id + description: Get job log by job id, it is only used by administrator + produces: + - text/plain + tags: + - jobservice + parameters: + - $ref: '#/parameters/requestId' + - name: job_id + in: path + required: true + type: string + description: The id of the job. + responses: + '200': + description: Get job log successfully. + headers: + Content-Type: + description: The content type of response body + type: string + schema: + type: string + '401': + $ref: '#/responses/401' + '403': + $ref: '#/responses/403' + '404': + $ref: '#/responses/404' + '500': + $ref: '#/responses/500' /jobservice/queues: get: operationId: listJobQueues @@ -8343,7 +8497,7 @@ definitions: description: 'The group type, 1 for LDAP group, 2 for HTTP group, 3 for OIDC group.' SupportedWebhookEventTypes: type: object - description: Supportted webhook event types and notify types. + description: Supported webhook event types and notify types. properties: event_type: type: array @@ -8353,14 +8507,33 @@ definitions: type: array items: $ref: '#/definitions/NotifyType' + payload_formats: + type: array + items: + $ref: '#/definitions/PayloadFormat' EventType: type: string - description: Webhook supportted event type. - example: 'pullImage' + description: Webhook supported event type. + example: 'PULL_ARTIFACT' NotifyType: type: string - description: Webhook supportted notify type. + description: Webhook supported notify type. example: 'http' + PayloadFormatType: + type: string + description: The type of webhook paylod format. + example: 'cloudevent' + PayloadFormat: + type: object + description: Webhook supported payload format type collections. + properties: + notify_type: + $ref: '#/definitions/NotifyType' + formats: + type: array + description: The supported payload formats for this notify type. + items: + $ref: '#/definitions/PayloadFormatType' WebhookTargetObject: type: object @@ -8378,6 +8551,8 @@ definitions: skip_cert_verify: type: boolean description: Whether or not to skip cert verify. + payload_format: + $ref: '#/definitions/PayloadFormatType' WebhookPolicy: type: object description: The webhook policy object @@ -9207,10 +9382,9 @@ definitions: format: int64 description: The artifact id of the accessory x-omitempty: false - subject_artifact_id: - type: integer - format: int64 - description: The subject artifact id of the accessory + subject_artifact_digest: + type: string + description: The subject artifact digest of the accessory x-omitempty: false size: type: integer diff --git a/make/migrations/postgresql/0110_2.8.0_schema.up.sql b/make/migrations/postgresql/0110_2.8.0_schema.up.sql index 0b4daea1fc5b..5b3987a7b2da 100644 --- a/make/migrations/postgresql/0110_2.8.0_schema.up.sql +++ b/make/migrations/postgresql/0110_2.8.0_schema.up.sql @@ -1,2 +1,82 @@ /* remove the redundant data from table artifact_blob */ -delete from artifact_blob afb where not exists (select digest from blob b where b.digest = afb.digest_af); \ No newline at end of file +delete from artifact_blob afb where not exists (select digest from blob b where b.digest = afb.digest_af); + +/* replace subject_artifact_id with subject_artifact_digest*/ +alter table artifact_accessory add column subject_artifact_digest varchar(1024); + +DO $$ +DECLARE + acc RECORD; + art RECORD; +BEGIN + FOR acc IN SELECT * FROM artifact_accessory + LOOP + SELECT * INTO art from artifact where id = acc.subject_artifact_id; + UPDATE artifact_accessory SET subject_artifact_digest=art.digest WHERE subject_artifact_id = art.id; + END LOOP; +END $$; + +alter table artifact_accessory drop CONSTRAINT artifact_accessory_subject_artifact_id_fkey; +alter table artifact_accessory drop CONSTRAINT unique_artifact_accessory; +alter table artifact_accessory add CONSTRAINT unique_artifact_accessory UNIQUE (artifact_id, subject_artifact_digest); +alter table artifact_accessory drop column subject_artifact_id; + +/* Update the registry and replication policy associated with the chartmuseum */ +UPDATE registry +SET description = 'Chartmuseum has been deprecated in Harbor v2.8.0, please delete this registry.' +WHERE type in ('artifact-hub', 'helm-hub'); +WITH filter_objects AS ( + SELECT id, jsonb_array_elements(filters::jsonb) AS filter + FROM replication_policy + WHERE filters IS NOT NULL AND filters != '' + AND jsonb_typeof(CAST(filters AS jsonb)) = 'array' +), +replication_policy_ids AS ( + SELECT rp.id + FROM registry r + INNER JOIN replication_policy rp ON (rp.dest_registry_id = r.id OR rp.src_registry_id = r.id) + WHERE r.type IN ('artifact-hub', 'helm-hub') +) +UPDATE replication_policy AS rp +SET enabled = false, + filters = ( + SELECT COALESCE(jsonb_agg(fo.filter)::text, '') + FROM filter_objects AS fo + WHERE fo.id = rp.id AND NOT(filter ->> 'type' = 'resource' AND filter ->> 'value' = 'chart') + ), + description = 'Chartmuseum is deprecated in Harbor v2.8.0, because the Source resource filter of this rule is chart(chartmuseum), so please update this rule.' +WHERE id IN ( + SELECT id FROM filter_objects WHERE (filter ->> 'type' = 'resource' AND filter ->> 'value' = 'chart') + UNION + SELECT id FROM replication_policy_ids +); +/* Update the role permission and permission policy associated with the chartmuseum */ +DELETE FROM role_permission +WHERE permission_policy_id IN ( + SELECT id FROM permission_policy WHERE resource IN ('helm-chart', 'helm-chart-version' ,'helm-chart-version-label') +); + +DELETE FROM permission_policy +WHERE resource IN ('helm-chart', 'helm-chart-version' ,'helm-chart-version-label'); +/* Update the notification policy associated with the chartmuseum */ +WITH event_type_objects AS ( + SELECT id, jsonb_array_elements(event_types::jsonb) as event_type + FROM notification_policy + WHERE event_types IS NOT NULL AND event_types != '' + AND jsonb_typeof(CAST(event_types AS jsonb)) = 'array' +) +UPDATE notification_policy AS np +SET event_types = ( + SELECT COALESCE(jsonb_agg(eto.event_type), '[]') + FROM event_type_objects AS eto + WHERE eto.id = np.id + AND NOT(event_type @> '"UPLOAD_CHART"'::jsonb OR event_type @> '"DOWNLOAD_CHART"'::jsonb OR event_type @> '"DELETE_CHART"'::jsonb) +) +WHERE id IN ( + SELECT id FROM event_type_objects WHERE (event_type @> '"UPLOAD_CHART"'::jsonb OR event_type @> '"DOWNLOAD_CHART"'::jsonb OR event_type @> '"DELETE_CHART"'::jsonb) +); + +UPDATE notification_policy +SET enabled = false, + description = 'Chartmuseum is deprecated in Harbor v2.8.0, because this notification policy only has event type about Chartmuseum, so please update or delete this notification policy.' +WHERE event_types = '[]'; diff --git a/make/photon/prepare/templates/jobservice/config.yml.jinja b/make/photon/prepare/templates/jobservice/config.yml.jinja index e41846519bf4..7da2c6f83d1c 100644 --- a/make/photon/prepare/templates/jobservice/config.yml.jinja +++ b/make/photon/prepare/templates/jobservice/config.yml.jinja @@ -51,3 +51,6 @@ metric: path: {{ metric.path }} port: {{ metric.port }} {% endif %} + +# the max size of job log returned by API, default is 10M +max_retrieve_size_mb: 10 \ No newline at end of file diff --git a/src/common/security/robot/context.go b/src/common/security/robot/context.go index 7cc8e88035d5..2754f90fb288 100644 --- a/src/common/security/robot/context.go +++ b/src/common/security/robot/context.go @@ -93,7 +93,7 @@ func (s *SecurityContext) Can(ctx context.Context, action types.Action, resource accesses = append(accesses, &types.Policy{ Action: a.Action, Effect: a.Effect, - Resource: types.Resource(fmt.Sprintf("%s/%s", p.Scope, a.Resource)), + Resource: types.Resource(getPolicyResource(p, a)), }) } } @@ -138,3 +138,11 @@ func filterRobotPolicies(p *models.Project, policies []*types.Policy) []*types.P } return results } + +// getPolicyResource to determine permissions for the project resource, the path should be /project instead of /project/project. +func getPolicyResource(perm *robot.Permission, pol *types.Policy) string { + if strings.HasPrefix(perm.Scope, robot.SCOPEPROJECT) && pol.Resource == rbac.ResourceProject { + return perm.Scope + } + return fmt.Sprintf("%s/%s", perm.Scope, pol.Resource) +} diff --git a/src/common/security/robot/context_test.go b/src/common/security/robot/context_test.go index d1cbd01fd1e1..c499ed24b7d1 100644 --- a/src/common/security/robot/context_test.go +++ b/src/common/security/robot/context_test.go @@ -242,3 +242,89 @@ func Test_filterRobotPolicies(t *testing.T) { }) } } + +func Test_getPolicyResource(t *testing.T) { + type args struct { + perm *robot.Permission + poli *types.Policy + } + tests := []struct { + name string + args args + want string + }{ + { + "project resource", + args{ + &robot.Permission{ + Kind: "project", + Namespace: "library", + Access: []*types.Policy{ + { + Resource: rbac.Resource(fmt.Sprintf("project/%d/repository", private.ProjectID)), + Action: rbac.ActionPush, + }, + { + Resource: rbac.Resource(fmt.Sprintf("project/%d/repository", private.ProjectID)), + Action: rbac.ActionPull, + }, + }, + Scope: fmt.Sprintf("/project/%d", private.ProjectID), + }, + &types.Policy{Resource: "project", Action: "pull", Effect: "allow"}, + }, + fmt.Sprintf("/project/%d", private.ProjectID), + }, + { + "project resource", + args{ + &robot.Permission{ + Kind: "project", + Namespace: "library", + Access: []*types.Policy{ + { + Resource: rbac.Resource(fmt.Sprintf("project/%d/repository", private.ProjectID)), + Action: rbac.ActionPush, + }, + { + Resource: rbac.Resource(fmt.Sprintf("project/%d/repository", private.ProjectID)), + Action: rbac.ActionPull, + }, + }, + Scope: fmt.Sprintf("/project/%d", private.ProjectID), + }, + &types.Policy{Resource: "repository", Action: "get", Effect: "allow"}, + }, + fmt.Sprintf("/project/%d/repository", private.ProjectID), + }, + { + "system resource", + args{ + &robot.Permission{ + Kind: "project", + Namespace: "library", + Access: []*types.Policy{ + { + Resource: rbac.Resource(fmt.Sprintf("project/%d/repository", private.ProjectID)), + Action: rbac.ActionPush, + }, + { + Resource: rbac.Resource(fmt.Sprintf("project/%d/repository", private.ProjectID)), + Action: rbac.ActionPull, + }, + }, + Scope: "/system", + }, + &types.Policy{Resource: "repository", Action: "get", Effect: "allow"}, + }, + "/system/repository", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getPolicyResource(tt.args.perm, tt.args.poli); !reflect.DeepEqual(got, tt.want) { + t.Errorf("getPolicyResource() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/src/controller/artifact/controller.go b/src/controller/artifact/controller.go index e86625e147e1..19907d66e193 100644 --- a/src/controller/artifact/controller.go +++ b/src/controller/artifact/controller.go @@ -161,7 +161,7 @@ func (c *controller) Ensure(ctx context.Context, repository, digest string, opti } } for _, acc := range option.Accs { - if err = c.accessoryMgr.Ensure(ctx, artifact.ID, acc.ArtifactID, acc.Size, acc.Digest, acc.Type); err != nil { + if err = c.accessoryMgr.Ensure(ctx, artifact.Digest, acc.ArtifactID, acc.Size, acc.Digest, acc.Type); err != nil { return false, 0, err } } @@ -722,7 +722,7 @@ func (c *controller) populateAdditionLinks(ctx context.Context, artifact *Artifa } func (c *controller) populateAccessories(ctx context.Context, art *Artifact) { - accs, err := c.accessoryMgr.List(ctx, q.New(q.KeyWords{"SubjectArtifactID": art.ID})) + accs, err := c.accessoryMgr.List(ctx, q.New(q.KeyWords{"SubjectArtifactDigest": art.Digest})) if err != nil { log.Errorf("failed to list accessories of artifact %d: %v", art.ID, err) return diff --git a/src/controller/artifact/controller_test.go b/src/controller/artifact/controller_test.go index 3ad16dea6ae2..40c6537d5195 100644 --- a/src/controller/artifact/controller_test.go +++ b/src/controller/artifact/controller_test.go @@ -137,10 +137,10 @@ func (c *controllerTestSuite) TestAssembleArtifact() { }, nil) acc := &basemodel.Default{ Data: accessorymodel.AccessoryData{ - ID: 1, - ArtifactID: 2, - SubArtifactID: 1, - Type: accessorymodel.TypeCosignSignature, + ID: 1, + ArtifactID: 2, + SubArtifactDigest: "sha256:123", + Type: accessorymodel.TypeCosignSignature, }, } c.accMgr.On("List", mock.Anything, mock.Anything).Return([]accessorymodel.Accessory{ @@ -546,10 +546,10 @@ func (c *controllerTestSuite) TestCopy() { }, nil) acc := &basemodel.Default{ Data: accessorymodel.AccessoryData{ - ID: 1, - ArtifactID: 2, - SubArtifactID: 1, - Type: accessorymodel.TypeCosignSignature, + ID: 1, + ArtifactID: 2, + SubArtifactDigest: "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180", + Type: accessorymodel.TypeCosignSignature, }, } c.accMgr.On("List", mock.Anything, mock.Anything).Return([]accessorymodel.Accessory{ diff --git a/src/controller/artifact/model_test.go b/src/controller/artifact/model_test.go index 93703efc527d..a5ef1f87b20f 100644 --- a/src/controller/artifact/model_test.go +++ b/src/controller/artifact/model_test.go @@ -10,7 +10,7 @@ import ( ) func TestUnmarshalJSONWithACC(t *testing.T) { - data := []byte(`[{"accessories":[{"artifact_id":9,"creation_time":"2022-01-20T09:18:50.993Z","digest":"sha256:a7caa2636af890178a0b8c4cdbc47ced4dbdf29a1680e9e50823e85ce35b28d3","icon":"","id":4,"size":501,"subject_artifact_id":8,"type":"signature.cosign"}], + data := []byte(`[{"accessories":[{"artifact_id":9,"creation_time":"2022-01-20T09:18:50.993Z","digest":"sha256:a7caa2636af890178a0b8c4cdbc47ced4dbdf29a1680e9e50823e85ce35b28d3","icon":"","id":4,"size":501,"subject_artifact_digest":"sha256:e4b315ad03a1d1d9ff0c111e648a1a91066c09ead8352d3d6a48fa971a82922c","type":"signature.cosign"}], "addition_links":{"build_history":{"absolute":false,"href":"/api/v2.0/projects/source_project011642670285/repositories/redis/artifacts/sha256:e4b315ad03a1d1d9ff0c111e648a1a91066c09ead8352d3d6a48fa971a82922c/additions/build_history"}, "vulnerabilities":{"absolute":false,"href":"/api/v2.0/projects/source_project011642670285/repositories/redis/artifacts/sha256:e4b315ad03a1d1d9ff0c111e648a1a91066c09ead8352d3d6a48fa971a82922c/additions/vulnerabilities"}}, "digest":"sha256:e4b315ad03a1d1d9ff0c111e648a1a91066c09ead8352d3d6a48fa971a82922c", @@ -31,7 +31,7 @@ func TestUnmarshalJSONWithACC(t *testing.T) { } func TestUnmarshalJSONWithACCPartial(t *testing.T) { - data := []byte(`[{"accessories":[{"artifact_id":9,"creation_time":"2022-01-20T09:18:50.993Z","digest":"sha256:a7caa2636af890178a0b8c4cdbc47ced4dbdf29a1680e9e50823e85ce35b28d3","icon":"","id":4,"size":501,"subject_artifact_id":8,"type":"signature.cosign"}, {"artifact_id":2, "type":"signature.cosign"}], + data := []byte(`[{"accessories":[{"artifact_id":9,"creation_time":"2022-01-20T09:18:50.993Z","digest":"sha256:a7caa2636af890178a0b8c4cdbc47ced4dbdf29a1680e9e50823e85ce35b28d3","icon":"","id":4,"size":501,"subject_artifact_digest":"sha256:a7caa2636af890178a0b8c4cdbc47ced4dbdf29a1680e9e50823e85ce35b28d3","type":"signature.cosign"}, {"artifact_id":2, "type":"signature.cosign"}], "digest":"sha256:e4b315ad03a1d1d9ff0c111e648a1a91066c09ead8352d3d6a48fa971a82922c","tags":[{"artifact_id":8,"id":6,"immutable":false,"name":"latest","pull_time":"2022-01-20T09:18:50.783Z","push_time":"2022-01-20T09:18:50.303Z","repository_id":5,"signed":false}],"type":"IMAGE"}]`) var artifact []Artifact @@ -47,7 +47,7 @@ func TestUnmarshalJSONWithACCPartial(t *testing.T) { } func TestUnmarshalJSONWithACCUnknownType(t *testing.T) { - data := []byte(`[{"accessories":[{"artifact_id":9,"creation_time":"2022-01-20T09:18:50.993Z","digest":"sha256:a7caa2636af890178a0b8c4cdbc47ced4dbdf29a1680e9e50823e85ce35b28d3","icon":"","id":4,"size":501,"subject_artifact_id":8}], + data := []byte(`[{"accessories":[{"artifact_id":9,"creation_time":"2022-01-20T09:18:50.993Z","digest":"sha256:a7caa2636af890178a0b8c4cdbc47ced4dbdf29a1680e9e50823e85ce35b28d3","icon":"","id":4,"size":501,"subject_artifact_digest":"sha256:a7caa2636af890178a0b8c4cdbc47ced4dbdf29a1680e9e50823e85ce35b28d3"}], "digest":"sha256:e4b315ad03a1d1d9ff0c111e648a1a91066c09ead8352d3d6a48fa971a82922c","tags":[{"artifact_id":8,"id":6,"immutable":false,"name":"latest","pull_time":"2022-01-20T09:18:50.783Z","push_time":"2022-01-20T09:18:50.303Z","repository_id":5,"signed":false}],"type":"IMAGE"}]`) var artifact []Artifact diff --git a/src/controller/jobmonitor/monitor.go b/src/controller/jobmonitor/monitor.go index 49fd9e0ebd2f..c25f34b296ea 100644 --- a/src/controller/jobmonitor/monitor.go +++ b/src/controller/jobmonitor/monitor.go @@ -70,6 +70,7 @@ type MonitorController interface { PauseJobQueues(ctx context.Context, jobType string) error // ResumeJobQueues resume the job queue by type ResumeJobQueues(ctx context.Context, jobType string) error + GetJobLog(ctx context.Context, jobID string) ([]byte, error) } type monitorController struct { @@ -366,3 +367,7 @@ func (w *monitorController) resumeQueue(ctx context.Context, jobType string) err } return nil } + +func (w *monitorController) GetJobLog(ctx context.Context, jobID string) ([]byte, error) { + return w.taskManager.GetLogByJobID(ctx, jobID) +} diff --git a/src/controller/p2p/preheat/enforcer.go b/src/controller/p2p/preheat/enforcer.go index 69d1ad37bb86..e017b648e7ca 100644 --- a/src/controller/p2p/preheat/enforcer.go +++ b/src/controller/p2p/preheat/enforcer.go @@ -483,7 +483,7 @@ func (de *defaultEnforcer) startTask(ctx context.Context, executionID int64, can // getVulnerabilitySev gets the severity code value for the given artifact with allowlist option set func (de *defaultEnforcer) getVulnerabilitySev(ctx context.Context, p *proModels.Project, art *artifact.Artifact) (uint, error) { - vulnerable, err := de.scanCtl.GetVulnerable(ctx, art, p.CVEAllowlist.CVESet()) + vulnerable, err := de.scanCtl.GetVulnerable(ctx, art, p.CVEAllowlist.CVESet(), p.CVEAllowlist.IsExpired()) if err != nil { if errors.IsNotFoundErr(err) { // no vulnerability report diff --git a/src/controller/p2p/preheat/enforcer_test.go b/src/controller/p2p/preheat/enforcer_test.go index ee9f9a047b22..384031e0a2f0 100644 --- a/src/controller/p2p/preheat/enforcer_test.go +++ b/src/controller/p2p/preheat/enforcer_test.go @@ -111,6 +111,7 @@ func (suite *EnforcerTestSuite) SetupSuite() { context.TODO(), mock.AnythingOfType("*artifact.Artifact"), mock.AnythingOfType("models.CVESet"), + mock.AnythingOfType("bool"), ).Return(&scan.Vulnerable{Severity: &low, ScanStatus: "Success"}, nil) fakeProCtl := &project.Controller{} diff --git a/src/controller/replication/flow/mock_adapter_factory_test.go b/src/controller/replication/flow/mock_adapter_factory_test.go index 1aedc23d9c26..f86ceee8a586 100644 --- a/src/controller/replication/flow/mock_adapter_factory_test.go +++ b/src/controller/replication/flow/mock_adapter_factory_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package flow @@ -35,6 +35,10 @@ func (_m *mockFactory) Create(_a0 *model.Registry) (adapter.Adapter, error) { ret := _m.Called(_a0) var r0 adapter.Adapter + var r1 error + if rf, ok := ret.Get(0).(func(*model.Registry) (adapter.Adapter, error)); ok { + return rf(_a0) + } if rf, ok := ret.Get(0).(func(*model.Registry) adapter.Adapter); ok { r0 = rf(_a0) } else { @@ -43,7 +47,6 @@ func (_m *mockFactory) Create(_a0 *model.Registry) (adapter.Adapter, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(*model.Registry) error); ok { r1 = rf(_a0) } else { diff --git a/src/controller/replication/flow/mock_adapter_test.go b/src/controller/replication/flow/mock_adapter_test.go index c522c6eb09bd..be7675ef201e 100644 --- a/src/controller/replication/flow/mock_adapter_test.go +++ b/src/controller/replication/flow/mock_adapter_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package flow @@ -22,13 +22,16 @@ func (_m *mockAdapter) BlobExist(repository string, digest string) (bool, error) ret := _m.Called(repository, digest) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(string, string) (bool, error)); ok { + return rf(repository, digest) + } if rf, ok := ret.Get(0).(func(string, string) bool); ok { r0 = rf(repository, digest) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(string, string) error); ok { r1 = rf(repository, digest) } else { @@ -43,20 +46,23 @@ func (_m *mockAdapter) CanBeMount(digest string) (bool, string, error) { ret := _m.Called(digest) var r0 bool + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func(string) (bool, string, error)); ok { + return rf(digest) + } if rf, ok := ret.Get(0).(func(string) bool); ok { r0 = rf(digest) } else { r0 = ret.Get(0).(bool) } - var r1 string if rf, ok := ret.Get(1).(func(string) string); ok { r1 = rf(digest) } else { r1 = ret.Get(1).(string) } - var r2 error if rf, ok := ret.Get(2).(func(string) error); ok { r2 = rf(digest) } else { @@ -99,6 +105,10 @@ func (_m *mockAdapter) FetchArtifacts(filters []*model.Filter) ([]*model.Resourc ret := _m.Called(filters) var r0 []*model.Resource + var r1 error + if rf, ok := ret.Get(0).(func([]*model.Filter) ([]*model.Resource, error)); ok { + return rf(filters) + } if rf, ok := ret.Get(0).(func([]*model.Filter) []*model.Resource); ok { r0 = rf(filters) } else { @@ -107,7 +117,6 @@ func (_m *mockAdapter) FetchArtifacts(filters []*model.Filter) ([]*model.Resourc } } - var r1 error if rf, ok := ret.Get(1).(func([]*model.Filter) error); ok { r1 = rf(filters) } else { @@ -122,13 +131,16 @@ func (_m *mockAdapter) HealthCheck() (string, error) { ret := _m.Called() var r0 string + var r1 error + if rf, ok := ret.Get(0).(func() (string, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { r0 = ret.Get(0).(string) } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -143,6 +155,10 @@ func (_m *mockAdapter) Info() (*model.RegistryInfo, error) { ret := _m.Called() var r0 *model.RegistryInfo + var r1 error + if rf, ok := ret.Get(0).(func() (*model.RegistryInfo, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() *model.RegistryInfo); ok { r0 = rf() } else { @@ -151,7 +167,6 @@ func (_m *mockAdapter) Info() (*model.RegistryInfo, error) { } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -166,6 +181,10 @@ func (_m *mockAdapter) ListTags(repository string) ([]string, error) { ret := _m.Called(repository) var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(string) ([]string, error)); ok { + return rf(repository) + } if rf, ok := ret.Get(0).(func(string) []string); ok { r0 = rf(repository) } else { @@ -174,7 +193,6 @@ func (_m *mockAdapter) ListTags(repository string) ([]string, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(repository) } else { @@ -189,13 +207,17 @@ func (_m *mockAdapter) ManifestExist(repository string, reference string) (bool, ret := _m.Called(repository, reference) var r0 bool + var r1 *distribution.Descriptor + var r2 error + if rf, ok := ret.Get(0).(func(string, string) (bool, *distribution.Descriptor, error)); ok { + return rf(repository, reference) + } if rf, ok := ret.Get(0).(func(string, string) bool); ok { r0 = rf(repository, reference) } else { r0 = ret.Get(0).(bool) } - var r1 *distribution.Descriptor if rf, ok := ret.Get(1).(func(string, string) *distribution.Descriptor); ok { r1 = rf(repository, reference) } else { @@ -204,7 +226,6 @@ func (_m *mockAdapter) ManifestExist(repository string, reference string) (bool, } } - var r2 error if rf, ok := ret.Get(2).(func(string, string) error); ok { r2 = rf(repository, reference) } else { @@ -247,13 +268,17 @@ func (_m *mockAdapter) PullBlob(repository string, digest string) (int64, io.Rea ret := _m.Called(repository, digest) var r0 int64 + var r1 io.ReadCloser + var r2 error + if rf, ok := ret.Get(0).(func(string, string) (int64, io.ReadCloser, error)); ok { + return rf(repository, digest) + } if rf, ok := ret.Get(0).(func(string, string) int64); ok { r0 = rf(repository, digest) } else { r0 = ret.Get(0).(int64) } - var r1 io.ReadCloser if rf, ok := ret.Get(1).(func(string, string) io.ReadCloser); ok { r1 = rf(repository, digest) } else { @@ -262,7 +287,6 @@ func (_m *mockAdapter) PullBlob(repository string, digest string) (int64, io.Rea } } - var r2 error if rf, ok := ret.Get(2).(func(string, string) error); ok { r2 = rf(repository, digest) } else { @@ -277,13 +301,17 @@ func (_m *mockAdapter) PullBlobChunk(repository string, digest string, blobSize ret := _m.Called(repository, digest, blobSize, start, end) var r0 int64 + var r1 io.ReadCloser + var r2 error + if rf, ok := ret.Get(0).(func(string, string, int64, int64, int64) (int64, io.ReadCloser, error)); ok { + return rf(repository, digest, blobSize, start, end) + } if rf, ok := ret.Get(0).(func(string, string, int64, int64, int64) int64); ok { r0 = rf(repository, digest, blobSize, start, end) } else { r0 = ret.Get(0).(int64) } - var r1 io.ReadCloser if rf, ok := ret.Get(1).(func(string, string, int64, int64, int64) io.ReadCloser); ok { r1 = rf(repository, digest, blobSize, start, end) } else { @@ -292,7 +320,6 @@ func (_m *mockAdapter) PullBlobChunk(repository string, digest string, blobSize } } - var r2 error if rf, ok := ret.Get(2).(func(string, string, int64, int64, int64) error); ok { r2 = rf(repository, digest, blobSize, start, end) } else { @@ -314,6 +341,11 @@ func (_m *mockAdapter) PullManifest(repository string, reference string, acceptt ret := _m.Called(_ca...) var r0 distribution.Manifest + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func(string, string, ...string) (distribution.Manifest, string, error)); ok { + return rf(repository, reference, accepttedMediaTypes...) + } if rf, ok := ret.Get(0).(func(string, string, ...string) distribution.Manifest); ok { r0 = rf(repository, reference, accepttedMediaTypes...) } else { @@ -322,14 +354,12 @@ func (_m *mockAdapter) PullManifest(repository string, reference string, acceptt } } - var r1 string if rf, ok := ret.Get(1).(func(string, string, ...string) string); ok { r1 = rf(repository, reference, accepttedMediaTypes...) } else { r1 = ret.Get(1).(string) } - var r2 error if rf, ok := ret.Get(2).(func(string, string, ...string) error); ok { r2 = rf(repository, reference, accepttedMediaTypes...) } else { @@ -358,20 +388,23 @@ func (_m *mockAdapter) PushBlobChunk(repository string, digest string, size int6 ret := _m.Called(repository, digest, size, chunk, start, end, location) var r0 string + var r1 int64 + var r2 error + if rf, ok := ret.Get(0).(func(string, string, int64, io.Reader, int64, int64, string) (string, int64, error)); ok { + return rf(repository, digest, size, chunk, start, end, location) + } if rf, ok := ret.Get(0).(func(string, string, int64, io.Reader, int64, int64, string) string); ok { r0 = rf(repository, digest, size, chunk, start, end, location) } else { r0 = ret.Get(0).(string) } - var r1 int64 if rf, ok := ret.Get(1).(func(string, string, int64, io.Reader, int64, int64, string) int64); ok { r1 = rf(repository, digest, size, chunk, start, end, location) } else { r1 = ret.Get(1).(int64) } - var r2 error if rf, ok := ret.Get(2).(func(string, string, int64, io.Reader, int64, int64, string) error); ok { r2 = rf(repository, digest, size, chunk, start, end, location) } else { @@ -386,13 +419,16 @@ func (_m *mockAdapter) PushManifest(repository string, reference string, mediaTy ret := _m.Called(repository, reference, mediaType, payload) var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(string, string, string, []byte) (string, error)); ok { + return rf(repository, reference, mediaType, payload) + } if rf, ok := ret.Get(0).(func(string, string, string, []byte) string); ok { r0 = rf(repository, reference, mediaType, payload) } else { r0 = ret.Get(0).(string) } - var r1 error if rf, ok := ret.Get(1).(func(string, string, string, []byte) error); ok { r1 = rf(repository, reference, mediaType, payload) } else { diff --git a/src/controller/replication/mock_flow_controller_test.go b/src/controller/replication/mock_flow_controller_test.go index 7c8305b948e7..d144ee6d2aeb 100644 --- a/src/controller/replication/mock_flow_controller_test.go +++ b/src/controller/replication/mock_flow_controller_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package replication diff --git a/src/controller/robot/controller.go b/src/controller/robot/controller.go index 865bed5d003d..5e5c75bd65d1 100644 --- a/src/controller/robot/controller.go +++ b/src/controller/robot/controller.go @@ -152,7 +152,7 @@ func (d *controller) Update(ctx context.Context, r *Robot, option *Option) error } // update the permission if option != nil && option.WithPermission { - if err := d.rbacMgr.DeletePermissionsByRole(ctx, ROBOTTYPE, r.ID); err != nil { + if err := d.rbacMgr.DeletePermissionsByRole(ctx, ROBOTTYPE, r.ID); err != nil && !errors.IsNotFoundErr(err) { return err } if err := d.createPermission(ctx, r); err != nil { diff --git a/src/controller/scan/base_controller.go b/src/controller/scan/base_controller.go index 3a28bacbe23b..5eda6b723c6a 100644 --- a/src/controller/scan/base_controller.go +++ b/src/controller/scan/base_controller.go @@ -755,7 +755,7 @@ func (bc *basicController) DeleteReports(ctx context.Context, digests ...string) return nil } -func (bc *basicController) GetVulnerable(ctx context.Context, artifact *ar.Artifact, allowlist allowlist.CVESet) (*Vulnerable, error) { +func (bc *basicController) GetVulnerable(ctx context.Context, artifact *ar.Artifact, allowlist allowlist.CVESet, allowlistIsExpired bool) (*Vulnerable, error) { if artifact == nil { return nil, errors.New("no way to get vulnerable for nil artifact") } @@ -816,7 +816,7 @@ func (bc *basicController) GetVulnerable(ctx context.Context, artifact *ar.Artif var severity vuln.Severity for _, v := range vuls { - if allowlist.Contains(v.ID) { + if !allowlistIsExpired && allowlist.Contains(v.ID) { // Append the by passed CVEs specified in the allowlist vulnerable.CVEBypassed = append(vulnerable.CVEBypassed, v.ID) diff --git a/src/controller/scan/controller.go b/src/controller/scan/controller.go index 47b47ccdb164..5029a7dcc3fe 100644 --- a/src/controller/scan/controller.go +++ b/src/controller/scan/controller.go @@ -120,9 +120,11 @@ type Controller interface { // Arguments: // ctx context.Context : the context for this method // artifact *artifact.Artifact : artifact to be scanned + // allowlist map[string]struct{} : the set of CVE id of the items in the allowlist + // allowlistIsExpired bool : whether the allowlist is expired // // Returns // *Vulnerable : the vulnerable // error : non nil error if any errors occurred - GetVulnerable(ctx context.Context, artifact *artifact.Artifact, allowlist allowlist.CVESet) (*Vulnerable, error) + GetVulnerable(ctx context.Context, artifact *artifact.Artifact, allowlist allowlist.CVESet, allowlistIsExpired bool) (*Vulnerable, error) } diff --git a/src/controller/tag/controller.go b/src/controller/tag/controller.go index 7351ebbee525..1d59302c6a3a 100644 --- a/src/controller/tag/controller.go +++ b/src/controller/tag/controller.go @@ -16,6 +16,7 @@ package tag import ( "context" + "regexp" "time" "github.com/goharbor/harbor/src/common/utils" @@ -35,7 +36,8 @@ import ( var ( // Ctl is a global tag controller instance - Ctl = NewController() + Ctl = NewController() + tagNamePattern = regexp.MustCompile(`^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$`) ) // Controller manages the tags @@ -160,9 +162,18 @@ func (c *controller) Get(ctx context.Context, id int64, option *Option) (tag *Ta // Create ... func (c *controller) Create(ctx context.Context, tag *Tag) (id int64, err error) { + if !isValidTag(tag.Name) { + return 0, errors.BadRequestError(errors.Errorf("invalid tag name: %s", tag.Name)) + } return c.tagMgr.Create(ctx, &(tag.Tag)) } +func isValidTag(name string) bool { + // tag name should follow OCI spec + // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pull + return tagNamePattern.MatchString(name) +} + // Update ... func (c *controller) Update(ctx context.Context, tag *Tag, props ...string) (err error) { return c.tagMgr.Update(ctx, &tag.Tag, props...) diff --git a/src/controller/tag/controller_test.go b/src/controller/tag/controller_test.go index fafddda8c1bf..95d31bdd8bf5 100644 --- a/src/controller/tag/controller_test.go +++ b/src/controller/tag/controller_test.go @@ -236,3 +236,29 @@ func (c *controllerTestSuite) TestAssembleTag() { func TestControllerTestSuite(t *testing.T) { suite.Run(t, &controllerTestSuite{}) } + +func Test_isValidTag(t *testing.T) { + type args struct { + name string + } + tests := []struct { + name string + args args + want bool + }{ + {"normal", args{`latest`}, true}, + {"invalid_char", args{`latest&delete`}, false}, + {"invalid_start", args{`-abc`}, false}, + {"invalid_start_&", args{`&asdf`}, false}, + {"valid_start", args{`_abc`}, true}, + {"pure_number", args{`123456`}, true}, + {"empty", args{` `}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isValidTag(tt.args.name); got != tt.want { + t.Errorf("isValidTag() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/src/controller/webhook/controller.go b/src/controller/webhook/controller.go new file mode 100644 index 000000000000..663969348279 --- /dev/null +++ b/src/controller/webhook/controller.go @@ -0,0 +1,195 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package webhook + +import ( + "context" + "time" + + "github.com/goharbor/harbor/src/jobservice/job" + "github.com/goharbor/harbor/src/lib/errors" + "github.com/goharbor/harbor/src/lib/q" + "github.com/goharbor/harbor/src/pkg/notification/policy" + "github.com/goharbor/harbor/src/pkg/notification/policy/model" + "github.com/goharbor/harbor/src/pkg/task" +) + +var ( + // Ctl is a global webhook controller instance + Ctl = NewController() + + // webhookJobVendors represents webhook(http) or slack. + webhookJobVendors = q.NewOrList([]interface{}{job.WebhookJobVendorType, job.SlackJobVendorType}) +) + +type Controller interface { + // CreatePolicy creates webhook policy + CreatePolicy(ctx context.Context, policy *model.Policy) (int64, error) + // ListPolicies lists webhook policies filter by query + ListPolicies(ctx context.Context, query *q.Query) ([]*model.Policy, error) + // CountPolicies counts webhook policies filter by query + CountPolicies(ctx context.Context, query *q.Query) (int64, error) + // GetPolicy gets webhook policy by specified ID + GetPolicy(ctx context.Context, id int64) (*model.Policy, error) + // UpdatePolicy updates webhook policy + UpdatePolicy(ctx context.Context, policy *model.Policy) error + // DeletePolicy deletes webhook policy by specified ID + DeletePolicy(ctx context.Context, policyID int64) error + // GetRelatedPolices gets related policies by the input project id and event type + GetRelatedPolices(ctx context.Context, projectID int64, eventType string) ([]*model.Policy, error) + + // CountExecutions counts executions under the webhook policy + CountExecutions(ctx context.Context, policyID int64, query *q.Query) (int64, error) + // ListExecutions lists executions under the webhook policy + ListExecutions(ctx context.Context, policyID int64, query *q.Query) ([]*task.Execution, error) + // CountTasks counts tasks under the webhook execution + CountTasks(ctx context.Context, execID int64, query *q.Query) (int64, error) + // ListTasks lists tasks under the webhook execution + ListTasks(ctx context.Context, execID int64, query *q.Query) ([]*task.Task, error) + // GetTask gets the webhook task by the specified ID + GetTask(ctx context.Context, taskID int64) (*task.Task, error) + // GetTaskLog gets task log + GetTaskLog(ctx context.Context, taskID int64) ([]byte, error) + + // GetLastTriggerTime gets policy last trigger time group by event type + GetLastTriggerTime(ctx context.Context, eventType string, policyID int64) (time.Time, error) +} + +type controller struct { + policyMgr policy.Manager + execMgr task.ExecutionManager + taskMgr task.Manager +} + +func NewController() Controller { + return &controller{ + policyMgr: policy.Mgr, + execMgr: task.ExecMgr, + taskMgr: task.Mgr, + } +} + +func (c *controller) CreatePolicy(ctx context.Context, policy *model.Policy) (int64, error) { + return c.policyMgr.Create(ctx, policy) +} + +func (c *controller) ListPolicies(ctx context.Context, query *q.Query) ([]*model.Policy, error) { + return c.policyMgr.List(ctx, query) +} + +func (c *controller) CountPolicies(ctx context.Context, query *q.Query) (int64, error) { + return c.policyMgr.Count(ctx, query) +} + +func (c *controller) GetPolicy(ctx context.Context, id int64) (*model.Policy, error) { + return c.policyMgr.Get(ctx, id) +} + +func (c *controller) UpdatePolicy(ctx context.Context, policy *model.Policy) error { + return c.policyMgr.Update(ctx, policy) +} + +func (c *controller) DeletePolicy(ctx context.Context, policyID int64) error { + // delete executions under the webhook policy, + // there are two vendor types(webhook & slack) needs to be deleted. + if err := c.execMgr.DeleteByVendor(ctx, job.WebhookJobVendorType, policyID); err != nil { + return errors.Wrapf(err, "failed to delete executions for webhook of policy %d", policyID) + } + if err := c.execMgr.DeleteByVendor(ctx, job.SlackJobVendorType, policyID); err != nil { + return errors.Wrapf(err, "failed to delete executions for slack of policy %d", policyID) + } + + return c.policyMgr.Delete(ctx, policyID) +} + +func (c *controller) GetRelatedPolices(ctx context.Context, projectID int64, eventType string) ([]*model.Policy, error) { + return c.policyMgr.GetRelatedPolices(ctx, projectID, eventType) +} + +func (c *controller) CountExecutions(ctx context.Context, policyID int64, query *q.Query) (int64, error) { + return c.execMgr.Count(ctx, buildExecutionQuery(policyID, query)) +} + +func (c *controller) ListExecutions(ctx context.Context, policyID int64, query *q.Query) ([]*task.Execution, error) { + return c.execMgr.List(ctx, buildExecutionQuery(policyID, query)) +} + +func (c *controller) CountTasks(ctx context.Context, execID int64, query *q.Query) (int64, error) { + return c.taskMgr.Count(ctx, buildTaskQuery(execID, query)) +} + +func (c *controller) ListTasks(ctx context.Context, execID int64, query *q.Query) ([]*task.Task, error) { + return c.taskMgr.List(ctx, buildTaskQuery(execID, query)) +} + +func (c *controller) GetTask(ctx context.Context, taskID int64) (*task.Task, error) { + query := q.New(q.KeyWords{ + "id": taskID, + "vendor_type": webhookJobVendors, + }) + tasks, err := c.taskMgr.List(ctx, query) + if err != nil { + return nil, err + } + + if len(tasks) == 0 { + return nil, errors.New(nil).WithCode(errors.NotFoundCode). + WithMessage("webhook task %d not found", taskID) + } + return tasks[0], nil +} + +func (c *controller) GetTaskLog(ctx context.Context, taskID int64) ([]byte, error) { + // ensure the webhook task exist + _, err := c.GetTask(ctx, taskID) + if err != nil { + return nil, err + } + + return c.taskMgr.GetLog(ctx, taskID) +} + +func buildExecutionQuery(policyID int64, query *q.Query) *q.Query { + query = q.MustClone(query) + query.Keywords["vendor_type"] = webhookJobVendors + query.Keywords["vendor_id"] = policyID + return query +} + +func buildTaskQuery(execID int64, query *q.Query) *q.Query { + query = q.MustClone(query) + query.Keywords["vendor_type"] = webhookJobVendors + query.Keywords["execution_id"] = execID + return query +} + +func (c *controller) GetLastTriggerTime(ctx context.Context, eventType string, policyID int64) (time.Time, error) { + query := q.New(q.KeyWords{ + "vendor_type": webhookJobVendors, + "vendor_id": policyID, + "ExtraAttrs.type": eventType, + }) + // fetch the latest execution sort by start_time + execs, err := c.execMgr.List(ctx, query.First(q.NewSort("start_time", true))) + if err != nil { + return time.Time{}, err + } + + if len(execs) > 0 { + return execs[0].StartTime, nil + } + + return time.Time{}, nil +} diff --git a/src/controller/webhook/controller_test.go b/src/controller/webhook/controller_test.go new file mode 100644 index 000000000000..7435289363a9 --- /dev/null +++ b/src/controller/webhook/controller_test.go @@ -0,0 +1,166 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package webhook + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/goharbor/harbor/src/lib/q" + "github.com/goharbor/harbor/src/pkg/notification/policy/model" + task_model "github.com/goharbor/harbor/src/pkg/task" + "github.com/goharbor/harbor/src/testing/mock" + "github.com/goharbor/harbor/src/testing/pkg/notification/policy" + "github.com/goharbor/harbor/src/testing/pkg/task" + "github.com/stretchr/testify/suite" +) + +type controllerTestSuite struct { + suite.Suite + ctl *controller + policyMgr *policy.Manager + taskMgr *task.Manager + execMgr *task.ExecutionManager +} + +func TestControllerTestSuite(t *testing.T) { + suite.Run(t, &controllerTestSuite{}) +} + +func (c *controllerTestSuite) SetupTest() { + c.policyMgr = &policy.Manager{} + c.taskMgr = &task.Manager{} + c.execMgr = &task.ExecutionManager{} + c.ctl = &controller{ + policyMgr: c.policyMgr, + taskMgr: c.taskMgr, + execMgr: c.execMgr, + } +} + +func (c *controllerTestSuite) TestCreatePolicy() { + c.policyMgr.On("Create", mock.Anything, mock.Anything).Return(int64(1), nil) + id, err := c.ctl.CreatePolicy(context.TODO(), &model.Policy{Name: "test-policy"}) + c.NoError(err) + c.Equal(int64(1), id) +} + +func (c *controllerTestSuite) TestListPolicies() { + c.policyMgr.On("List", mock.Anything, mock.Anything).Return([]*model.Policy{{Name: "test-policy-1"}, {Name: "test-policy-2"}}, nil) + policies, err := c.ctl.ListPolicies(context.TODO(), q.MustClone(nil)) + c.NoError(err) + c.Len(policies, 2) +} + +func (c *controllerTestSuite) TestCountPolicies() { + c.policyMgr.On("Count", mock.Anything, mock.Anything).Return(int64(3), nil) + cnt, err := c.ctl.CountPolicies(context.TODO(), q.MustClone(nil)) + c.NoError(err) + c.Equal(int64(3), cnt) +} + +func (c *controllerTestSuite) TestGetPolicy() { + c.policyMgr.On("Get", mock.Anything, mock.Anything).Return(&model.Policy{Name: "test-policy"}, nil) + p, err := c.ctl.GetPolicy(context.TODO(), 1) + c.NoError(err) + c.Equal("test-policy", p.Name) +} + +func (c *controllerTestSuite) TestUpdatePolicy() { + c.policyMgr.On("Update", mock.Anything, mock.Anything).Return(nil) + err := c.ctl.UpdatePolicy(context.TODO(), &model.Policy{}) + c.NoError(err) +} + +func (c *controllerTestSuite) TestDeletePolicy() { + delExecErr := errors.New("delete executions error") + // failed to delete policy due to webhook executions deletion error + c.execMgr.On("DeleteByVendor", mock.Anything, "WEBHOOK", mock.Anything).Return(delExecErr).Once() + err := c.ctl.DeletePolicy(context.TODO(), 1) + c.ErrorIs(err, delExecErr) + + // failed to delete policy due to slack executions deletion error + c.execMgr.On("DeleteByVendor", mock.Anything, "WEBHOOK", mock.Anything).Return(nil).Once() + c.execMgr.On("DeleteByVendor", mock.Anything, "SLACK", mock.Anything).Return(delExecErr).Once() + err = c.ctl.DeletePolicy(context.TODO(), 1) + c.ErrorIs(err, delExecErr) + + // successfully deletion for all + c.execMgr.On("DeleteByVendor", mock.Anything, mock.Anything, mock.Anything).Return(nil) + c.policyMgr.On("Delete", mock.Anything, mock.Anything).Return(nil) + err = c.ctl.DeletePolicy(context.TODO(), 1) + c.NoError(err) +} + +func (c *controllerTestSuite) TestGetRelatedPolices() { + c.policyMgr.On("GetRelatedPolices", mock.Anything, mock.Anything, mock.Anything).Return([]*model.Policy{{Name: "test-policy-1"}, {Name: "test-policy-2"}}, nil) + policies, err := c.ctl.GetRelatedPolices(context.TODO(), 1, "mock") + c.NoError(err) + c.Len(policies, 2) +} + +func (c *controllerTestSuite) TestCountExecutions() { + c.execMgr.On("Count", mock.Anything, mock.Anything).Return(int64(1), nil) + cnt, err := c.ctl.CountExecutions(context.TODO(), 1, q.MustClone(nil)) + c.NoError(err) + c.Equal(int64(1), cnt) +} + +func (c *controllerTestSuite) TestListExecutions() { + c.execMgr.On("List", mock.Anything, mock.Anything).Return([]*task_model.Execution{{ID: 1, VendorType: "WEBHOOK", VendorID: 1}}, nil) + execs, err := c.ctl.ListExecutions(context.TODO(), 1, q.MustClone(nil)) + c.NoError(err) + c.Len(execs, 1) +} + +func (c *controllerTestSuite) TestCountTasks() { + c.taskMgr.On("Count", mock.Anything, mock.Anything).Return(int64(1), nil) + cnt, err := c.ctl.CountTasks(context.TODO(), 1, q.MustClone(nil)) + c.NoError(err) + c.Equal(int64(1), cnt) +} + +func (c *controllerTestSuite) TestListTasks() { + c.taskMgr.On("List", mock.Anything, mock.Anything).Return([]*task_model.Task{{ID: 1, ExecutionID: 1}}, nil) + tasks, err := c.ctl.ListTasks(context.TODO(), 1, q.MustClone(nil)) + c.NoError(err) + c.Len(tasks, 1) +} + +func (c *controllerTestSuite) TestGetTask() { + c.taskMgr.On("List", mock.Anything, mock.Anything).Return([]*task_model.Task{{ID: 1, ExecutionID: 1}}, nil) + task, err := c.ctl.GetTask(context.TODO(), 1) + c.NoError(err) + c.Equal(int64(1), task.ID) + c.Equal(int64(1), task.ExecutionID) +} + +func (c *controllerTestSuite) TestGetTaskLog() { + c.taskMgr.On("List", mock.Anything, mock.Anything).Return([]*task_model.Task{{ID: 1, ExecutionID: 1}}, nil) + c.taskMgr.On("GetLog", mock.Anything, mock.Anything).Return([]byte("test logs"), nil) + logs, err := c.ctl.GetTaskLog(context.TODO(), 1) + c.NoError(err) + c.Equal("test logs", string(logs)) +} + +func (c *controllerTestSuite) TestGetLastTriggerTime() { + now := time.Now() + c.execMgr.On("List", mock.Anything, mock.Anything).Return([]*task_model.Execution{{ID: 1, StartTime: now}}, nil) + time, err := c.ctl.GetLastTriggerTime(context.TODO(), "mock", 1) + c.NoError(err) + c.Equal(now, time) +} diff --git a/src/core/controllers/oidc.go b/src/core/controllers/oidc.go index 1f4f040b39f6..6758da3327dd 100644 --- a/src/core/controllers/oidc.go +++ b/src/core/controllers/oidc.go @@ -58,7 +58,7 @@ func (oc *OIDCController) Prepare() { // RedirectLogin redirect user's browser to OIDC provider's login page func (oc *OIDCController) RedirectLogin() { state := utils.GenerateRandomString() - url, err := oidc.AuthCodeURL(state) + url, err := oidc.AuthCodeURL(oc.Context(), state) if err != nil { oc.SendInternalServerError(err) return diff --git a/src/core/middlewares/middlewares.go b/src/core/middlewares/middlewares.go index 62a1c13d228f..2d3486a0bbde 100644 --- a/src/core/middlewares/middlewares.go +++ b/src/core/middlewares/middlewares.go @@ -70,10 +70,8 @@ var ( middleware.MethodAndPathSkipper(http.MethodPost, match("^/service/notifications/jobs/adminjob/"+numericRegexp.String())), middleware.MethodAndPathSkipper(http.MethodPost, match("^/service/notifications/jobs/replication/"+numericRegexp.String())), middleware.MethodAndPathSkipper(http.MethodPost, match("^/service/notifications/jobs/replication/task/"+numericRegexp.String())), - middleware.MethodAndPathSkipper(http.MethodPost, match("^/service/notifications/jobs/webhook/"+numericRegexp.String())), middleware.MethodAndPathSkipper(http.MethodPost, match("^/service/notifications/jobs/retention/task/"+numericRegexp.String())), middleware.MethodAndPathSkipper(http.MethodPost, match("^/service/notifications/jobs/schedules/"+numericRegexp.String())), - middleware.MethodAndPathSkipper(http.MethodPost, match("^/service/notifications/jobs/webhook/"+numericRegexp.String())), pingSkipper, } ) diff --git a/src/core/service/notifications/base.go b/src/core/service/notifications/base.go deleted file mode 100644 index faa07b2916b7..000000000000 --- a/src/core/service/notifications/base.go +++ /dev/null @@ -1,8 +0,0 @@ -package notifications - -import "github.com/goharbor/harbor/src/core/api" - -// BaseHandler extracts the common funcs, all notification handlers should shadow this struct -type BaseHandler struct { - api.BaseController -} diff --git a/src/core/service/notifications/jobs/handler.go b/src/core/service/notifications/jobs/handler.go deleted file mode 100644 index fb7512ac1278..000000000000 --- a/src/core/service/notifications/jobs/handler.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2018 Project Harbor Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package jobs - -import ( - "encoding/json" - "time" - - "github.com/goharbor/harbor/src/common/job" - "github.com/goharbor/harbor/src/common/models" - "github.com/goharbor/harbor/src/core/service/notifications" - jjob "github.com/goharbor/harbor/src/jobservice/job" - "github.com/goharbor/harbor/src/lib/log" - "github.com/goharbor/harbor/src/lib/orm" - "github.com/goharbor/harbor/src/pkg/notification" - "github.com/goharbor/harbor/src/pkg/notification/job/model" -) - -var statusMap = map[string]string{ - job.JobServiceStatusPending: models.JobPending, - job.JobServiceStatusScheduled: models.JobScheduled, - job.JobServiceStatusRunning: models.JobRunning, - job.JobServiceStatusStopped: models.JobStopped, - job.JobServiceStatusError: models.JobError, - job.JobServiceStatusSuccess: models.JobFinished, -} - -// Handler handles request on /service/notifications/jobs/*, which listens to the webhook of jobservice. -type Handler struct { - notifications.BaseHandler - id int64 - status string - rawStatus string - checkIn string - revision int64 - trackID string - change *jjob.StatusChange -} - -// Prepare ... -func (h *Handler) Prepare() { - h.BaseHandler.Prepare() - h.trackID = h.GetStringFromPath(":uuid") - if len(h.trackID) == 0 { - id, err := h.GetInt64FromPath(":id") - if err != nil { - log.Errorf("Failed to get job ID, error: %v", err) - // Avoid job service from resending... - h.Abort("200") - return - } - h.id = id - } - - var data jjob.StatusChange - err := json.Unmarshal(h.Ctx.Input.CopyBody(1<<32), &data) - if err != nil { - log.Errorf("Failed to decode job status change with error: %v", err) - h.Abort("200") - return - } - h.change = &data - h.rawStatus = data.Status - status, ok := statusMap[data.Status] - if !ok { - log.Debugf("drop the job status update event: job id-%d/track id-%s, status-%s", h.id, h.trackID, status) - h.Abort("200") - return - } - h.status = status - h.checkIn = data.CheckIn - if data.Metadata != nil { - h.revision = data.Metadata.Revision - } -} - -// HandleNotificationJob handles the hook of notification job -func (h *Handler) HandleNotificationJob() { - log.Debugf("received notification job status update event: job-%d, status-%s", h.id, h.status) - if err := notification.JobMgr.Update(orm.Context(), &model.Job{ - ID: h.id, - Status: h.status, - UpdateTime: time.Now(), - }, "Status", "UpdateTime"); err != nil { - log.Errorf("Failed to update notification job status, id: %d, status: %s", h.id, h.status) - h.SendInternalServerError(err) - return - } -} diff --git a/src/go.mod b/src/go.mod index 5986daa2dc39..3c984aebdec1 100644 --- a/src/go.mod +++ b/src/go.mod @@ -46,7 +46,7 @@ require ( github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/olekukonko/tablewriter v0.0.5 github.com/opencontainers/go-digest v1.0.0 - github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 + github.com/opencontainers/image-spec v1.1.0-rc2 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.13.0 github.com/robfig/cron v1.0.0 // indirect @@ -84,7 +84,6 @@ require ( require ( cloud.google.com/go v0.99.0 // indirect github.com/Azure/azure-sdk-for-go v37.2.0+incompatible // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.27 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect @@ -95,6 +94,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect github.com/Masterminds/semver/v3 v3.1.1 // indirect + github.com/Microsoft/go-winio v0.5.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/Unknwon/goconfig v0.0.0-20160216183935-5f601ca6ef4d // indirect @@ -105,24 +105,14 @@ require ( github.com/bugsnag/panicwrap v1.2.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cloudflare/cfssl v0.0.0-20190510060611-9c027c93ba9e // indirect - github.com/containerd/containerd v1.6.6 // indirect - github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/denverdino/aliyungo v0.0.0-20191227032621-df38c6fa730c // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dnaeon/go-vcr v1.2.0 // indirect - github.com/docker/cli v20.10.17+incompatible // indirect - github.com/docker/docker v20.10.17+incompatible // indirect - github.com/docker/docker-credential-helpers v0.6.4 // indirect github.com/docker/go v0.0.0-20160303222718-d30aec9fd63c // indirect - github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect - github.com/docker/go-units v0.4.0 // indirect - github.com/emicklei/go-restful/v3 v3.8.0 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/felixge/httpsnoop v1.0.2 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect - github.com/go-errors/errors v1.0.1 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.19.10 // indirect @@ -131,22 +121,16 @@ require ( github.com/go-stack/stack v1.8.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/btree v1.0.1 // indirect github.com/google/certificate-transparency-go v1.0.21 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/securecookie v1.1.1 // indirect - github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgerrcode v0.0.0-20201024163028-a0d42d470451 // indirect github.com/jackc/pgio v1.0.0 // indirect @@ -157,25 +141,15 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.13.6 // indirect - github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/moby/locker v1.0.1 // indirect - github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect - github.com/morikuni/aec v1.0.0 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pelletier/go-toml v1.9.3 // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.37.0 // indirect @@ -185,16 +159,11 @@ require ( github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/afero v1.6.0 // indirect github.com/spf13/cast v1.4.1 // indirect - github.com/spf13/cobra v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.4.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect github.com/vmihailenco/tagparser v0.1.2 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect - github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/xlab/treeprint v1.1.0 // indirect go.mongodb.org/mongo-driver v1.7.0 // indirect go.opentelemetry.io/contrib v0.22.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 // indirect @@ -202,9 +171,7 @@ require ( go.opentelemetry.io/otel/internal/metric v0.22.0 // indirect go.opentelemetry.io/otel/metric v0.22.0 // indirect go.opentelemetry.io/proto/otlp v0.11.0 // indirect - go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect go.uber.org/atomic v1.7.0 // indirect - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect golang.org/x/sys v0.2.0 // indirect golang.org/x/term v0.2.0 // indirect google.golang.org/api v0.61.0 // indirect @@ -220,15 +187,9 @@ require ( gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.25.2 // indirect - k8s.io/cli-runtime v0.25.2 // indirect k8s.io/klog/v2 v2.70.1 // indirect - k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect - oras.land/oras-go v1.2.0 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/kustomize/api v0.12.1 // indirect - sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/src/go.sum b/src/go.sum index 234a2e0f6079..bbc0763fde13 100644 --- a/src/go.sum +++ b/src/go.sum @@ -99,6 +99,7 @@ github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JP github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= @@ -107,7 +108,6 @@ github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2 github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.9.3 h1:k371PzBuRrz2b+ebGuI2nVgVhgsVX60jMfSw80NECxo= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -206,7 +206,6 @@ github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTS github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bugsnag/bugsnag-go v1.5.2 h1:fdaGJJEReigPzSE6HajOhpJwE2IEP/TdHDHXKGeOJtc= github.com/bugsnag/bugsnag-go v1.5.2/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= @@ -267,7 +266,6 @@ github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4S github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= @@ -287,8 +285,6 @@ github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09Zvgq github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0= -github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -358,20 +354,15 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= -github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -389,22 +380,15 @@ github.com/dhui/dktest v0.3.7 h1:jWjWgHAPDAdqgUr7lAsB3bqB2DKWC3OaA+isfekjRew= github.com/dhui/dktest v0.3.7/go.mod h1:nYMOkafiA07WchSwKnKFUSbGMb2hMm5DrCGiXYG6gwM= github.com/distribution/distribution v2.8.1+incompatible h1:8iXUoOqRPx30bhzIEPUmNIqlmBlWdrieW1bqr6LrX30= github.com/distribution/distribution v2.8.1+incompatible/go.mod h1:EgLm2NgWtdKgzF9NpMzUKgzmR7AMmb0VQi2B+ZzDRjc= -github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= -github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v20.10.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= -github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= -github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/go v0.0.0-20160303222718-d30aec9fd63c h1:Ggg7IiOtghyZzn3ozi31kPHpV6qSjMgmesXaWCijYNM= github.com/docker/go v0.0.0-20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= @@ -427,7 +411,6 @@ github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkg github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -440,8 +423,6 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= @@ -467,8 +448,6 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= @@ -637,7 +616,6 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -673,13 +651,10 @@ github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNu github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -712,8 +687,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20210715191844-86eeefc3e471/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -744,7 +717,6 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/graph-gophers/dataloader v5.0.0+incompatible h1:R+yjsbrNq1Mo3aPG+Z/EKYrXrXXUNJHOgbRt+U6jOug= github.com/graph-gophers/dataloader v5.0.0+incompatible/go.mod h1:jk4jk0c5ZISbKaMe8WsVopGB5/15GvGHMdMdPtwlRp4= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -798,9 +770,7 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= @@ -917,7 +887,6 @@ github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -943,8 +912,6 @@ github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= @@ -991,8 +958,6 @@ github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -1005,13 +970,9 @@ github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= @@ -1024,8 +985,6 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= @@ -1090,8 +1049,8 @@ github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go. github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= @@ -1128,9 +1087,7 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= @@ -1210,7 +1167,6 @@ github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= @@ -1219,7 +1175,6 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik= github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= @@ -1257,8 +1212,6 @@ github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -1272,7 +1225,6 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -1329,16 +1281,10 @@ github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+ github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= -github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1346,9 +1292,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1372,7 +1315,6 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/contrib v0.22.0 h1:0F7gDEjgb1WGn4ODIjaCAg75hmqF+UN0LiVgwxsCodc= go.opentelemetry.io/contrib v0.22.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2bvnvzBlGM= @@ -1416,8 +1358,6 @@ go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaT go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU= go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1598,8 +1538,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1634,7 +1572,6 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1844,7 +1781,6 @@ google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1m google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= @@ -1961,10 +1897,8 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= helm.sh/helm/v3 v3.10.3 h1:wL7IUZ7Zyukm5Kz0OUmIFZgKHuAgByCrUcJBtY0kDyw= helm.sh/helm/v3 v3.10.3/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= @@ -1981,8 +1915,6 @@ k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8= k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0= -k8s.io/apiextensions-apiserver v0.25.2 h1:8uOQX17RE7XL02ngtnh3TgifY7EhekpK+/piwzQNnBo= -k8s.io/apiextensions-apiserver v0.25.2/go.mod h1:iRwwRDlWPfaHhuBfQ0WMa5skdQfrE18QXJaJvIDLvE8= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= @@ -1991,8 +1923,6 @@ k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHw k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/cli-runtime v0.25.2 h1:XOx+SKRjBpYMLY/J292BHTkmyDffl/qOx3YSuFZkTuc= -k8s.io/cli-runtime v0.25.2/go.mod h1:OQx3+/0st6x5YpkkJQlEWLC73V0wHsOFMC1/roxV8Oc= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= @@ -2012,7 +1942,6 @@ k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= @@ -2043,8 +1972,6 @@ modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= -oras.land/oras-go v1.2.0 h1:yoKosVIbsPoFMqAIFHTnrmOuafHal+J/r+I5bdbVWu4= -oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= @@ -2053,10 +1980,6 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyz sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= -sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= -sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= -sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= diff --git a/src/jobservice/config/config.go b/src/jobservice/config/config.go index 838454d3e9ff..7fc7f29feda4 100644 --- a/src/jobservice/config/config.go +++ b/src/jobservice/config/config.go @@ -23,7 +23,7 @@ import ( "strconv" "strings" - yaml "gopkg.in/yaml.v2" + "gopkg.in/yaml.v2" "github.com/goharbor/harbor/src/jobservice/common/utils" "github.com/goharbor/harbor/src/lib/log" @@ -82,6 +82,9 @@ type Configuration struct { // Metric configurations Metric *MetricConfig `yaml:"metric,omitempty"` + + // MaxLogSizeReturnedMB is the max size of log returned by job log API + MaxLogSizeReturnedMB int `yaml:"max_retrieve_size_mb,omitempty"` } // HTTPSConfig keeps additional configurations when using https protocol diff --git a/src/jobservice/config/config_test.go b/src/jobservice/config/config_test.go index 158a0aa75bb1..df9b41bd3b41 100644 --- a/src/jobservice/config/config_test.go +++ b/src/jobservice/config/config_test.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -86,6 +86,7 @@ func (suite *ConfigurationTestSuite) TestDefaultConfig() { err := DefaultConfig.Load("../config_test.yml", true) require.Nil(suite.T(), err, "load config from yaml file, expect nil error but got error '%s'", err) + assert.Equal(suite.T(), 10, DefaultConfig.MaxLogSizeReturnedMB, "expect max log size returned 10MB but got %d", DefaultConfig.MaxLogSizeReturnedMB) redisURL := DefaultConfig.PoolConfig.RedisPoolCfg.RedisURL assert.Equal(suite.T(), "redis://localhost:6379", redisURL, "expect redisURL '%s' but got '%s'", "redis://localhost:6379", redisURL) diff --git a/src/jobservice/config_test.yml b/src/jobservice/config_test.yml index c1f2ce4336d1..c8d7bb45afe5 100644 --- a/src/jobservice/config_test.yml +++ b/src/jobservice/config_test.yml @@ -39,3 +39,5 @@ job_loggers: loggers: - name: "STD_OUTPUT" # Same with above level: "DEBUG" + +max_retrieve_size_mb: 10 \ No newline at end of file diff --git a/src/jobservice/logger/getter/db_getter.go b/src/jobservice/logger/getter/db_getter.go index 0e53b3156e99..8d4f22fbcae7 100644 --- a/src/jobservice/logger/getter/db_getter.go +++ b/src/jobservice/logger/getter/db_getter.go @@ -30,5 +30,15 @@ func (dbg *DBGetter) Retrieve(logID string) ([]byte, error) { return nil, errs.NoObjectFoundError(fmt.Sprintf("log entity: %s", logID)) } - return []byte(jobLog.Content), nil + sz := int64(len(jobLog.Content)) + var buf []byte + sizeLimit := logSizeLimit() + if sizeLimit <= 0 { + buf = []byte(jobLog.Content) + return buf, nil + } + if sz > sizeLimit { + buf = []byte(jobLog.Content[sz-sizeLimit:]) + } + return buf, nil } diff --git a/src/jobservice/logger/getter/file_getter.go b/src/jobservice/logger/getter/file_getter.go index 48684840cb57..ef9c52d6a86f 100644 --- a/src/jobservice/logger/getter/file_getter.go +++ b/src/jobservice/logger/getter/file_getter.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/goharbor/harbor/src/jobservice/common/utils" + "github.com/goharbor/harbor/src/jobservice/config" "github.com/goharbor/harbor/src/jobservice/errs" ) @@ -21,6 +22,12 @@ type FileGetter struct { func NewFileGetter(baseDir string) *FileGetter { return &FileGetter{baseDir} } +func logSizeLimit() int64 { + if config.DefaultConfig == nil { + return int64(0) + } + return int64(config.DefaultConfig.MaxLogSizeReturnedMB * 1024 * 1024) +} // Retrieve implements @Interface.Retrieve func (fg *FileGetter) Retrieve(logID string) ([]byte, error) { @@ -34,7 +41,7 @@ func (fg *FileGetter) Retrieve(logID string) ([]byte, error) { return nil, errs.NoObjectFoundError(logID) } - return os.ReadFile(fPath) + return tailLogFile(fPath, logSizeLimit()) } func isValidLogID(id string) error { @@ -54,3 +61,42 @@ func isValidLogID(id string) error { return nil } + +func tailLogFile(filename string, limit int64) ([]byte, error) { + fInfo, err := os.Stat(filename) + if err != nil { + return nil, err + } + size := fInfo.Size() + + var sizeToRead int64 + if limit <= 0 { + sizeToRead = size + } else { + sizeToRead = limit + } + if sizeToRead > size { + sizeToRead = size + } + + fi, err := os.Open(filename) + if err != nil { + return nil, err + } + defer fi.Close() + + pos := size - sizeToRead + if pos < 0 { + pos = 0 + } + if pos != 0 { + _, err = fi.Seek(pos, 0) + if err != nil { + return nil, err + } + } + + buf := make([]byte, sizeToRead) + _, err = fi.Read(buf) + return buf, err +} diff --git a/src/jobservice/logger/getter/file_getter_test.go b/src/jobservice/logger/getter/file_getter_test.go index 2a7b745091b5..7dbfb82cf8bb 100644 --- a/src/jobservice/logger/getter/file_getter_test.go +++ b/src/jobservice/logger/getter/file_getter_test.go @@ -44,3 +44,32 @@ func TestLogDataGetter(t *testing.T) { t.Errorf("expect reading 5 bytes but got %d bytes", len(data)) } } + +func Test_tailLogFile(t *testing.T) { + type args struct { + filename string + mbs int64 + } + tests := []struct { + name string + args args + want int + wantErr bool + }{ + {"normal test", args{"testdata/normal.log", 1000}, len(`hello world`), false}, + {"truncated test", args{"testdata/truncated.log", 1000}, 1000, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tailLogFile(tt.args.filename, tt.args.mbs) + if (err != nil) != tt.wantErr { + t.Errorf("tailLogFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + // result should always less than the size limit + if len(got) > tt.want { + t.Errorf("tailLogFile() got = %v, want %v", len(got), tt.want) + } + }) + } +} diff --git a/src/jobservice/logger/getter/testdata/normal.log b/src/jobservice/logger/getter/testdata/normal.log new file mode 100644 index 000000000000..95d09f2b1015 --- /dev/null +++ b/src/jobservice/logger/getter/testdata/normal.log @@ -0,0 +1 @@ +hello world \ No newline at end of file diff --git a/src/jobservice/logger/getter/testdata/truncated.log b/src/jobservice/logger/getter/testdata/truncated.log new file mode 100644 index 000000000000..1207976e5759 --- /dev/null +++ b/src/jobservice/logger/getter/testdata/truncated.log @@ -0,0 +1,179 @@ +Mar 19 12:34:50 172.18.0.1 core[8188]: 2019-03-19T12:34:50Z [INFO] Config path: /etc/core/app.conf +Mar 19 12:34:50 172.18.0.1 core[8188]: 2019-03-19T12:34:50Z [INFO] initializing configurations... +Mar 19 12:34:50 172.18.0.1 core[8188]: 2019-03-19T12:34:50Z [INFO] key path: /etc/core/key +Mar 19 12:34:50 172.18.0.1 core[8188]: 2019-03-19T12:34:50Z [INFO] initializing client for adminserver http://adminserver:8080 ... +Mar 19 12:34:50 172.18.0.1 core[8188]: 2019-03-19T12:34:50Z [ERROR] [utils.go:101]: failed to connect to tcp://adminserver:8080, retry after 2 seconds :dial tcp 172.18.0.4:8080: getsockopt: connection refused +Mar 19 12:34:52 172.18.0.1 core[8188]: 2019-03-19T12:34:52Z [ERROR] [utils.go:101]: failed to connect to tcp://adminserver:8080, retry after 2 seconds :dial tcp 172.18.0.4:8080: getsockopt: connection refused +Mar 19 12:34:54 172.18.0.1 core[8188]: 2019-03-19T12:34:54Z [ERROR] [utils.go:101]: failed to connect to tcp://adminserver:8080, retry after 2 seconds :dial tcp 172.18.0.4:8080: getsockopt: connection refused +Mar 19 12:34:56 172.18.0.1 core[8188]: 2019-03-19T12:34:56Z [ERROR] [utils.go:101]: failed to connect to tcp://adminserver:8080, retry after 2 seconds :dial tcp 172.18.0.4:8080: getsockopt: connection refused +Mar 19 12:34:58 172.18.0.1 core[8188]: 2019-03-19T12:34:58Z [ERROR] [utils.go:101]: failed to connect to tcp://adminserver:8080, retry after 2 seconds :dial tcp 172.18.0.4:8080: getsockopt: connection refused +Mar 19 12:35:00 172.18.0.1 core[8188]: 2019-03-19T12:35:00Z [INFO] initializing the project manager based on local database... +Mar 19 12:35:00 172.18.0.1 core[8188]: 2019-03-19T12:35:00Z [INFO] configurations initialization completed +Mar 19 12:35:00 172.18.0.1 core[8188]: 2019-03-19T12:35:00Z [INFO] Registering database: type-PostgreSQL host-postgresql port-5432 databse-registry sslmode-"disable" +Mar 19 12:35:00 172.18.0.1 core[8188]: 2019-03-19T12:35:00Z [INFO] Register database completed +Mar 19 12:35:00 172.18.0.1 core[8188]: 2019-03-19T12:35:00Z [INFO] User id: 1 updated its encypted password successfully. +Mar 19 12:35:00 172.18.0.1 core[8188]: 2019-03-19T12:35:00Z [INFO] Enable redis cache for chart caching +Mar 19 12:35:00 172.18.0.1 core[8188]: 2019-03-19T12:35:00Z [INFO] API controller for chart repository server is successfully initialized +Mar 19 12:35:00 172.18.0.1 core[8188]: 2019-03-19T12:35:00Z [INFO] initialized clair database +Mar 19 12:35:00 172.18.0.1 core[8188]: 2019-03-19T12:35:00Z [INFO] Because SYNC_REGISTRY set false , no need to sync registry +Mar 19 12:35:00 172.18.0.1 core[8188]: 2019-03-19T12:35:00Z [INFO] Init proxy +Mar 19 12:35:00 172.18.0.1 core[8188]: 2019/03/19 12:35:00 #033[1;34m[I] [asm_amd64.s:2337] http server Running on http://:8080#033[0m +Mar 19 12:35:01 172.18.0.1 core[8188]: 2019/03/19 12:35:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 14.536904ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:35:01 172.18.0.1 core[8188]: 2019/03/19 12:35:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 3.296536ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:35:02 172.18.0.1 core[8188]: 2019/03/19 12:35:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 24.097879ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:35:02 172.18.0.1 core[8188]: 2019/03/19 12:35:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 3.778134ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:35:04 172.18.0.1 core[8188]: 2019/03/19 12:35:04 #033[1;44m[D] [server.go:2619] | 172.18.0.9|#033[42m 200 #033[0m| 3.771354ms| match|#033[44m GET #033[0m /api/configs r:/api/configs#033[0m +Mar 19 12:35:20 172.18.0.1 core[8188]: 2019/03/19 12:35:20 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.5463ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:35:50 172.18.0.1 core[8188]: 2019/03/19 12:35:50 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 8.743277ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:36:02 172.18.0.1 core[8188]: 2019/03/19 12:36:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 8.21945ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:36:02 172.18.0.1 core[8188]: 2019/03/19 12:36:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 3.409655ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:36:02 172.18.0.1 core[8188]: 2019/03/19 12:36:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 30.516066ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:36:02 172.18.0.1 core[8188]: 2019/03/19 12:36:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 5.688545ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:36:20 172.18.0.1 core[8188]: 2019/03/19 12:36:20 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 1.920741ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:36:50 172.18.0.1 core[8188]: 2019/03/19 12:36:50 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 3.017657ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:37:20 172.18.0.1 core[8188]: 2019/03/19 12:37:20 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 14.560241ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:37:20 172.18.0.1 core[8188]: 2019/03/19 12:37:20 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 201 #033[0m| 16.837086ms| match|#033[46m POST #033[0m /api/projects r:/api/projects/#033[0m +Mar 19 12:37:20 172.18.0.1 core[8188]: 2019/03/19 12:37:20 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[43m 401 #033[0m| 4.482415ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:37:20 172.18.0.1 core[8188]: 2019/03/19 12:37:20 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 29.386126ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:37:20 172.18.0.1 core[8188]: 2019/03/19 12:37:20 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 3.408002ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:37:20 172.18.0.1 core[8188]: 2019/03/19 12:37:20 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[43m 401 #033[0m| 5.372253ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:37:20 172.18.0.1 core[8188]: 2019/03/19 12:37:20 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 28.107979ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:37:20 172.18.0.1 core[8188]: 2019/03/19 12:37:20 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 202 #033[0m| 24.829902ms| match|#033[46m POST #033[0m /v2/errandsmoketest/busybox/blobs/uploads/ r:/v2/*#033[0m +Mar 19 12:37:20 172.18.0.1 core[8188]: 2019/03/19 12:37:20 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 226.554µs| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:37:20 172.18.0.1 core[8188]: 2019/03/19 12:37:20 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 202 #033[0m| 169.69419ms| match|#033[42m PATCH #033[0m /v2/errandsmoketest/busybox/blobs/uploads/d8241d14-eef1-476c-80bb-12021374145f r:/v2/*#033[0m +Mar 19 12:37:20 172.18.0.1 core[8188]: 2019/03/19 12:37:20 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 201 #033[0m| 29.202776ms| match|#033[43m PUT #033[0m /v2/errandsmoketest/busybox/blobs/uploads/d8241d14-eef1-476c-80bb-12021374145f r:/v2/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:20 #033[1;44m[D] [server.go:2619] | 172.18.0.3|#033[42m 200 #033[0m| 537.27µs| match|#033[46m POST #033[0m /service/notifications r:/service/notifications#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 12.621509ms| match|#033[45m HEAD #033[0m /v2/errandsmoketest/busybox/blobs/sha256:0ffadd58f2a61468f527cc4f0fc45272ee4a1a428abe014546c89de2aa6a0eb5 r:/v2/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.18.0.3|#033[42m 200 #033[0m| 3.368015ms| match|#033[46m POST #033[0m /service/notifications r:/service/notifications#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[43m 404 #033[0m| 6.342259ms| match|#033[45m HEAD #033[0m /v2/errandsmoketest/busybox/blobs/sha256:6ad733544a6317992a6fac4eb19fe1df577d4dec7529efec28a5bd0edad0fd30 r:/v2/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 202 #033[0m| 11.412618ms| match|#033[46m POST #033[0m /v2/errandsmoketest/busybox/blobs/uploads/ r:/v2/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 202 #033[0m| 43.269666ms| match|#033[42m PATCH #033[0m /v2/errandsmoketest/busybox/blobs/uploads/fcf3c33e-6944-4fd9-bf7d-c217aee65ada r:/v2/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 201 #033[0m| 27.841418ms| match|#033[43m PUT #033[0m /v2/errandsmoketest/busybox/blobs/uploads/fcf3c33e-6944-4fd9-bf7d-c217aee65ada r:/v2/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.18.0.3|#033[42m 200 #033[0m| 348.362µs| match|#033[46m POST #033[0m /service/notifications r:/service/notifications#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 6.197794ms| match|#033[45m HEAD #033[0m /v2/errandsmoketest/busybox/blobs/sha256:6ad733544a6317992a6fac4eb19fe1df577d4dec7529efec28a5bd0edad0fd30 r:/v2/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.18.0.3|#033[42m 200 #033[0m| 316.628µs| match|#033[46m POST #033[0m /service/notifications r:/service/notifications#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 201 #033[0m| 67.158336ms| match|#033[43m PUT #033[0m /v2/errandsmoketest/busybox/manifests/latest r:/v2/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.18.0.3|#033[42m 200 #033[0m| 43.513105ms| match|#033[46m POST #033[0m /service/notifications r:/service/notifications#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019-03-19T12:37:21Z [INFO] Handle notification with topic 'OnPush': notification.OnPushNotification{Image:"errandsmoketest/busybox:latest"} +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.18.0.3|#033[42m 200 #033[0m| 284.756µs| match|#033[46m POST #033[0m /service/notifications r:/service/notifications#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[43m 401 #033[0m| 2.531966ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 28.124757ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 4.690525ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[43m 401 #033[0m| 3.614495ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 30.690429ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.18.0.3|#033[42m 200 #033[0m| 251.226µs| match|#033[46m POST #033[0m /service/notifications r:/service/notifications#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 33.74484ms| match|#033[44m GET #033[0m /v2/errandsmoketest/busybox/manifests/latest r:/v2/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.18.0.3|#033[42m 200 #033[0m| 1.015052ms| match|#033[46m POST #033[0m /service/notifications r:/service/notifications#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019-03-19T12:37:21Z [ERROR] [helper.go:100]: Repository not exist, repo: pksharbor.opendc.com/errandsmoketest/busybox, error: notary-server:4443 does not have trust data for pksharbor.opendc.com/errandsmoketest/busybox, returning empty signature +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.18.0.3|#033[42m 200 #033[0m| 215.535µs| match|#033[46m POST #033[0m /service/notifications r:/service/notifications#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.18.0.3|#033[42m 200 #033[0m| 203.4µs| match|#033[46m POST #033[0m /service/notifications r:/service/notifications#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019-03-19T12:37:21Z [INFO] delete tag: errandsmoketest/busybox:latest +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019-03-19T12:37:21Z [INFO] Handle notification with topic 'OnDeletion': notification.OnDeletionNotification{Image:"errandsmoketest/busybox:latest"} +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.18.0.3|#033[42m 200 #033[0m| 521.666µs| match|#033[46m POST #033[0m /service/notifications r:/service/notifications#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 138.021056ms| match|#033[41m DELETE #033[0m /api/repositories/errandsmoketest/busybox r:/api/repositories/*#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 15.199072ms| match|#033[44m GET #033[0m /api/projects r:/api/projects/#033[0m +Mar 19 12:37:21 172.18.0.1 core[8188]: 2019/03/19 12:37:21 #033[1;44m[D] [server.go:2619] | 172.31.0.13|#033[42m 200 #033[0m| 22.837654ms| match|#033[41m DELETE #033[0m /api/projects/2 r:/api/projects/:id([0-9]+)#033[0m +Mar 19 12:37:51 172.18.0.1 core[8188]: 2019/03/19 12:37:51 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.373319ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:38:01 172.18.0.1 core[8188]: 2019/03/19 12:38:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 6.750886ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:38:01 172.18.0.1 core[8188]: 2019/03/19 12:38:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 4.396347ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:38:01 172.18.0.1 core[8188]: 2019/03/19 12:38:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 29.98101ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:38:01 172.18.0.1 core[8188]: 2019/03/19 12:38:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 2.914537ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:38:21 172.18.0.1 core[8188]: 2019/03/19 12:38:21 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.402622ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:38:51 172.18.0.1 core[8188]: 2019/03/19 12:38:51 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.271274ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:39:21 172.18.0.1 core[8188]: 2019/03/19 12:39:21 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 5.932917ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:39:51 172.18.0.1 core[8188]: 2019/03/19 12:39:51 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.946484ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:40:01 172.18.0.1 core[8188]: 2019/03/19 12:40:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 4.990851ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:40:02 172.18.0.1 core[8188]: 2019/03/19 12:40:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 4.227635ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:40:02 172.18.0.1 core[8188]: 2019/03/19 12:40:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 24.346023ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:40:02 172.18.0.1 core[8188]: 2019/03/19 12:40:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 2.789886ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:40:21 172.18.0.1 core[8188]: 2019/03/19 12:40:21 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 3.042565ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:40:51 172.18.0.1 core[8188]: 2019/03/19 12:40:51 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.359124ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:41:21 172.18.0.1 core[8188]: 2019/03/19 12:41:21 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 4.678742ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:41:51 172.18.0.1 core[8188]: 2019/03/19 12:41:51 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.281185ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:42:01 172.18.0.1 core[8188]: 2019/03/19 12:42:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 6.151161ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:42:01 172.18.0.1 core[8188]: 2019/03/19 12:42:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 5.140206ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:42:01 172.18.0.1 core[8188]: 2019/03/19 12:42:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 31.413998ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:42:01 172.18.0.1 core[8188]: 2019/03/19 12:42:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 3.084734ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:42:22 172.18.0.1 core[8188]: 2019/03/19 12:42:22 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 3.741307ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:42:52 172.18.0.1 core[8188]: 2019/03/19 12:42:52 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.65481ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:43:22 172.18.0.1 core[8188]: 2019/03/19 12:43:22 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.478782ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:43:52 172.18.0.1 core[8188]: 2019/03/19 12:43:52 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.834162ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:44:01 172.18.0.1 core[8188]: 2019/03/19 12:44:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 5.729978ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:44:01 172.18.0.1 core[8188]: 2019/03/19 12:44:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 3.267458ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:44:01 172.18.0.1 core[8188]: 2019/03/19 12:44:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 29.992328ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:44:01 172.18.0.1 core[8188]: 2019/03/19 12:44:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 2.437427ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:44:22 172.18.0.1 core[8188]: 2019/03/19 12:44:22 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.129687ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:44:52 172.18.0.1 core[8188]: 2019/03/19 12:44:52 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.275487ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:45:22 172.18.0.1 core[8188]: 2019/03/19 12:45:22 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 5.468859ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:45:52 172.18.0.1 core[8188]: 2019/03/19 12:45:52 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 3.883718ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:46:02 172.18.0.1 core[8188]: 2019/03/19 12:46:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 4.112602ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:46:02 172.18.0.1 core[8188]: 2019/03/19 12:46:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 3.497234ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:46:02 172.18.0.1 core[8188]: 2019/03/19 12:46:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 30.010274ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:46:02 172.18.0.1 core[8188]: 2019/03/19 12:46:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 5.973529ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:46:23 172.18.0.1 core[8188]: 2019/03/19 12:46:23 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 3.162478ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:46:53 172.18.0.1 core[8188]: 2019/03/19 12:46:53 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.009288ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:47:23 172.18.0.1 core[8188]: 2019/03/19 12:47:23 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 5.247765ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:47:53 172.18.0.1 core[8188]: 2019/03/19 12:47:53 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 1.847279ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:48:01 172.18.0.1 core[8188]: 2019/03/19 12:48:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 6.387329ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:48:01 172.18.0.1 core[8188]: 2019/03/19 12:48:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 3.819339ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:48:01 172.18.0.1 core[8188]: 2019/03/19 12:48:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 31.661762ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:48:01 172.18.0.1 core[8188]: 2019/03/19 12:48:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 3.480124ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:48:23 172.18.0.1 core[8188]: 2019/03/19 12:48:23 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 5.727201ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:48:53 172.18.0.1 core[8188]: 2019/03/19 12:48:53 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.193369ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:49:23 172.18.0.1 core[8188]: 2019/03/19 12:49:23 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.855111ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:49:53 172.18.0.1 core[8188]: 2019/03/19 12:49:53 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.110888ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:50:02 172.18.0.1 core[8188]: 2019/03/19 12:50:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 7.150414ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:50:02 172.18.0.1 core[8188]: 2019/03/19 12:50:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 4.332007ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:50:02 172.18.0.1 core[8188]: 2019/03/19 12:50:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 30.362359ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:50:02 172.18.0.1 core[8188]: 2019/03/19 12:50:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 4.847472ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:50:23 172.18.0.1 core[8188]: 2019/03/19 12:50:23 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.076008ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:50:54 172.18.0.1 core[8188]: 2019/03/19 12:50:54 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.183799ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:51:24 172.18.0.1 core[8188]: 2019/03/19 12:51:24 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.766273ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:51:54 172.18.0.1 core[8188]: 2019/03/19 12:51:54 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.442362ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:52:01 172.18.0.1 core[8188]: 2019/03/19 12:52:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 6.023211ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:52:01 172.18.0.1 core[8188]: 2019/03/19 12:52:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 3.44847ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:52:01 172.18.0.1 core[8188]: 2019/03/19 12:52:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 30.549598ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:52:01 172.18.0.1 core[8188]: 2019/03/19 12:52:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 2.862251ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:52:24 172.18.0.1 core[8188]: 2019/03/19 12:52:24 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 3.164715ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:52:54 172.18.0.1 core[8188]: 2019/03/19 12:52:54 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 3.062933ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:53:24 172.18.0.1 core[8188]: 2019/03/19 12:53:24 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.085709ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:53:54 172.18.0.1 core[8188]: 2019/03/19 12:53:54 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.223595ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:54:01 172.18.0.1 core[8188]: 2019/03/19 12:54:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 8.469219ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:54:02 172.18.0.1 core[8188]: 2019/03/19 12:54:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 2.955256ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:54:02 172.18.0.1 core[8188]: 2019/03/19 12:54:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 23.839053ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:54:02 172.18.0.1 core[8188]: 2019/03/19 12:54:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 4.02656ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:54:24 172.18.0.1 core[8188]: 2019/03/19 12:54:24 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 1.842321ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:54:55 172.18.0.1 core[8188]: 2019/03/19 12:54:55 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.093892ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:55:25 172.18.0.1 core[8188]: 2019/03/19 12:55:25 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 7.823017ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:55:55 172.18.0.1 core[8188]: 2019/03/19 12:55:55 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.439695ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:56:01 172.18.0.1 core[8188]: 2019/03/19 12:56:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 5.613111ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:56:01 172.18.0.1 core[8188]: 2019/03/19 12:56:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 4.551849ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:56:01 172.18.0.1 core[8188]: 2019/03/19 12:56:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 31.893063ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:56:01 172.18.0.1 core[8188]: 2019/03/19 12:56:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 2.985837ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:56:25 172.18.0.1 core[8188]: 2019/03/19 12:56:25 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 4.812971ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:56:55 172.18.0.1 core[8188]: 2019/03/19 12:56:55 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.124162ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:57:25 172.18.0.1 core[8188]: 2019/03/19 12:57:25 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 4.565592ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:57:55 172.18.0.1 core[8188]: 2019/03/19 12:57:55 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.728811ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:58:01 172.18.0.1 core[8188]: 2019/03/19 12:58:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 8.703956ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 12:58:01 172.18.0.1 core[8188]: 2019/03/19 12:58:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 3.073086ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:58:01 172.18.0.1 core[8188]: 2019/03/19 12:58:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 30.741321ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 12:58:01 172.18.0.1 core[8188]: 2019/03/19 12:58:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 6.992504ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 12:58:25 172.18.0.1 core[8188]: 2019/03/19 12:58:25 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.785165ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:58:55 172.18.0.1 core[8188]: 2019/03/19 12:58:55 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 1.906476ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:59:26 172.18.0.1 core[8188]: 2019/03/19 12:59:26 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.567999ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 12:59:56 172.18.0.1 core[8188]: 2019/03/19 12:59:56 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.492075ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 13:00:02 172.18.0.1 core[8188]: 2019/03/19 13:00:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 7.714592ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 13:00:02 172.18.0.1 core[8188]: 2019/03/19 13:00:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 5.226376ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 13:00:02 172.18.0.1 core[8188]: 2019/03/19 13:00:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 31.122702ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 13:00:02 172.18.0.1 core[8188]: 2019/03/19 13:00:02 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 4.834111ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 13:00:26 172.18.0.1 core[8188]: 2019/03/19 13:00:26 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.055412ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 13:00:56 172.18.0.1 core[8188]: 2019/03/19 13:00:56 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 1.989599ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 13:01:26 172.18.0.1 core[8188]: 2019/03/19 13:01:26 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.834664ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 13:01:56 172.18.0.1 core[8188]: 2019/03/19 13:01:56 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.341489ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 13:02:01 172.18.0.1 core[8188]: 2019/03/19 13:02:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 3.472989ms| match|#033[44m GET #033[0m /api/systeminfo r:/api/systeminfo#033[0m +Mar 19 13:02:01 172.18.0.1 core[8188]: 2019/03/19 13:02:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[43m 401 #033[0m| 2.942381ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 13:02:01 172.18.0.1 core[8188]: 2019/03/19 13:02:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 24.579534ms| match|#033[44m GET #033[0m /service/token r:/service/token#033[0m +Mar 19 13:02:01 172.18.0.1 core[8188]: 2019/03/19 13:02:01 #033[1;44m[D] [server.go:2619] | 172.31.0.14|#033[42m 200 #033[0m| 2.214257ms| match|#033[44m GET #033[0m /v2/ r:/v2/*#033[0m +Mar 19 13:02:26 172.18.0.1 core[8188]: 2019/03/19 13:02:26 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.00152ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 13:02:56 172.18.0.1 core[8188]: 2019/03/19 13:02:56 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.232816ms| match|#033[44m GET #033[0m /api/ping r:/api/ping#033[0m +Mar 19 13:03:26 172.18.0.1 core[8188]: 2019/03/19 13:03:26 #033[1;44m[D] [server.go:2619] | 127.0.0.1|#033[42m 200 #033[0m| 2.63494 \ No newline at end of file diff --git a/src/jobservice/mgt/mock_manager.go b/src/jobservice/mgt/mock_manager.go index cf523079bd13..b01e4e46b1d1 100644 --- a/src/jobservice/mgt/mock_manager.go +++ b/src/jobservice/mgt/mock_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package mgt @@ -19,6 +19,10 @@ func (_m *MockManager) GetJob(jobID string) (*job.Stats, error) { ret := _m.Called(jobID) var r0 *job.Stats + var r1 error + if rf, ok := ret.Get(0).(func(string) (*job.Stats, error)); ok { + return rf(jobID) + } if rf, ok := ret.Get(0).(func(string) *job.Stats); ok { r0 = rf(jobID) } else { @@ -27,7 +31,6 @@ func (_m *MockManager) GetJob(jobID string) (*job.Stats, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(jobID) } else { @@ -42,6 +45,11 @@ func (_m *MockManager) GetJobs(q *query.Parameter) ([]*job.Stats, int64, error) ret := _m.Called(q) var r0 []*job.Stats + var r1 int64 + var r2 error + if rf, ok := ret.Get(0).(func(*query.Parameter) ([]*job.Stats, int64, error)); ok { + return rf(q) + } if rf, ok := ret.Get(0).(func(*query.Parameter) []*job.Stats); ok { r0 = rf(q) } else { @@ -50,14 +58,12 @@ func (_m *MockManager) GetJobs(q *query.Parameter) ([]*job.Stats, int64, error) } } - var r1 int64 if rf, ok := ret.Get(1).(func(*query.Parameter) int64); ok { r1 = rf(q) } else { r1 = ret.Get(1).(int64) } - var r2 error if rf, ok := ret.Get(2).(func(*query.Parameter) error); ok { r2 = rf(q) } else { @@ -72,6 +78,11 @@ func (_m *MockManager) GetPeriodicExecution(pID string, q *query.Parameter) ([]* ret := _m.Called(pID, q) var r0 []*job.Stats + var r1 int64 + var r2 error + if rf, ok := ret.Get(0).(func(string, *query.Parameter) ([]*job.Stats, int64, error)); ok { + return rf(pID, q) + } if rf, ok := ret.Get(0).(func(string, *query.Parameter) []*job.Stats); ok { r0 = rf(pID, q) } else { @@ -80,14 +91,12 @@ func (_m *MockManager) GetPeriodicExecution(pID string, q *query.Parameter) ([]* } } - var r1 int64 if rf, ok := ret.Get(1).(func(string, *query.Parameter) int64); ok { r1 = rf(pID, q) } else { r1 = ret.Get(1).(int64) } - var r2 error if rf, ok := ret.Get(2).(func(string, *query.Parameter) error); ok { r2 = rf(pID, q) } else { @@ -102,6 +111,11 @@ func (_m *MockManager) GetScheduledJobs(q *query.Parameter) ([]*job.Stats, int64 ret := _m.Called(q) var r0 []*job.Stats + var r1 int64 + var r2 error + if rf, ok := ret.Get(0).(func(*query.Parameter) ([]*job.Stats, int64, error)); ok { + return rf(q) + } if rf, ok := ret.Get(0).(func(*query.Parameter) []*job.Stats); ok { r0 = rf(q) } else { @@ -110,14 +124,12 @@ func (_m *MockManager) GetScheduledJobs(q *query.Parameter) ([]*job.Stats, int64 } } - var r1 int64 if rf, ok := ret.Get(1).(func(*query.Parameter) int64); ok { r1 = rf(q) } else { r1 = ret.Get(1).(int64) } - var r2 error if rf, ok := ret.Get(2).(func(*query.Parameter) error); ok { r2 = rf(q) } else { diff --git a/src/jobservice/period/mock_scheduler.go b/src/jobservice/period/mock_scheduler.go index b0b19cae6c88..51439fa0897f 100644 --- a/src/jobservice/period/mock_scheduler.go +++ b/src/jobservice/period/mock_scheduler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package period @@ -14,13 +14,16 @@ func (_m *MockScheduler) Schedule(policy *Policy) (int64, error) { ret := _m.Called(policy) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(*Policy) (int64, error)); ok { + return rf(policy) + } if rf, ok := ret.Get(0).(func(*Policy) int64); ok { r0 = rf(policy) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(*Policy) error); ok { r1 = rf(policy) } else { diff --git a/src/lib/patterns.go b/src/lib/patterns.go index 9af942435588..7ae9d1df4457 100644 --- a/src/lib/patterns.go +++ b/src/lib/patterns.go @@ -28,6 +28,8 @@ var ( V2BlobUploadURLRe = regexp.MustCompile(fmt.Sprintf(`^/v2/(?P<%s>%s)/blobs/uploads[/a-zA-Z0-9\-_\.=]*$`, RepositorySubexp, reference.NameRegexp.String())) // V2CatalogURLRe is the regular expression for matching the request to v2 handler to list catalog V2CatalogURLRe = regexp.MustCompile(`^/v2/_catalog(/.*)?$`) + // V2ReferrersURLRe is the regular expression for matching request to v2 handler to list referrers + V2ReferrersURLRe = regexp.MustCompile(fmt.Sprintf(`^/v2/(?P<%s>%s)/referrers/(?P<%s>.*)$`, RepositorySubexp, reference.NameRegexp.String(), ReferenceSubexp)) // RepositoryNameRe is the regular expression for matching repository name RepositoryNameRe = regexp.MustCompile(fmt.Sprintf("^%s$", reference.NameRegexp)) ) diff --git a/src/lib/patterns_test.go b/src/lib/patterns_test.go index c4270b5147ef..8426791275eb 100644 --- a/src/lib/patterns_test.go +++ b/src/lib/patterns_test.go @@ -109,6 +109,41 @@ func TestMatchCatalogURLPattern(t *testing.T) { } } +func TestMatchReferrersURLPattern(t *testing.T) { + cases := []struct { + url string + match bool + }{ + { + url: "/v2/library/hello-world/referrers/!@#!@#%", + match: true, + }, + { + url: "/v2/library/hello-world/referrers/test", + match: true, + }, + { + url: "/v2/library/hello-world/referrers/sha256:e5785cb0c62cebbed4965129bae371f0589cadd6d84798fb58c2c5f9e237efd9", + match: true, + }, + { + url: "/v2/library/hello-world/referrers/e5785cb0c62cebbed4965129bae371f0589cadd6d84798fb58c2c5f9e237efd9", + match: true, + }, + { + url: "/v2/library/hello-world/referrers/.Invalid", + match: true, + }, + { + url: "/v2/library/hello-world/referrers//v2/library/photon/referrers/sha256:0000000000000000000000000000000000000000000000000000000000000000", + match: true, + }, + } + for _, c := range cases { + assert.Equal(t, c.match, V2ReferrersURLRe.MatchString(c.url), "failed for %s", c.url) + } +} + func TestRepositoryNamePattern(t *testing.T) { assert := assert.New(t) assert.False(RepositoryNameRe.MatchString("a/*")) diff --git a/src/pkg/accessory/dao/dao.go b/src/pkg/accessory/dao/dao.go index 791b08915a56..85fb1e709eb0 100644 --- a/src/pkg/accessory/dao/dao.go +++ b/src/pkg/accessory/dao/dao.go @@ -95,11 +95,11 @@ func (d *dao) Create(ctx context.Context, acc *Accessory) (int64, error) { } id, err := ormer.Insert(acc) if err != nil { - if e := orm.AsConflictError(err, "accessory %s already exists under the artifact %d", - acc.Digest, acc.SubjectArtifactID); e != nil { + if e := orm.AsConflictError(err, "accessory %s already exists under the artifact %s", + acc.Digest, acc.SubjectArtifactDigest); e != nil { err = e - } else if e := orm.AsForeignKeyError(err, "the accessory %s tries to attach to a non existing artifact %d", - acc.Digest, acc.SubjectArtifactID); e != nil { + } else if e := orm.AsForeignKeyError(err, "the accessory %s tries to attach to a non existing artifact %s", + acc.Digest, acc.SubjectArtifactDigest); e != nil { err = e } } diff --git a/src/pkg/accessory/dao/dao_test.go b/src/pkg/accessory/dao/dao_test.go index 660224f605e4..2a77010ff2b4 100644 --- a/src/pkg/accessory/dao/dao_test.go +++ b/src/pkg/accessory/dao/dao_test.go @@ -32,12 +32,13 @@ import ( type daoTestSuite struct { htesting.Suite - dao DAO - artDAO artdao.DAO - artifactID int64 - subArtifactID int64 - accID int64 - ctx context.Context + dao DAO + artDAO artdao.DAO + artifactID int64 + subArtifactID int64 + subArtifactDigest string + accID int64 + ctx context.Context } func (d *daoTestSuite) SetupSuite() { @@ -47,16 +48,18 @@ func (d *daoTestSuite) SetupSuite() { d.ClearTables = []string{"artifact", "artifact_accessory"} d.artDAO = artdao.New() - artifactID, err := d.artDAO.Create(d.ctx, &artdao.Artifact{ + art := &artdao.Artifact{ Type: "IMAGE", MediaType: "application/vnd.oci.image.config.v1+json", ManifestMediaType: "application/vnd.oci.image.manifest.v1+json", ProjectID: 1, RepositoryID: 1000, Digest: d.DigestString(), - }) - d.Require().Nil(err) + } + artifactID, err := d.artDAO.Create(d.ctx, art) d.subArtifactID = artifactID + d.Require().Nil(err) + d.subArtifactDigest = art.Digest d.artDAO = artdao.New() artifactID, err = d.artDAO.Create(d.ctx, &artdao.Artifact{ @@ -71,11 +74,11 @@ func (d *daoTestSuite) SetupSuite() { d.artifactID = artifactID accID, err := d.dao.Create(d.ctx, &Accessory{ - ArtifactID: d.artifactID, - SubjectArtifactID: d.subArtifactID, - Digest: d.DigestString(), - Size: 1234, - Type: "cosign.signature", + ArtifactID: d.artifactID, + SubjectArtifactDigest: d.subArtifactDigest, + Digest: d.DigestString(), + Size: 1234, + Type: "cosign.signature", }) d.Require().Nil(err) d.accID = accID @@ -103,7 +106,7 @@ func (d *daoTestSuite) TestCount() { d.True(total > 0) total, err = d.dao.Count(d.ctx, &q.Query{ Keywords: map[string]interface{}{ - "SubjectArtifactID": d.subArtifactID, + "SubjectArtifactDigest": d.subArtifactDigest, }, }) d.Require().Nil(err) @@ -125,7 +128,7 @@ func (d *daoTestSuite) TestList() { accs, err = d.dao.List(d.ctx, &q.Query{ Keywords: map[string]interface{}{ - "SubjectArtifactID": d.subArtifactID, + "SubjectArtifactDigest": d.subArtifactDigest, }, }) d.Require().Nil(err) @@ -149,27 +152,15 @@ func (d *daoTestSuite) TestCreate() { // conflict acc := &Accessory{ - ArtifactID: d.artifactID, - SubjectArtifactID: d.subArtifactID, - Digest: d.DigestString(), - Size: 1234, - Type: "cosign.signature", + ArtifactID: d.artifactID, + SubjectArtifactDigest: d.subArtifactDigest, + Digest: d.DigestString(), + Size: 1234, + Type: "cosign.signature", } _, err := d.dao.Create(d.ctx, acc) d.Require().NotNil(err) d.True(errors.IsErr(err, errors.ConflictCode)) - - // violating foreign key constraint: the artifact that the tag tries to attach doesn't exist - acc = &Accessory{ - ArtifactID: 999, - SubjectArtifactID: 998, - Digest: d.DigestString(), - Size: 1234, - Type: "cosign.signature", - } - _, err = d.dao.Create(d.ctx, acc) - d.Require().NotNil(err) - d.True(errors.IsErr(err, errors.ViolateForeignKeyConstraintCode)) } func (d *daoTestSuite) TestDelete() { @@ -184,14 +175,15 @@ func (d *daoTestSuite) TestDelete() { } func (d *daoTestSuite) TestDeleteOfArtifact() { - subArtID, err := d.artDAO.Create(d.ctx, &artdao.Artifact{ + art := &artdao.Artifact{ Type: "IMAGE", MediaType: "application/vnd.oci.image.config.v1+json", ManifestMediaType: "application/vnd.oci.image.manifest.v1+json", ProjectID: 1, RepositoryID: 1000, Digest: d.DigestString(), - }) + } + subArtID, err := d.artDAO.Create(d.ctx, art) d.Require().Nil(err) defer d.artDAO.Delete(d.ctx, subArtID) @@ -218,28 +210,28 @@ func (d *daoTestSuite) TestDeleteOfArtifact() { defer d.artDAO.Delete(d.ctx, artID2) acc1 := &Accessory{ - ArtifactID: artID1, - SubjectArtifactID: subArtID, - Digest: d.DigestString(), - Size: 1234, - Type: "cosign.signature", + ArtifactID: artID1, + SubjectArtifactDigest: art.Digest, + Digest: d.DigestString(), + Size: 1234, + Type: "cosign.signature", } _, err = d.dao.Create(d.ctx, acc1) d.Require().Nil(err) acc2 := &Accessory{ - ArtifactID: artID2, - SubjectArtifactID: subArtID, - Digest: d.DigestString(), - Size: 1234, - Type: "cosign.signature", + ArtifactID: artID2, + SubjectArtifactDigest: art.Digest, + Digest: d.DigestString(), + Size: 1234, + Type: "cosign.signature", } _, err = d.dao.Create(d.ctx, acc2) d.Require().Nil(err) accs, err := d.dao.List(d.ctx, &q.Query{ Keywords: map[string]interface{}{ - "SubjectArtifactID": subArtID, + "SubjectArtifactDigest": art.Digest, }, }) for _, acc := range accs { @@ -250,14 +242,14 @@ func (d *daoTestSuite) TestDeleteOfArtifact() { _, err = d.dao.DeleteAccessories(d.ctx, &q.Query{ Keywords: map[string]interface{}{ - "SubjectArtifactID": subArtID, + "SubjectArtifactDigest": art.Digest, }, }) d.Require().Nil(err) accs, err = d.dao.List(d.ctx, &q.Query{ Keywords: map[string]interface{}{ - "SubjectArtifactID": subArtID, + "SubjectArtifactDigest": art.Digest, }, }) d.Require().Nil(err) diff --git a/src/pkg/accessory/dao/model.go b/src/pkg/accessory/dao/model.go index 1c0f64e4a124..0d084064e66f 100644 --- a/src/pkg/accessory/dao/model.go +++ b/src/pkg/accessory/dao/model.go @@ -26,13 +26,13 @@ func init() { // Accessory model in database type Accessory struct { - ID int64 `orm:"pk;auto;column(id)" json:"id"` - ArtifactID int64 `orm:"column(artifact_id)" json:"artifact_id"` - SubjectArtifactID int64 `orm:"column(subject_artifact_id)" json:"subject_artifact_id"` - Type string `orm:"column(type)" json:"type"` - Size int64 `orm:"column(size)" json:"size"` - Digest string `orm:"column(digest)" json:"digest"` - CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"` + ID int64 `orm:"pk;auto;column(id)" json:"id"` + ArtifactID int64 `orm:"column(artifact_id)" json:"artifact_id"` + SubjectArtifactDigest string `orm:"column(subject_artifact_digest)" json:"subject_artifact_digest"` + Type string `orm:"column(type)" json:"type"` + Size int64 `orm:"column(size)" json:"size"` + Digest string `orm:"column(digest)" json:"digest"` + CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"` } // TableName for artifact reference diff --git a/src/pkg/accessory/manager.go b/src/pkg/accessory/manager.go index c0f22a9161f1..d14ec3ae2e18 100644 --- a/src/pkg/accessory/manager.go +++ b/src/pkg/accessory/manager.go @@ -38,7 +38,7 @@ var ( // Manager is the only interface of artifact module to provide the management functions for artifacts type Manager interface { // Ensure ... - Ensure(ctx context.Context, subArtID, artifactID, size int64, digest, accType string) error + Ensure(ctx context.Context, subArtDigest string, artifactID, size int64, digest, accType string) error // Get the artifact specified by the ID Get(ctx context.Context, id int64) (accessory model.Accessory, err error) // Count returns the total count of accessory according to the query. @@ -66,7 +66,7 @@ type manager struct { dao dao.DAO } -func (m *manager) Ensure(ctx context.Context, subArtID, artifactID, size int64, digest, accType string) error { +func (m *manager) Ensure(ctx context.Context, subArtDigest string, artifactID, size int64, digest, accType string) error { accs, err := m.dao.List(ctx, q.New(q.KeyWords{"ArtifactID": artifactID, "Digest": digest})) if err != nil { return err @@ -76,11 +76,11 @@ func (m *manager) Ensure(ctx context.Context, subArtID, artifactID, size int64, } acc := model.AccessoryData{ - ArtifactID: artifactID, - SubArtifactID: subArtID, - Digest: digest, - Size: size, - Type: accType, + ArtifactID: artifactID, + SubArtifactDigest: subArtDigest, + Digest: digest, + Size: size, + Type: accType, } _, err = m.Create(ctx, acc) return err @@ -92,13 +92,13 @@ func (m *manager) Get(ctx context.Context, id int64) (model.Accessory, error) { return nil, err } return model.New(acc.Type, model.AccessoryData{ - ID: acc.ID, - ArtifactID: acc.ArtifactID, - SubArtifactID: acc.SubjectArtifactID, - Size: acc.Size, - Digest: acc.Digest, - CreatTime: acc.CreationTime, - Icon: m.GetIcon(acc.Type), + ID: acc.ID, + ArtifactID: acc.ArtifactID, + SubArtifactDigest: acc.SubjectArtifactDigest, + Size: acc.Size, + Digest: acc.Digest, + CreatTime: acc.CreationTime, + Icon: m.GetIcon(acc.Type), }) } @@ -114,13 +114,13 @@ func (m *manager) List(ctx context.Context, query *q.Query) ([]model.Accessory, var accs []model.Accessory for _, accD := range accsDao { acc, err := model.New(accD.Type, model.AccessoryData{ - ID: accD.ID, - ArtifactID: accD.ArtifactID, - SubArtifactID: accD.SubjectArtifactID, - Size: accD.Size, - Digest: accD.Digest, - CreatTime: accD.CreationTime, - Icon: m.GetIcon(accD.Type), + ID: accD.ID, + ArtifactID: accD.ArtifactID, + SubArtifactDigest: accD.SubjectArtifactDigest, + Size: accD.Size, + Digest: accD.Digest, + CreatTime: accD.CreationTime, + Icon: m.GetIcon(accD.Type), }) if err != nil { return nil, errors.New(err).WithCode(errors.BadRequestCode) @@ -132,11 +132,11 @@ func (m *manager) List(ctx context.Context, query *q.Query) ([]model.Accessory, func (m *manager) Create(ctx context.Context, accessory model.AccessoryData) (int64, error) { acc := &dao.Accessory{ - ArtifactID: accessory.ArtifactID, - SubjectArtifactID: accessory.SubArtifactID, - Size: accessory.Size, - Digest: accessory.Digest, - Type: accessory.Type, + ArtifactID: accessory.ArtifactID, + SubjectArtifactDigest: accessory.SubArtifactDigest, + Size: accessory.Size, + Digest: accessory.Digest, + Type: accessory.Type, } return m.dao.Create(ctx, acc) } diff --git a/src/pkg/accessory/manager_test.go b/src/pkg/accessory/manager_test.go index fdc4d77d973e..5136acdf10a8 100644 --- a/src/pkg/accessory/manager_test.go +++ b/src/pkg/accessory/manager_test.go @@ -45,7 +45,7 @@ func (m *managerTestSuite) SetupTest() { func (m *managerTestSuite) TestEnsure() { mock.OnAnything(m.dao, "List").Return([]*dao.Accessory{}, nil) mock.OnAnything(m.dao, "Create").Return(int64(1), nil) - err := m.mgr.Ensure(nil, int64(1), int64(1), int64(1), "sha256:1234", model.TypeCosignSignature) + err := m.mgr.Ensure(nil, string(""), int64(1), int64(1), "sha256:1234", model.TypeCosignSignature) m.Require().Nil(err) } diff --git a/src/pkg/accessory/model/accessory.go b/src/pkg/accessory/model/accessory.go index 83a0df9be1af..fed23973db73 100644 --- a/src/pkg/accessory/model/accessory.go +++ b/src/pkg/accessory/model/accessory.go @@ -73,14 +73,14 @@ const ( // AccessoryData ... type AccessoryData struct { - ID int64 `json:"id"` - ArtifactID int64 `json:"artifact_id"` - SubArtifactID int64 `json:"subject_artifact_id"` - Type string `json:"type"` - Size int64 `json:"size"` - Digest string `json:"digest"` - CreatTime time.Time `json:"creation_time"` - Icon string `json:"icon"` + ID int64 `json:"id"` + ArtifactID int64 `json:"artifact_id"` + SubArtifactDigest string `json:"subject_artifact_id"` + Type string `json:"type"` + Size int64 `json:"size"` + Digest string `json:"digest"` + CreatTime time.Time `json:"creation_time"` + Icon string `json:"icon"` } // Accessory Independent, but linked to an existing subject artifact, which enabling the extensibility of an OCI artifact diff --git a/src/pkg/accessory/model/base/base_test.go b/src/pkg/accessory/model/base/base_test.go index 9c3eb9a373f9..ec66a7872171 100644 --- a/src/pkg/accessory/model/base/base_test.go +++ b/src/pkg/accessory/model/base/base_test.go @@ -21,10 +21,10 @@ func (suite *BaseTestSuite) SetupSuite() { suite.subDigest = suite.DigestString() suite.accessory, _ = model.New(model.TypeNone, model.AccessoryData{ - ArtifactID: 1, - SubArtifactID: 2, - Size: 1234, - Digest: suite.digest, + ArtifactID: 1, + SubArtifactDigest: suite.subDigest, + Size: 1234, + Digest: suite.digest, }) } @@ -37,7 +37,7 @@ func (suite *BaseTestSuite) TestGetArtID() { } func (suite *BaseTestSuite) TestSubGetArtID() { - suite.Equal(int64(2), suite.accessory.GetData().SubArtifactID) + suite.Equal(suite.subDigest, suite.accessory.GetData().SubArtifactDigest) } func (suite *BaseTestSuite) TestSubGetSize() { diff --git a/src/pkg/accessory/model/cosign/cosign_test.go b/src/pkg/accessory/model/cosign/cosign_test.go index 5d898ac2e71a..46d6b2b605af 100644 --- a/src/pkg/accessory/model/cosign/cosign_test.go +++ b/src/pkg/accessory/model/cosign/cosign_test.go @@ -13,16 +13,18 @@ type CosignTestSuite struct { htesting.Suite accessory model.Accessory digest string + subDigest string } func (suite *CosignTestSuite) SetupSuite() { suite.digest = suite.DigestString() + suite.subDigest = suite.DigestString() suite.accessory, _ = model.New(model.TypeCosignSignature, model.AccessoryData{ - ArtifactID: 1, - SubArtifactID: 2, - Size: 4321, - Digest: suite.digest, + ArtifactID: 1, + SubArtifactDigest: suite.subDigest, + Size: 4321, + Digest: suite.digest, }) } @@ -35,7 +37,7 @@ func (suite *CosignTestSuite) TestGetArtID() { } func (suite *CosignTestSuite) TestSubGetArtID() { - suite.Equal(int64(2), suite.accessory.GetData().SubArtifactID) + suite.Equal(suite.subDigest, suite.accessory.GetData().SubArtifactDigest) } func (suite *CosignTestSuite) TestSubGetSize() { diff --git a/src/pkg/accessory/model/nydus/nydus_test.go b/src/pkg/accessory/model/nydus/nydus_test.go index b067d9398724..90ae6c041ade 100644 --- a/src/pkg/accessory/model/nydus/nydus_test.go +++ b/src/pkg/accessory/model/nydus/nydus_test.go @@ -13,16 +13,18 @@ type NydusTestSuite struct { htesting.Suite accessory model.Accessory digest string + subDigest string } func (suite *NydusTestSuite) SetupSuite() { suite.digest = suite.DigestString() + suite.subDigest = suite.DigestString() suite.accessory, _ = model.New(model.TypeNydusAccelerator, model.AccessoryData{ - ArtifactID: 1, - SubArtifactID: 2, - Size: 4321, - Digest: suite.digest, + ArtifactID: 1, + SubArtifactDigest: suite.subDigest, + Size: 4321, + Digest: suite.digest, }) } @@ -35,7 +37,7 @@ func (suite *NydusTestSuite) TestGetArtID() { } func (suite *NydusTestSuite) TestSubGetArtID() { - suite.Equal(int64(2), suite.accessory.GetData().SubArtifactID) + suite.Equal(suite.subDigest, suite.accessory.GetData().SubArtifactDigest) } func (suite *NydusTestSuite) TestSubGetSize() { diff --git a/src/pkg/notification/hook/hook.go b/src/pkg/notification/hook/hook.go index 8fa6c39c163e..e7c4b868c228 100644 --- a/src/pkg/notification/hook/hook.go +++ b/src/pkg/notification/hook/hook.go @@ -4,17 +4,13 @@ import ( "context" "encoding/json" "fmt" - "time" - cJob "github.com/goharbor/harbor/src/common/job" "github.com/goharbor/harbor/src/common/job/models" - cModels "github.com/goharbor/harbor/src/common/models" - "github.com/goharbor/harbor/src/core/utils" - "github.com/goharbor/harbor/src/lib/config" + "github.com/goharbor/harbor/src/jobservice/job" + "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/log" - "github.com/goharbor/harbor/src/pkg/notification/job" - job_model "github.com/goharbor/harbor/src/pkg/notification/job/model" "github.com/goharbor/harbor/src/pkg/notifier/model" + "github.com/goharbor/harbor/src/pkg/task" ) // Manager send hook @@ -24,63 +20,61 @@ type Manager interface { // DefaultManager ... type DefaultManager struct { - jobMgr job.Manager - client cJob.Client + execMgr task.ExecutionManager + taskMgr task.Manager } // NewHookManager ... func NewHookManager() *DefaultManager { return &DefaultManager{ - jobMgr: job.NewManager(), - client: utils.GetJobServiceClient(), + execMgr: task.ExecMgr, + taskMgr: task.Mgr, } } -// StartHook create a notification job record in database, and submit it to jobservice +// StartHook create a webhook job record in database, and submit it to jobservice func (hm *DefaultManager) StartHook(ctx context.Context, event *model.HookEvent, data *models.JobData) error { payload, err := json.Marshal(event.Payload) if err != nil { return err } - t := time.Now() - id, err := hm.jobMgr.Create(ctx, &job_model.Job{ - PolicyID: event.PolicyID, - EventType: event.EventType, - NotifyType: event.Target.Type, - Status: cModels.JobPending, - CreationTime: t, - UpdateTime: t, - JobDetail: string(payload), - }) - if err != nil { - return fmt.Errorf("failed to create the job record for notification based on policy %d: %v", event.PolicyID, err) + extraAttrs := make(map[string]interface{}) + if err = json.Unmarshal(payload, &extraAttrs); err != nil { + return err } - statusHookURL := fmt.Sprintf("%s/service/notifications/jobs/webhook/%d", config.InternalCoreURL(), id) - data.StatusHook = statusHookURL - log.Debugf("created a notification job %d for the policy %d", id, event.PolicyID) + var vendorType string + switch event.Target.Type { + case model.NotifyTypeHTTP: + vendorType = job.WebhookJobVendorType + case model.NotifyTypeSlack: + vendorType = job.SlackJobVendorType + } + + if len(vendorType) == 0 { + return errors.Errorf("invalid event target type: %s", event.Target.Type) + } - // submit hook job to jobservice - jobUUID, err := hm.client.SubmitJob(data) + // create execution firstly, then create task. + execID, err := hm.execMgr.Create(ctx, vendorType, event.PolicyID, task.ExecutionTriggerEvent, extraAttrs) if err != nil { - log.Errorf("failed to submit job with notification event: %v", err) - e := hm.jobMgr.Update(ctx, &job_model.Job{ - ID: id, - Status: cModels.JobError, - }, "Status") - if e != nil { - log.Errorf("failed to update the notification job status %d: %v", id, e) - } - return err + log.Errorf("failed to create execution for webhook based on policy %d: %v", event.PolicyID, err) + return nil } - if err = hm.jobMgr.Update(ctx, &job_model.Job{ - ID: id, - UUID: jobUUID, - }, "UUID"); err != nil { - log.Errorf("failed to update the notification job %d: %v", id, err) - return err + taskID, err := hm.taskMgr.Create(ctx, execID, &task.Job{ + Name: data.Name, + Metadata: &job.Metadata{ + JobKind: data.Metadata.JobKind, + }, + Parameters: map[string]interface{}(data.Parameters), + }, extraAttrs) + if err != nil { + return fmt.Errorf("failed to create the task for webhook based on policy %d: %v", event.PolicyID, err) } + + log.Debugf("created a webhook job %d for the policy %d", taskID, event.PolicyID) + return nil } diff --git a/src/pkg/notification/job/dao/dao.go b/src/pkg/notification/job/dao/dao.go deleted file mode 100644 index 1c5c3229206e..000000000000 --- a/src/pkg/notification/job/dao/dao.go +++ /dev/null @@ -1,186 +0,0 @@ -package dao - -import ( - "context" - "fmt" - - "github.com/goharbor/harbor/src/common/models" - "github.com/goharbor/harbor/src/lib/errors" - "github.com/goharbor/harbor/src/lib/log" - "github.com/goharbor/harbor/src/lib/orm" - "github.com/goharbor/harbor/src/lib/q" - "github.com/goharbor/harbor/src/pkg/notification/job/model" -) - -// DAO defines the interface to access the robot data model -type DAO interface { - // Create ... - Create(ctx context.Context, n *model.Job) (int64, error) - - // Update ... - Update(ctx context.Context, n *model.Job, props ...string) error - - // Get ... - Get(ctx context.Context, id int64) (*model.Job, error) - - // Count ... - Count(ctx context.Context, query *q.Query) (total int64, err error) - - // List ... - List(ctx context.Context, query *q.Query) ([]*model.Job, error) - - // Delete ... - Delete(ctx context.Context, id int64) error - - // GetLastTriggerJobsGroupByEventType ... - GetLastTriggerJobsGroupByEventType(ctx context.Context, policyID int64) ([]*model.Job, error) - - // DeleteByPolicyID - DeleteByPolicyID(ctx context.Context, policyID int64) error -} - -// New creates a default implementation for Dao -func New() DAO { - return &dao{} -} - -type dao struct{} - -// UpdateNotificationJob update notification job -func (d *dao) Update(ctx context.Context, job *model.Job, props ...string) error { - if job == nil { - return errors.New("nil job") - } - - if job.ID == 0 { - return fmt.Errorf("notification job ID is empty") - } - - ormer, err := orm.FromContext(ctx) - if err != nil { - return err - } - n, err := ormer.Update(job, props...) - if n == 0 { - return errors.NotFoundError(nil).WithMessage("notification %d not found", job.ID) - } - if err != nil { - return err - } - return nil -} - -// Create insert new notification job to DB -func (d *dao) Create(ctx context.Context, job *model.Job) (int64, error) { - if job == nil { - return 0, errors.New("nil job") - } - ormer, err := orm.FromContext(ctx) - if err != nil { - return 0, err - } - if len(job.Status) == 0 { - job.Status = models.JobPending - } - return ormer.Insert(job) -} - -// Get ... -func (d *dao) Get(ctx context.Context, id int64) (*model.Job, error) { - ormer, err := orm.FromContext(ctx) - if err != nil { - return nil, err - } - j := &model.Job{ - ID: id, - } - if err := ormer.Read(j); err != nil { - if e := orm.AsNotFoundError(err, "notificationJob %d not found", id); e != nil { - err = e - } - return nil, err - } - return j, nil -} - -// Count ... -func (d *dao) Count(ctx context.Context, query *q.Query) (int64, error) { - qs, err := orm.QuerySetterForCount(ctx, &model.Job{}, query) - if err != nil { - return 0, err - } - return qs.Count() -} - -// List ... -func (d *dao) List(ctx context.Context, query *q.Query) ([]*model.Job, error) { - jobs := []*model.Job{} - - qs, err := orm.QuerySetter(ctx, &model.Job{}, query) - if err != nil { - return nil, err - } - if _, err = qs.All(&jobs); err != nil { - return nil, err - } - return jobs, nil -} - -// GetLastTriggerJobsGroupByEventType get notification jobs info of policy, including event type and last trigger time -func (d *dao) GetLastTriggerJobsGroupByEventType(ctx context.Context, policyID int64) ([]*model.Job, error) { - ormer, err := orm.FromContext(ctx) - if err != nil { - return nil, err - } - // get jobs last triggered(created) group by event_type. postgres group by usage reference: - // https://stackoverflow.com/questions/13325583/postgresql-max-and-group-by - sql := `select distinct on (event_type) event_type, id, creation_time, status, notify_type, job_uuid, update_time, - creation_time, job_detail from notification_job where policy_id = ? - order by event_type, id desc, creation_time, status, notify_type, job_uuid, update_time, creation_time, job_detail` - - jobs := []*model.Job{} - _, err = ormer.Raw(sql, policyID).QueryRows(&jobs) - if err != nil { - log.Errorf("query last trigger info group by event type failed: %v", err) - return nil, err - } - - return jobs, nil -} - -func (d *dao) Delete(ctx context.Context, id int64) error { - ormer, err := orm.FromContext(ctx) - if err != nil { - return err - } - n, err := ormer.Delete(&model.Job{ - ID: id, - }) - if err != nil { - return err - } - if n == 0 { - return errors.NotFoundError(nil).WithMessage("notificationJob %d not found", id) - } - return nil -} - -// DeleteByPolicyID ... -func (d *dao) DeleteByPolicyID(ctx context.Context, policyID int64) error { - qs, err := orm.QuerySetter(ctx, &model.Job{}, &q.Query{ - Keywords: map[string]interface{}{ - "policy_id": policyID, - }, - }) - if err != nil { - return err - } - n, err := qs.Delete() - if err != nil { - return err - } - if n == 0 { - return errors.NotFoundError(nil).WithMessage("notificationJob %d not found", policyID) - } - return nil -} diff --git a/src/pkg/notification/job/dao/dao_test.go b/src/pkg/notification/job/dao/dao_test.go deleted file mode 100644 index 5549ba6af568..000000000000 --- a/src/pkg/notification/job/dao/dao_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package dao - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/goharbor/harbor/src/lib/errors" - "github.com/goharbor/harbor/src/lib/orm" - "github.com/goharbor/harbor/src/lib/q" - "github.com/goharbor/harbor/src/pkg/notification/job/model" - htesting "github.com/goharbor/harbor/src/testing" -) - -var ( - testJob1 = &model.Job{ - PolicyID: 1111, - EventType: "pushImage", - NotifyType: "http", - Status: "pending", - JobDetail: "{\"type\":\"pushImage\",\"occur_at\":1563536782,\"event_data\":{\"resources\":[{\"digest\":\"sha256:bf1684a6e3676389ec861c602e97f27b03f14178e5bc3f70dce198f9f160cce9\",\"tag\":\"v1.0\",\"resource_url\":\"10.194.32.23/myproj/alpine:v1.0\"}],\"repository\":{\"date_created\":1563505587,\"name\":\"alpine\",\"namespace\":\"myproj\",\"repo_full_name\":\"myproj/alpine\",\"repo_type\":\"private\"}},\"operator\":\"admin\"}", - UUID: "00000000", - } - testJob2 = &model.Job{ - PolicyID: 111, - EventType: "pullImage", - NotifyType: "http", - Status: "", - JobDetail: "{\"type\":\"pushImage\",\"occur_at\":1563537782,\"event_data\":{\"resources\":[{\"digest\":\"sha256:bf1684a6e3676389ec861c602e97f27b03f14178e5bc3f70dce198f9f160cce9\",\"tag\":\"v1.0\",\"resource_url\":\"10.194.32.23/myproj/alpine:v1.0\"}],\"repository\":{\"date_created\":1563505587,\"name\":\"alpine\",\"namespace\":\"myproj\",\"repo_full_name\":\"myproj/alpine\",\"repo_type\":\"private\"}},\"operator\":\"admin\"}", - UUID: "00000000", - } - testJob3 = &model.Job{ - PolicyID: 111, - EventType: "deleteImage", - NotifyType: "http", - Status: "pending", - JobDetail: "{\"type\":\"pushImage\",\"occur_at\":1563538782,\"event_data\":{\"resources\":[{\"digest\":\"sha256:bf1684a6e3676389ec861c602e97f27b03f14178e5bc3f70dce198f9f160cce9\",\"tag\":\"v1.0\",\"resource_url\":\"10.194.32.23/myproj/alpine:v1.0\"}],\"repository\":{\"date_created\":1563505587,\"name\":\"alpine\",\"namespace\":\"myproj\",\"repo_full_name\":\"myproj/alpine\",\"repo_type\":\"private\"}},\"operator\":\"admin\"}", - UUID: "00000000", - } -) - -type DaoTestSuite struct { - htesting.Suite - dao DAO - - jobID1 int64 - jobID2 int64 - jobID3 int64 -} - -func (suite *DaoTestSuite) SetupSuite() { - suite.Suite.SetupSuite() - suite.dao = New() - suite.Suite.ClearTables = []string{"notification_job"} - suite.jobs() -} - -func (suite *DaoTestSuite) jobs() { - var err error - suite.jobID1, err = suite.dao.Create(orm.Context(), testJob1) - suite.Nil(err) - - suite.jobID2, err = suite.dao.Create(orm.Context(), testJob2) - suite.Nil(err) - - suite.jobID3, err = suite.dao.Create(orm.Context(), testJob3) - suite.Nil(err) -} - -func (suite *DaoTestSuite) TestCreate() { - _, err := suite.dao.Create(orm.Context(), nil) - suite.NotNil(err) -} - -func (suite *DaoTestSuite) TestDelete() { - err := suite.dao.Delete(orm.Context(), 1234) - suite.Require().NotNil(err) - suite.True(errors.IsErr(err, errors.NotFoundCode)) - - err = suite.dao.Delete(orm.Context(), suite.jobID2) - suite.Nil(err) -} - -func (suite *DaoTestSuite) TestList() { - jobs, err := suite.dao.List(orm.Context(), &q.Query{ - Keywords: map[string]interface{}{ - "EventType": "pushImage", - }, - }) - suite.Require().Nil(err) - suite.Equal(len(jobs), 1) - suite.Equal(suite.jobID1, jobs[0].ID) -} - -func (suite *DaoTestSuite) TestGet() { - _, err := suite.dao.Get(orm.Context(), 1234) - suite.Require().NotNil(err) - suite.True(errors.IsErr(err, errors.NotFoundCode)) - - id, err := suite.dao.Create(orm.Context(), &model.Job{ - PolicyID: 2222, - EventType: "pullImage", - NotifyType: "http", - Status: "pending", - JobDetail: "{\"type\":\"pushImage\",\"occur_at\":1563536782,\"event_data\":{\"resources\":[{\"digest\":\"sha256:bf1684a6e3676389ec861c602e97f27b03f14178e5bc3f70dce198f9f160cce9\",\"tag\":\"v1.0\",\"resource_url\":\"10.194.32.23/myproj/alpine:v1.0\"}],\"repository\":{\"date_created\":1563505587,\"name\":\"alpine\",\"namespace\":\"myproj\",\"repo_full_name\":\"myproj/alpine\",\"repo_type\":\"private\"}},\"operator\":\"admin\"}", - UUID: "00000000", - }) - suite.Nil(err) - - r, err := suite.dao.Get(orm.Context(), id) - suite.Nil(err) - suite.Equal("pullImage", r.EventType) -} - -func (suite *DaoTestSuite) TestUpdate() { - j := &model.Job{ - ID: suite.jobID1, - Status: "success", - } - - err := suite.dao.Update(orm.Context(), j) - suite.Nil(err) - - r1, err := suite.dao.Get(orm.Context(), j.ID) - suite.Equal("success", r1.Status) -} - -func (suite *DaoTestSuite) TestCount() { - // nil query - total, err := suite.dao.Count(orm.Context(), nil) - suite.Nil(err) - suite.True(total > 0) - - // query by name - total, err = suite.dao.Count(orm.Context(), &q.Query{ - Keywords: map[string]interface{}{ - "EventType": "deleteImage", - }, - }) - suite.Nil(err) - suite.Equal(int64(1), total) -} - -func (suite *DaoTestSuite) TestDeleteByPolicyID() { - jobs, err := suite.dao.List(orm.Context(), &q.Query{ - Keywords: map[string]interface{}{ - "PolicyID": 111, - }, - }) - suite.True(len(jobs) > 0) - - err = suite.dao.DeleteByPolicyID(orm.Context(), 111) - suite.Nil(err) - - jobs, err = suite.dao.List(orm.Context(), &q.Query{ - Keywords: map[string]interface{}{ - "PolicyID": 111, - }, - }) - suite.Equal(0, len(jobs)) -} - -func (suite *DaoTestSuite) TestGetLastTriggerJobsGroupByEventType() { - _, err := suite.dao.Create(orm.Context(), &model.Job{ - PolicyID: 3333, - EventType: "replicateImage", - NotifyType: "http", - Status: "pending", - JobDetail: "{\"type\":\"pushImage\",\"occur_at\":1563536782,\"event_data\":{\"resources\":[{\"digest\":\"sha256:bf1684a6e3676389ec861c602e97f27b03f14178e5bc3f70dce198f9f160cce9\",\"tag\":\"v1.0\",\"resource_url\":\"10.194.32.23/myproj/alpine:v1.0\"}],\"repository\":{\"date_created\":1563505587,\"name\":\"alpine\",\"namespace\":\"myproj\",\"repo_full_name\":\"myproj/alpine\",\"repo_type\":\"private\"}},\"operator\":\"admin\"}", - UUID: "00000000", - }) - suite.Nil(err) - _, err = suite.dao.Create(orm.Context(), &model.Job{ - PolicyID: 3333, - EventType: "pullImage", - NotifyType: "http", - Status: "pending", - JobDetail: "{\"type\":\"pushImage\",\"occur_at\":1563536782,\"event_data\":{\"resources\":[{\"digest\":\"sha256:bf1684a6e3676389ec861c602e97f27b03f14178e5bc3f70dce198f9f160cce9\",\"tag\":\"v1.0\",\"resource_url\":\"10.194.32.23/myproj/alpine:v1.0\"}],\"repository\":{\"date_created\":1563505587,\"name\":\"alpine\",\"namespace\":\"myproj\",\"repo_full_name\":\"myproj/alpine\",\"repo_type\":\"private\"}},\"operator\":\"admin\"}", - UUID: "00000000", - }) - suite.Nil(err) - jobs, err := suite.dao.GetLastTriggerJobsGroupByEventType(orm.Context(), 3333) - suite.Nil(err) - suite.Equal(2, len(jobs)) -} - -func TestDaoTestSuite(t *testing.T) { - suite.Run(t, &DaoTestSuite{}) -} diff --git a/src/pkg/notification/job/manager.go b/src/pkg/notification/job/manager.go deleted file mode 100644 index f60ed95ebc70..000000000000 --- a/src/pkg/notification/job/manager.go +++ /dev/null @@ -1,70 +0,0 @@ -package job - -import ( - "context" - - "github.com/goharbor/harbor/src/lib/q" - "github.com/goharbor/harbor/src/pkg/notification/job/dao" - "github.com/goharbor/harbor/src/pkg/notification/job/model" -) - -var ( - // Mgr is a global variable for the default notification job - Mgr = NewManager() -) - -// Manager manages notification jobs recorded in database -type Manager interface { - // Create create a notification job - Create(ctx context.Context, job *model.Job) (int64, error) - - // List list notification jobs - List(ctx context.Context, query *q.Query) ([]*model.Job, error) - - // Update update notification job - Update(ctx context.Context, job *model.Job, props ...string) error - - // ListJobsGroupByEventType lists last triggered jobs group by event type - ListJobsGroupByEventType(ctx context.Context, policyID int64) ([]*model.Job, error) - - // Count ... - Count(ctx context.Context, query *q.Query) (total int64, err error) -} - -var _ Manager = &manager{} - -type manager struct { - dao dao.DAO -} - -// NewManager ... -func NewManager() Manager { - return &manager{ - dao: dao.New(), - } -} - -// Create ... -func (d *manager) Create(ctx context.Context, job *model.Job) (int64, error) { - return d.dao.Create(ctx, job) -} - -// Count ... -func (d *manager) Count(ctx context.Context, query *q.Query) (int64, error) { - return d.dao.Count(ctx, query) -} - -// List ... -func (d *manager) List(ctx context.Context, query *q.Query) ([]*model.Job, error) { - return d.dao.List(ctx, query) -} - -// Update ... -func (d *manager) Update(ctx context.Context, job *model.Job, props ...string) error { - return d.dao.Update(ctx, job, props...) -} - -// ListJobsGroupByEventType lists last triggered jobs group by event type -func (d *manager) ListJobsGroupByEventType(ctx context.Context, policyID int64) ([]*model.Job, error) { - return d.dao.GetLastTriggerJobsGroupByEventType(ctx, policyID) -} diff --git a/src/pkg/notification/job/manager_test.go b/src/pkg/notification/job/manager_test.go deleted file mode 100644 index 5e8ae06fe062..000000000000 --- a/src/pkg/notification/job/manager_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package job - -import ( - "context" - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/goharbor/harbor/src/pkg/notification/job/model" - "github.com/goharbor/harbor/src/testing/mock" - "github.com/goharbor/harbor/src/testing/pkg/notification/job/dao" -) - -type managerTestSuite struct { - suite.Suite - mgr *manager - dao *dao.DAO -} - -func (m *managerTestSuite) SetupTest() { - m.dao = &dao.DAO{} - m.mgr = &manager{ - dao: m.dao, - } -} - -func (m *managerTestSuite) TestCreate() { - m.dao.On("Create", mock.Anything, mock.Anything).Return(int64(1), nil) - _, err := m.mgr.Create(context.Background(), &model.Job{}) - m.Nil(err) - m.dao.AssertExpectations(m.T()) -} - -func (m *managerTestSuite) TestUpdate() { - m.dao.On("Update", mock.Anything, mock.Anything).Return(nil) - err := m.mgr.Update(context.Background(), &model.Job{}) - m.Nil(err) - m.dao.AssertExpectations(m.T()) -} - -func (m *managerTestSuite) TestCount() { - m.dao.On("Count", mock.Anything, mock.Anything).Return(int64(1), nil) - n, err := m.mgr.Count(context.Background(), nil) - m.Nil(err) - m.Equal(int64(1), n) - m.dao.AssertExpectations(m.T()) -} - -func (m *managerTestSuite) TestList() { - m.dao.On("List", mock.Anything, mock.Anything).Return([]*model.Job{ - { - ID: 1, - EventType: "test_job", - }, - }, nil) - rpers, err := m.mgr.List(context.Background(), nil) - m.Nil(err) - m.Equal(1, len(rpers)) - m.dao.AssertExpectations(m.T()) -} - -func (m *managerTestSuite) TestListJobsGroupByEventType() { - m.dao.On("GetLastTriggerJobsGroupByEventType", mock.Anything, mock.Anything).Return([]*model.Job{ - { - ID: 1, - EventType: "test_job", - PolicyID: 1, - }, - { - ID: 2, - EventType: "test_job", - PolicyID: 1, - }, - }, nil) - rpers, err := m.mgr.ListJobsGroupByEventType(context.Background(), 1) - m.Nil(err) - m.Equal(2, len(rpers)) - m.dao.AssertExpectations(m.T()) -} - -func TestManager(t *testing.T) { - suite.Run(t, &managerTestSuite{}) -} diff --git a/src/pkg/notification/job/model/model.go b/src/pkg/notification/job/model/model.go deleted file mode 100644 index 8d96c5a3a6d8..000000000000 --- a/src/pkg/notification/job/model/model.go +++ /dev/null @@ -1,29 +0,0 @@ -package model - -import ( - "time" - - "github.com/beego/beego/v2/client/orm" -) - -func init() { - orm.RegisterModel(&Job{}) -} - -// Job is the model for a notification job -type Job struct { - ID int64 `orm:"pk;auto;column(id)" json:"id"` - PolicyID int64 `orm:"column(policy_id)" json:"policy_id"` - EventType string `orm:"column(event_type)" json:"event_type"` - NotifyType string `orm:"column(notify_type)" json:"notify_type"` - Status string `orm:"column(status)" json:"status"` - JobDetail string `orm:"column(job_detail)" json:"job_detail"` - UUID string `orm:"column(job_uuid)" json:"-"` - CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"` - UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time" sort:"default:desc"` -} - -// TableName set table name for ORM. -func (j *Job) TableName() string { - return "notification_job" -} diff --git a/src/pkg/notification/notification.go b/src/pkg/notification/notification.go index 744f086fd9f0..4f8e3616584b 100644 --- a/src/pkg/notification/notification.go +++ b/src/pkg/notification/notification.go @@ -7,27 +7,38 @@ import ( "github.com/goharbor/harbor/src/controller/event" "github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/pkg/notification/hook" - "github.com/goharbor/harbor/src/pkg/notification/job" "github.com/goharbor/harbor/src/pkg/notification/policy" n_event "github.com/goharbor/harbor/src/pkg/notifier/event" notifier_model "github.com/goharbor/harbor/src/pkg/notifier/model" ) +type ( + // EventType is the type of event + EventType string + // NotifyType is the type of notify + NotifyType string +) + +func (e EventType) String() string { + return string(e) +} + +func (n NotifyType) String() string { + return string(n) +} + var ( // PolicyMgr is a global notification policy manager PolicyMgr policy.Manager - // JobMgr is a notification job controller - JobMgr job.Manager - // HookManager is a hook manager HookManager hook.Manager - // SupportedEventTypes is a map to store supported event type, eg. pushImage, pullImage etc - SupportedEventTypes map[string]struct{} + // supportedEventTypes is a slice to store supported event type, eg. pushImage, pullImage etc + supportedEventTypes []EventType - // SupportedNotifyTypes is a map to store notification type, eg. HTTP, Email etc - SupportedNotifyTypes map[string]struct{} + // supportedNotifyTypes is a slice to store notification type, eg. HTTP, Email etc + supportedNotifyTypes []NotifyType ) // Init ... @@ -36,8 +47,6 @@ func Init() { PolicyMgr = policy.Mgr // init hook manager HookManager = hook.NewHookManager() - // init notification job manager - JobMgr = job.Mgr initSupportedNotifyType() @@ -45,8 +54,8 @@ func Init() { } func initSupportedNotifyType() { - SupportedEventTypes = make(map[string]struct{}, 0) - SupportedNotifyTypes = make(map[string]struct{}, 0) + supportedEventTypes = make([]EventType, 0) + supportedNotifyTypes = make([]NotifyType, 0) eventTypes := []string{ event.TopicPushArtifact, @@ -61,12 +70,12 @@ func initSupportedNotifyType() { event.TopicTagRetention, } for _, eventType := range eventTypes { - SupportedEventTypes[eventType] = struct{}{} + supportedEventTypes = append(supportedEventTypes, EventType(eventType)) } notifyTypes := []string{notifier_model.NotifyTypeHTTP, notifier_model.NotifyTypeSlack} for _, notifyType := range notifyTypes { - SupportedNotifyTypes[notifyType] = struct{}{} + supportedNotifyTypes = append(supportedNotifyTypes, NotifyType(notifyType)) } } @@ -110,3 +119,11 @@ func AddEvent(ctx context.Context, m n_event.Metadata, notify ...bool) { } e.Events.PushBack(m) } + +func GetSupportedEventTypes() []EventType { + return supportedEventTypes +} + +func GetSupportedNotifyTypes() []NotifyType { + return supportedNotifyTypes +} diff --git a/src/pkg/notification/policy/manager.go b/src/pkg/notification/policy/manager.go index 5dfdc28e58cc..5c01d1b2d8e6 100755 --- a/src/pkg/notification/policy/manager.go +++ b/src/pkg/notification/policy/manager.go @@ -3,16 +3,11 @@ package policy import ( "context" "fmt" - "net/http" "time" - commonhttp "github.com/goharbor/harbor/src/common/http" - "github.com/goharbor/harbor/src/lib/errors" - "github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/lib/q" "github.com/goharbor/harbor/src/pkg/notification/policy/dao" "github.com/goharbor/harbor/src/pkg/notification/policy/model" - notifier_model "github.com/goharbor/harbor/src/pkg/notifier/model" ) var ( @@ -30,14 +25,10 @@ type Manager interface { Count(ctx context.Context, query *q.Query) (int64, error) // Get policy with specified ID Get(ctx context.Context, id int64) (*model.Policy, error) - // GetByNameAndProjectID get policy by the name and projectID - GetByNameAndProjectID(ctx context.Context, name string, projectID int64) (*model.Policy, error) // Update the specified policy Update(ctx context.Context, policy *model.Policy) error // Delete the specified policy Delete(ctx context.Context, policyID int64) error - // Test the specified policy - Test(policy *model.Policy) error // GetRelatedPolices get event type related policies in project GetRelatedPolices(ctx context.Context, projectID int64, eventType string) ([]*model.Policy, error) } @@ -107,23 +98,6 @@ func (m *manager) Get(ctx context.Context, id int64) (*model.Policy, error) { return policy, err } -// GetByNameAndProjectID notification policy by the name and projectID -func (m *manager) GetByNameAndProjectID(ctx context.Context, name string, projectID int64) (*model.Policy, error) { - query := q.New(q.KeyWords{"name": name, "project_id": projectID}) - policies, err := m.dao.List(ctx, query) - if err != nil { - return nil, err - } - if len(policies) == 0 { - return nil, errors.New(nil).WithCode(errors.NotFoundCode).WithMessage("no notification policy found") - } - policy := policies[0] - if err := policy.ConvertFromDBModel(); err != nil { - return nil, err - } - return policy, err -} - // Update the specified notification policy func (m *manager) Update(ctx context.Context, policy *model.Policy) error { policy.UpdateTime = time.Now() @@ -139,40 +113,6 @@ func (m *manager) Delete(ctx context.Context, policyID int64) error { return m.dao.Delete(ctx, policyID) } -// Test the specified notification policy, just test for network connection without request body -func (m *manager) Test(policy *model.Policy) error { - for _, target := range policy.Targets { - switch target.Type { - case notifier_model.NotifyTypeHTTP, notifier_model.NotifyTypeSlack: - return m.policyHTTPTest(target.Address, target.SkipCertVerify) - default: - return fmt.Errorf("invalid policy target type: %s", target.Type) - } - } - return nil -} - -func (m *manager) policyHTTPTest(address string, skipCertVerify bool) error { - req, err := http.NewRequest(http.MethodPost, address, nil) - if err != nil { - return err - } - - req.Header.Set("Content-Type", "application/json") - client := http.Client{ - Transport: commonhttp.GetHTTPTransport(commonhttp.WithInsecure(skipCertVerify)), - } - - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - log.Debugf("policy test success with address %s, skip cert verify :%v", address, skipCertVerify) - - return nil -} - // GetRelatedPolices get policies including event type in project func (m *manager) GetRelatedPolices(ctx context.Context, projectID int64, eventType string) ([]*model.Policy, error) { policies, err := m.List(ctx, q.New(q.KeyWords{"project_id": projectID})) diff --git a/src/pkg/notification/policy/manager_test.go b/src/pkg/notification/policy/manager_test.go index 42d6d99a8c50..39e84a31909a 100644 --- a/src/pkg/notification/policy/manager_test.go +++ b/src/pkg/notification/policy/manager_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/suite" - "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/pkg/notification/policy/model" "github.com/goharbor/harbor/src/testing/mock" "github.com/goharbor/harbor/src/testing/pkg/notification/policy/dao" @@ -68,28 +67,6 @@ func (m *managerTestSuite) TestList() { m.Equal(1, len(rpers)) m.dao.AssertExpectations(m.T()) } - -func (m *managerTestSuite) TestGetByNameAndProjectID404() { - m.dao.On("List", mock.Anything, mock.Anything).Return([]*model.Policy{}, nil) - _, err := m.mgr.GetByNameAndProjectID(context.Background(), "test_policy", 1) - m.NotNil(err) - m.True(errors.IsNotFoundErr(err)) -} - -func (m *managerTestSuite) TestGetByNameAndProjectID() { - m.dao.On("List", mock.Anything, mock.Anything).Return([]*model.Policy{ - { - ID: 1, - Name: "test_policy", - ProjectID: 1, - }, - }, nil) - policy, err := m.mgr.GetByNameAndProjectID(context.Background(), "test_policy", 1) - m.Nil(err) - m.Equal("test_policy", policy.Name) - m.dao.AssertExpectations(m.T()) -} - func (m *managerTestSuite) TestGetRelatedPolices() { m.dao.On("List", mock.Anything, mock.Anything).Return([]*model.Policy{ { diff --git a/src/pkg/oidc/helper.go b/src/pkg/oidc/helper.go index ec828f97fc32..53bc5690f0ae 100644 --- a/src/pkg/oidc/helper.go +++ b/src/pkg/oidc/helper.go @@ -17,7 +17,6 @@ package oidc import ( "context" "crypto/tls" - "errors" "fmt" "net/http" "regexp" @@ -50,14 +49,13 @@ type claimsProvider interface { type providerHelper struct { sync.Mutex instance atomic.Value - setting atomic.Value creationTime time.Time } -func (p *providerHelper) get() (*gooidc.Provider, error) { +func (p *providerHelper) get(ctx context.Context) (*gooidc.Provider, error) { if p.instance.Load() != nil { if time.Since(p.creationTime) > 3*time.Second { - if err := p.create(); err != nil { + if err := p.create(ctx); err != nil { return nil, err } } @@ -65,42 +63,23 @@ func (p *providerHelper) get() (*gooidc.Provider, error) { p.Lock() defer p.Unlock() if p.instance.Load() == nil { - if err := p.reloadSetting(); err != nil { + if err := p.create(ctx); err != nil { return nil, err } - if err := p.create(); err != nil { - return nil, err - } - go func() { - for { - if err := p.reloadSetting(); err != nil { - log.Warningf("Failed to refresh configuration, error: %v", err) - } - time.Sleep(3 * time.Second) - } - }() } } return p.instance.Load().(*gooidc.Provider), nil } -func (p *providerHelper) reloadSetting() error { - conf, err := config.OIDCSetting(orm.Context()) +func (p *providerHelper) create(ctx context.Context) error { + s, err := config.OIDCSetting(ctx) if err != nil { - return fmt.Errorf("failed to load OIDC setting: %v", err) - } - p.setting.Store(*conf) - return nil -} - -func (p *providerHelper) create() error { - if p.setting.Load() == nil { - return errors.New("the configuration is not loaded") + log.Errorf("Failed to get OIDC configuration, error: %v", err) + return err } - s := p.setting.Load().(cfgModels.OIDCSetting) - ctx := clientCtx(context.Background(), s.VerifyCert) - provider, err := gooidc.NewProvider(ctx, s.Endpoint) + c := clientCtx(ctx, s.VerifyCert) + provider, err := gooidc.NewProvider(c, s.Endpoint) if err != nil { return fmt.Errorf("failed to create OIDC provider, error: %v", err) } @@ -137,12 +116,16 @@ type UserInfo struct { hasGroupClaim bool } -func getOauthConf() (*oauth2.Config, error) { - p, err := provider.get() +func getOauthConf(ctx context.Context) (*oauth2.Config, error) { + p, err := provider.get(ctx) if err != nil { return nil, err } - setting := provider.setting.Load().(cfgModels.OIDCSetting) + setting, err := config.OIDCSetting(ctx) + if err != nil { + log.Errorf("Failed to get OIDC configuration, error: %v", err) + return nil, err + } scopes := make([]string, 0) for _, sc := range setting.Scope { if strings.HasPrefix(p.Endpoint().AuthURL, googleEndpoint) && sc == gooidc.ScopeOfflineAccess { @@ -162,14 +145,18 @@ func getOauthConf() (*oauth2.Config, error) { // AuthCodeURL returns the URL for OIDC provider's consent page. The state should be verified when user is redirected // back to Harbor. -func AuthCodeURL(state string) (string, error) { - conf, err := getOauthConf() +func AuthCodeURL(ctx context.Context, state string) (string, error) { + conf, err := getOauthConf(ctx) if err != nil { log.Errorf("Failed to get OAuth configuration, error: %v", err) return "", err } var options []oauth2.AuthCodeOption - setting := provider.setting.Load().(cfgModels.OIDCSetting) + setting, err := config.OIDCSetting(ctx) + if err != nil { + log.Errorf("Failed to get OIDC configuration, error: %v", err) + return "", err + } for k, v := range setting.ExtraRedirectParms { options = append(options, oauth2.SetAuthURLParam(k, v)) } @@ -182,12 +169,16 @@ func AuthCodeURL(state string) (string, error) { // ExchangeToken get the token from token provider via the code func ExchangeToken(ctx context.Context, code string) (*Token, error) { - oauth, err := getOauthConf() + oauth, err := getOauthConf(ctx) if err != nil { log.Errorf("Failed to get OAuth configuration, error: %v", err) return nil, err } - setting := provider.setting.Load().(cfgModels.OIDCSetting) + setting, err := config.OIDCSetting(ctx) + if err != nil { + log.Errorf("Failed to get OIDC configuration, error: %v", err) + return nil, err + } ctx = clientCtx(ctx, setting.VerifyCert) oauthToken, err := oauth.Exchange(ctx, code) if err != nil { @@ -208,11 +199,15 @@ func VerifyToken(ctx context.Context, rawIDToken string) (*gooidc.IDToken, error func verifyTokenWithConfig(ctx context.Context, rawIDToken string, conf *gooidc.Config) (*gooidc.IDToken, error) { log.Debugf("Raw ID token for verification: %s", rawIDToken) - p, err := provider.get() + p, err := provider.get(ctx) + if err != nil { + return nil, err + } + settings, err := config.OIDCSetting(ctx) if err != nil { + log.Errorf("Failed to get OIDC configuration, error: %v", err) return nil, err } - settings := provider.setting.Load().(cfgModels.OIDCSetting) if conf == nil { conf = &gooidc.Config{ClientID: settings.ClientID} } @@ -236,11 +231,15 @@ func clientCtx(ctx context.Context, verifyCert bool) context.Context { // refreshToken tries to refresh the token if it's expired, if it doesn't the // original one will be returned. func refreshToken(ctx context.Context, token *Token) (*Token, error) { - oauthCfg, err := getOauthConf() + oauthCfg, err := getOauthConf(ctx) if err != nil { return nil, err } - setting := provider.setting.Load().(cfgModels.OIDCSetting) + setting, err := config.OIDCSetting(ctx) + if err != nil { + log.Errorf("Failed to get OIDC configuration, error: %v", err) + return nil, err + } cctx := clientCtx(ctx, setting.VerifyCert) ts := oauthCfg.TokenSource(cctx, &token.Token) nt, err := ts.Token() @@ -258,16 +257,20 @@ func refreshToken(ctx context.Context, token *Token) (*Token, error) { // to generate a UserInfo object, if the ID token is not in the input token struct, some attributes will be empty func UserInfoFromToken(ctx context.Context, token *Token) (*UserInfo, error) { // #10913: preload the configuration, in case it was not previously loaded by the UI - _, err := provider.get() + _, err := provider.get(ctx) + if err != nil { + return nil, err + } + setting, err := config.OIDCSetting(ctx) if err != nil { + log.Errorf("Failed to get OIDC configuration, error: %v", err) return nil, err } - setting := provider.setting.Load().(cfgModels.OIDCSetting) - local, err := UserInfoFromIDToken(ctx, token, setting) + local, err := UserInfoFromIDToken(ctx, token, *setting) if err != nil { return nil, err } - remote, err := userInfoFromRemote(ctx, token, setting) + remote, err := userInfoFromRemote(ctx, token, *setting) if err != nil { log.Warningf("Failed to get userInfo by calling remote userinfo endpoint, error: %v ", err) } @@ -319,7 +322,7 @@ func mergeUserInfo(remote, local *UserInfo) *UserInfo { } func userInfoFromRemote(ctx context.Context, token *Token, setting cfgModels.OIDCSetting) (*UserInfo, error) { - p, err := provider.get() + p, err := provider.get(ctx) if err != nil { return nil, err } @@ -409,6 +412,7 @@ func populateGroupsDB(groupNames []string) ([]int, error) { ctx := orm.Context() cfg, err := config.OIDCSetting(ctx) if err != nil { + log.Errorf("failed to get OIDC config, error: %v", err) return nil, err } log.Debugf("populateGroupsDB, group filter %v", cfg.GroupFilter) diff --git a/src/pkg/oidc/helper_test.go b/src/pkg/oidc/helper_test.go index a71173077635..0f96365c9dd7 100644 --- a/src/pkg/oidc/helper_test.go +++ b/src/pkg/oidc/helper_test.go @@ -55,20 +55,11 @@ func TestMain(m *testing.M) { os.Exit(result) } } -func TestHelperLoadConf(t *testing.T) { - testP := &providerHelper{} - assert.Nil(t, testP.setting.Load()) - err := testP.reloadSetting() - assert.Nil(t, err) - assert.Equal(t, "test", testP.setting.Load().(cfgModels.OIDCSetting).Name) -} func TestHelperCreate(t *testing.T) { testP := &providerHelper{} - err := testP.reloadSetting() - assert.Nil(t, err) assert.Nil(t, testP.instance.Load()) - err = testP.create() + err := testP.create(orm.Context()) assert.Nil(t, err) assert.NotNil(t, testP.instance.Load()) assert.True(t, time.Now().Sub(testP.creationTime) < 2*time.Second) @@ -76,7 +67,8 @@ func TestHelperCreate(t *testing.T) { func TestHelperGet(t *testing.T) { testP := &providerHelper{} - p, err := testP.get() + ctx := orm.Context() + p, err := testP.get(ctx) assert.Nil(t, err) assert.Equal(t, "https://oauth2.googleapis.com/token", p.Endpoint().TokenURL) @@ -89,12 +81,13 @@ func TestHelperGet(t *testing.T) { common.OIDCClientSecret: "new-secret", common.ExtEndpoint: "https://harbor.test", } - ctx := orm.Context() config.GetCfgManager(ctx).UpdateConfig(ctx, update) t.Log("Sleep for 5 seconds") time.Sleep(5 * time.Second) - assert.Equal(t, "new-secret", testP.setting.Load().(cfgModels.OIDCSetting).ClientSecret) + oidcSetting, err := config.OIDCSetting(ctx) + assert.Nil(t, err) + assert.Equal(t, "new-secret", oidcSetting.ClientSecret) } func TestAuthCodeURL(t *testing.T) { @@ -110,7 +103,7 @@ func TestAuthCodeURL(t *testing.T) { } ctx := orm.Context() config.GetCfgManager(ctx).UpdateConfig(ctx, conf) - res, err := AuthCodeURL("random") + res, err := AuthCodeURL(ctx, "random") assert.Nil(t, err) u, err := url.ParseRequestURI(res) assert.Nil(t, err) diff --git a/src/pkg/scheduler/mock_dao_test.go b/src/pkg/scheduler/mock_dao_test.go index 64886e5833db..6347469353b8 100644 --- a/src/pkg/scheduler/mock_dao_test.go +++ b/src/pkg/scheduler/mock_dao_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package scheduler @@ -19,13 +19,16 @@ func (_m *mockDAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { + return rf(ctx, query) + } if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok { r0 = rf(ctx, query) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok { r1 = rf(ctx, query) } else { @@ -40,13 +43,16 @@ func (_m *mockDAO) Create(ctx context.Context, s *schedule) (int64, error) { ret := _m.Called(ctx, s) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *schedule) (int64, error)); ok { + return rf(ctx, s) + } if rf, ok := ret.Get(0).(func(context.Context, *schedule) int64); ok { r0 = rf(ctx, s) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *schedule) error); ok { r1 = rf(ctx, s) } else { @@ -75,6 +81,10 @@ func (_m *mockDAO) Get(ctx context.Context, id int64) (*schedule, error) { ret := _m.Called(ctx, id) var r0 *schedule + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (*schedule, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int64) *schedule); ok { r0 = rf(ctx, id) } else { @@ -83,7 +93,6 @@ func (_m *mockDAO) Get(ctx context.Context, id int64) (*schedule, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { r1 = rf(ctx, id) } else { @@ -98,6 +107,10 @@ func (_m *mockDAO) List(ctx context.Context, query *q.Query) ([]*schedule, error ret := _m.Called(ctx, query) var r0 []*schedule + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*schedule, error)); ok { + return rf(ctx, query) + } if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*schedule); ok { r0 = rf(ctx, query) } else { @@ -106,7 +119,6 @@ func (_m *mockDAO) List(ctx context.Context, query *q.Query) ([]*schedule, error } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok { r1 = rf(ctx, query) } else { @@ -142,13 +154,16 @@ func (_m *mockDAO) UpdateRevision(ctx context.Context, id int64, revision int64) ret := _m.Called(ctx, id, revision) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, int64) (int64, error)); ok { + return rf(ctx, id, revision) + } if rf, ok := ret.Get(0).(func(context.Context, int64, int64) int64); ok { r0 = rf(ctx, id, revision) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64, int64) error); ok { r1 = rf(ctx, id, revision) } else { diff --git a/src/pkg/task/mock_execution_dao_test.go b/src/pkg/task/mock_execution_dao_test.go index 8cc981980fd0..6f242145546d 100644 --- a/src/pkg/task/mock_execution_dao_test.go +++ b/src/pkg/task/mock_execution_dao_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package task @@ -21,13 +21,16 @@ func (_m *mockExecutionDAO) Count(ctx context.Context, query *q.Query) (int64, e ret := _m.Called(ctx, query) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { + return rf(ctx, query) + } if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok { r0 = rf(ctx, query) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok { r1 = rf(ctx, query) } else { @@ -42,13 +45,16 @@ func (_m *mockExecutionDAO) Create(ctx context.Context, execution *dao.Execution ret := _m.Called(ctx, execution) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *dao.Execution) (int64, error)); ok { + return rf(ctx, execution) + } if rf, ok := ret.Get(0).(func(context.Context, *dao.Execution) int64); ok { r0 = rf(ctx, execution) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *dao.Execution) error); ok { r1 = rf(ctx, execution) } else { @@ -77,6 +83,10 @@ func (_m *mockExecutionDAO) Get(ctx context.Context, id int64) (*dao.Execution, ret := _m.Called(ctx, id) var r0 *dao.Execution + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (*dao.Execution, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int64) *dao.Execution); ok { r0 = rf(ctx, id) } else { @@ -85,7 +95,6 @@ func (_m *mockExecutionDAO) Get(ctx context.Context, id int64) (*dao.Execution, } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { r1 = rf(ctx, id) } else { @@ -100,6 +109,10 @@ func (_m *mockExecutionDAO) GetMetrics(ctx context.Context, id int64) (*dao.Metr ret := _m.Called(ctx, id) var r0 *dao.Metrics + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (*dao.Metrics, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int64) *dao.Metrics); ok { r0 = rf(ctx, id) } else { @@ -108,7 +121,6 @@ func (_m *mockExecutionDAO) GetMetrics(ctx context.Context, id int64) (*dao.Metr } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { r1 = rf(ctx, id) } else { @@ -123,6 +135,10 @@ func (_m *mockExecutionDAO) List(ctx context.Context, query *q.Query) ([]*dao.Ex ret := _m.Called(ctx, query) var r0 []*dao.Execution + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*dao.Execution, error)); ok { + return rf(ctx, query) + } if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*dao.Execution); ok { r0 = rf(ctx, query) } else { @@ -131,7 +147,6 @@ func (_m *mockExecutionDAO) List(ctx context.Context, query *q.Query) ([]*dao.Ex } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok { r1 = rf(ctx, query) } else { @@ -146,20 +161,23 @@ func (_m *mockExecutionDAO) RefreshStatus(ctx context.Context, id int64) (bool, ret := _m.Called(ctx, id) var r0 bool + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (bool, string, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int64) bool); ok { r0 = rf(ctx, id) } else { r0 = ret.Get(0).(bool) } - var r1 string if rf, ok := ret.Get(1).(func(context.Context, int64) string); ok { r1 = rf(ctx, id) } else { r1 = ret.Get(1).(string) } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, int64) error); ok { r2 = rf(ctx, id) } else { diff --git a/src/pkg/task/mock_jobservice_client_test.go b/src/pkg/task/mock_jobservice_client_test.go index 27b0b9059bbf..5f6aed737e77 100644 --- a/src/pkg/task/mock_jobservice_client_test.go +++ b/src/pkg/task/mock_jobservice_client_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package task @@ -19,6 +19,10 @@ func (_m *mockJobserviceClient) GetExecutions(uuid string) ([]job.Stats, error) ret := _m.Called(uuid) var r0 []job.Stats + var r1 error + if rf, ok := ret.Get(0).(func(string) ([]job.Stats, error)); ok { + return rf(uuid) + } if rf, ok := ret.Get(0).(func(string) []job.Stats); ok { r0 = rf(uuid) } else { @@ -27,7 +31,6 @@ func (_m *mockJobserviceClient) GetExecutions(uuid string) ([]job.Stats, error) } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(uuid) } else { @@ -42,6 +45,10 @@ func (_m *mockJobserviceClient) GetJobLog(uuid string) ([]byte, error) { ret := _m.Called(uuid) var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(string) ([]byte, error)); ok { + return rf(uuid) + } if rf, ok := ret.Get(0).(func(string) []byte); ok { r0 = rf(uuid) } else { @@ -50,7 +57,6 @@ func (_m *mockJobserviceClient) GetJobLog(uuid string) ([]byte, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(uuid) } else { @@ -65,6 +71,10 @@ func (_m *mockJobserviceClient) GetJobServiceConfig() (*job.Config, error) { ret := _m.Called() var r0 *job.Config + var r1 error + if rf, ok := ret.Get(0).(func() (*job.Config, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() *job.Config); ok { r0 = rf() } else { @@ -73,7 +83,6 @@ func (_m *mockJobserviceClient) GetJobServiceConfig() (*job.Config, error) { } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -102,13 +111,16 @@ func (_m *mockJobserviceClient) SubmitJob(_a0 *models.JobData) (string, error) { ret := _m.Called(_a0) var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(*models.JobData) (string, error)); ok { + return rf(_a0) + } if rf, ok := ret.Get(0).(func(*models.JobData) string); ok { r0 = rf(_a0) } else { r0 = ret.Get(0).(string) } - var r1 error if rf, ok := ret.Get(1).(func(*models.JobData) error); ok { r1 = rf(_a0) } else { diff --git a/src/pkg/task/mock_task_dao_test.go b/src/pkg/task/mock_task_dao_test.go index eee80da9cfe3..2ba7e33c65c2 100644 --- a/src/pkg/task/mock_task_dao_test.go +++ b/src/pkg/task/mock_task_dao_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package task @@ -23,13 +23,16 @@ func (_m *mockTaskDAO) Count(ctx context.Context, query *q.Query) (int64, error) ret := _m.Called(ctx, query) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { + return rf(ctx, query) + } if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok { r0 = rf(ctx, query) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok { r1 = rf(ctx, query) } else { @@ -44,13 +47,16 @@ func (_m *mockTaskDAO) Create(ctx context.Context, _a1 *dao.Task) (int64, error) ret := _m.Called(ctx, _a1) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *dao.Task) (int64, error)); ok { + return rf(ctx, _a1) + } if rf, ok := ret.Get(0).(func(context.Context, *dao.Task) int64); ok { r0 = rf(ctx, _a1) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *dao.Task) error); ok { r1 = rf(ctx, _a1) } else { @@ -79,6 +85,10 @@ func (_m *mockTaskDAO) ExecutionIDsByVendorAndStatus(ctx context.Context, vendor ret := _m.Called(ctx, vendorType, status) var r0 []int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]int64, error)); ok { + return rf(ctx, vendorType, status) + } if rf, ok := ret.Get(0).(func(context.Context, string, string) []int64); ok { r0 = rf(ctx, vendorType, status) } else { @@ -87,7 +97,6 @@ func (_m *mockTaskDAO) ExecutionIDsByVendorAndStatus(ctx context.Context, vendor } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { r1 = rf(ctx, vendorType, status) } else { @@ -102,6 +111,10 @@ func (_m *mockTaskDAO) Get(ctx context.Context, id int64) (*dao.Task, error) { ret := _m.Called(ctx, id) var r0 *dao.Task + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (*dao.Task, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int64) *dao.Task); ok { r0 = rf(ctx, id) } else { @@ -110,7 +123,6 @@ func (_m *mockTaskDAO) Get(ctx context.Context, id int64) (*dao.Task, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { r1 = rf(ctx, id) } else { @@ -125,13 +137,16 @@ func (_m *mockTaskDAO) GetMaxEndTime(ctx context.Context, executionID int64) (ti ret := _m.Called(ctx, executionID) var r0 time.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (time.Time, error)); ok { + return rf(ctx, executionID) + } if rf, ok := ret.Get(0).(func(context.Context, int64) time.Time); ok { r0 = rf(ctx, executionID) } else { r0 = ret.Get(0).(time.Time) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { r1 = rf(ctx, executionID) } else { @@ -146,6 +161,10 @@ func (_m *mockTaskDAO) List(ctx context.Context, query *q.Query) ([]*dao.Task, e ret := _m.Called(ctx, query) var r0 []*dao.Task + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*dao.Task, error)); ok { + return rf(ctx, query) + } if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*dao.Task); ok { r0 = rf(ctx, query) } else { @@ -154,7 +173,6 @@ func (_m *mockTaskDAO) List(ctx context.Context, query *q.Query) ([]*dao.Task, e } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok { r1 = rf(ctx, query) } else { @@ -169,6 +187,10 @@ func (_m *mockTaskDAO) ListStatusCount(ctx context.Context, executionID int64) ( ret := _m.Called(ctx, executionID) var r0 []*dao.StatusCount + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) ([]*dao.StatusCount, error)); ok { + return rf(ctx, executionID) + } if rf, ok := ret.Get(0).(func(context.Context, int64) []*dao.StatusCount); ok { r0 = rf(ctx, executionID) } else { @@ -177,7 +199,6 @@ func (_m *mockTaskDAO) ListStatusCount(ctx context.Context, executionID int64) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { r1 = rf(ctx, executionID) } else { diff --git a/src/pkg/task/mock_task_manager_test.go b/src/pkg/task/mock_task_manager_test.go index 17665a53c19c..916d5ea0045d 100644 --- a/src/pkg/task/mock_task_manager_test.go +++ b/src/pkg/task/mock_task_manager_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package task @@ -19,13 +19,16 @@ func (_m *mockTaskManager) Count(ctx context.Context, query *q.Query) (int64, er ret := _m.Called(ctx, query) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { + return rf(ctx, query) + } if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok { r0 = rf(ctx, query) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok { r1 = rf(ctx, query) } else { @@ -47,13 +50,16 @@ func (_m *mockTaskManager) Create(ctx context.Context, executionID int64, job *J ret := _m.Called(_ca...) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, *Job, ...map[string]interface{}) (int64, error)); ok { + return rf(ctx, executionID, job, extraAttrs...) + } if rf, ok := ret.Get(0).(func(context.Context, int64, *Job, ...map[string]interface{}) int64); ok { r0 = rf(ctx, executionID, job, extraAttrs...) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64, *Job, ...map[string]interface{}) error); ok { r1 = rf(ctx, executionID, job, extraAttrs...) } else { @@ -68,6 +74,10 @@ func (_m *mockTaskManager) ExecutionIDsByVendorAndStatus(ctx context.Context, ve ret := _m.Called(ctx, vendorType, status) var r0 []int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]int64, error)); ok { + return rf(ctx, vendorType, status) + } if rf, ok := ret.Get(0).(func(context.Context, string, string) []int64); ok { r0 = rf(ctx, vendorType, status) } else { @@ -76,7 +86,6 @@ func (_m *mockTaskManager) ExecutionIDsByVendorAndStatus(ctx context.Context, ve } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { r1 = rf(ctx, vendorType, status) } else { @@ -91,6 +100,10 @@ func (_m *mockTaskManager) Get(ctx context.Context, id int64) (*Task, error) { ret := _m.Called(ctx, id) var r0 *Task + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (*Task, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int64) *Task); ok { r0 = rf(ctx, id) } else { @@ -99,7 +112,6 @@ func (_m *mockTaskManager) Get(ctx context.Context, id int64) (*Task, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { r1 = rf(ctx, id) } else { @@ -114,6 +126,10 @@ func (_m *mockTaskManager) GetLog(ctx context.Context, id int64) ([]byte, error) ret := _m.Called(ctx, id) var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) ([]byte, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int64) []byte); ok { r0 = rf(ctx, id) } else { @@ -122,7 +138,6 @@ func (_m *mockTaskManager) GetLog(ctx context.Context, id int64) ([]byte, error) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { r1 = rf(ctx, id) } else { @@ -132,11 +147,41 @@ func (_m *mockTaskManager) GetLog(ctx context.Context, id int64) ([]byte, error) return r0, r1 } +// GetLogByJobID provides a mock function with given fields: ctx, jobID +func (_m *mockTaskManager) GetLogByJobID(ctx context.Context, jobID string) ([]byte, error) { + ret := _m.Called(ctx, jobID) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) ([]byte, error)); ok { + return rf(ctx, jobID) + } + if rf, ok := ret.Get(0).(func(context.Context, string) []byte); ok { + r0 = rf(ctx, jobID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, jobID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // List provides a mock function with given fields: ctx, query func (_m *mockTaskManager) List(ctx context.Context, query *q.Query) ([]*Task, error) { ret := _m.Called(ctx, query) var r0 []*Task + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*Task, error)); ok { + return rf(ctx, query) + } if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*Task); ok { r0 = rf(ctx, query) } else { @@ -145,7 +190,6 @@ func (_m *mockTaskManager) List(ctx context.Context, query *q.Query) ([]*Task, e } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok { r1 = rf(ctx, query) } else { diff --git a/src/pkg/task/task.go b/src/pkg/task/task.go index 05d529ad150e..88fae30929cb 100644 --- a/src/pkg/task/task.go +++ b/src/pkg/task/task.go @@ -53,6 +53,8 @@ type Manager interface { UpdateExtraAttrs(ctx context.Context, id int64, extraAttrs map[string]interface{}) (err error) // Get the log of the specified task GetLog(ctx context.Context, id int64) (log []byte, err error) + // GetLogByJobID get the log of specified job id + GetLogByJobID(ctx context.Context, jobID string) (log []byte, err error) // Count counts total of tasks according to the query. // Query the "ExtraAttrs" by setting 'query.Keywords["ExtraAttrs.key"]="value"' Count(ctx context.Context, query *q.Query) (int64, error) @@ -259,3 +261,7 @@ func (m *manager) UpdateStatusInBatch(ctx context.Context, jobIDs []string, stat func (m *manager) ExecutionIDsByVendorAndStatus(ctx context.Context, vendorType, status string) ([]int64, error) { return m.dao.ExecutionIDsByVendorAndStatus(ctx, vendorType, status) } + +func (m *manager) GetLogByJobID(ctx context.Context, jobID string) (log []byte, err error) { + return m.jsClient.GetJobLog(jobID) +} diff --git a/src/portal/README.md b/src/portal/README.md index ecb2b8d3eeaf..9f0fa6941bb1 100644 --- a/src/portal/README.md +++ b/src/portal/README.md @@ -10,27 +10,9 @@ Start ============ 1. npm install (should trigger 'npm postinstall') 2. npm run postinstall (if not triggered, manually run this step) -3. create "proxy.config.json" file with below content under "portal" directory, and replace "hostname" with an available Harbor hostname -4. npm run start -5. open your browser on https://localhost:4200 -```json -[ - { - "context": [ - "/api", - "/c", - "/i18n", - "/chartrepo", - "/LICENSE", - "/swagger.json", - "/swagger2.json", - "/devcenter-api-2.0", - "/swagger-ui.bundle.js" - ], - "target": "https://hostname", - "secure": false, - "changeOrigin": true, - "logLevel": "debug" - } -] -``` +3. copy "proxy.config.mjs.temp" file to "proxy.config.mjs" + `cp proxy.config.mjs.temp proxy.config.mjs` +4. Modify "proxy.config.mjs" to specify a Harbor server. And you can specify the agent if you work behind a corporate proxy +5. npm run start +6. open your browser on https://localhost:4200 + diff --git a/src/portal/angular.json b/src/portal/angular.json index 0845b47091e0..414ab96ca22b 100644 --- a/src/portal/angular.json +++ b/src/portal/angular.json @@ -83,7 +83,8 @@ "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "browserTarget": "harbor-portal:build" + "browserTarget": "harbor-portal:build", + "proxyConfig": "proxy.config.mjs" }, "configurations": { "production": { diff --git a/src/portal/package-lock.json b/src/portal/package-lock.json index 4a974cb83411..c12b4aa5c62b 100644 --- a/src/portal/package-lock.json +++ b/src/portal/package-lock.json @@ -51,6 +51,7 @@ "eslint-config-prettier": "^8.5.0", "eslint-plugin-prettier": "^4.0.0", "express": "^4.17.1", + "https-proxy-agent": "^5.0.1", "jasmine-core": "~4.5.0", "jasmine-spec-reporter": "~7.0.0", "karma": "^6.4.0", diff --git a/src/portal/package.json b/src/portal/package.json index fbec2d8135c3..0c0017818fa0 100644 --- a/src/portal/package.json +++ b/src/portal/package.json @@ -5,9 +5,9 @@ "angular-cli": {}, "scripts": { "postinstall": "node scripts/convert-yaml-to-json.js && ng-swagger-gen -i ng-swagger-gen/swagger.json -o ng-swagger-gen && node scripts/delete-swagger-json.js", - "start": "node --max_old_space_size=2048 ./node_modules/@angular/cli/bin/ng serve --ssl true --host 0.0.0.0 --proxy-config proxy.config.json", - "start:prod": "node --max_old_space_size=2048 ./node_modules/@angular/cli/bin/ng serve --ssl true --host 0.0.0.0 --proxy-config proxy.config.json --configuration production", - "start_default_port": "node --max_old_space_size=2048 ./node_modules/@angular/cli/bin/ng serve --ssl true --host 0.0.0.0 --port 443 --disable-host-check --proxy-config proxy.config.json", + "start": "node --max_old_space_size=2048 ./node_modules/@angular/cli/bin/ng serve --ssl true --host 0.0.0.0", + "start:prod": "node --max_old_space_size=2048 ./node_modules/@angular/cli/bin/ng serve --ssl true --host 0.0.0.0 --configuration production", + "start_default_port": "node --max_old_space_size=2048 ./node_modules/@angular/cli/bin/ng serve --ssl true --host 0.0.0.0 --port 443 --disable-host-check", "lint": "ng lint", "lint_fix": "ng lint --fix", "lint:style": "npx stylelint \"**/*.scss\"", @@ -70,6 +70,7 @@ "eslint-config-prettier": "^8.5.0", "eslint-plugin-prettier": "^4.0.0", "express": "^4.17.1", + "https-proxy-agent": "^5.0.1", "jasmine-core": "~4.5.0", "jasmine-spec-reporter": "~7.0.0", "karma": "^6.4.0", diff --git a/src/portal/proxy.config.mjs.temp b/src/portal/proxy.config.mjs.temp new file mode 100644 index 000000000000..ad58b87edb39 --- /dev/null +++ b/src/portal/proxy.config.mjs.temp @@ -0,0 +1,43 @@ +import HttpsProxyAgent from 'https-proxy-agent'; +// Define the proxy configuration +const HarborProxyConfig = [ + { + "context": [ + "/api", + "/c", + "/i18n", + "/chartrepo", + "/LICENSE", + "/swagger.json", + "/swagger2.json", + "/devcenter-api-2.0", + "/swagger-ui.bundle.js" + ], + "target": "${A Harbor server}", + "secure": false, + "changeOrigin": true, + "logLevel": "debug" + } +]; +// Define if you use agent +const useAgent = false; +// Specify an agent server, if empty, will read it from environment variable http_proxy or HTTP_PROXY +const specifiedAgentServer = "${An agent server}"; + +function setupForCorporateProxy(proxyConfig) { + if (useAgent) { + const agentServer = process.env.http_proxy || process.env.HTTP_PROXY || specifiedAgentServer; + if (agentServer) { + const agent = new HttpsProxyAgent(agentServer); + console.log('Using corporate agent server: ' + agentServer); + proxyConfig.forEach(function(entry) { + entry.agent = agent; + }); + } + } + return proxyConfig; +} + +export default setupForCorporateProxy(HarborProxyConfig); + + diff --git a/src/portal/src/app/base/left-side-nav/job-service-dashboard/worker-list/worker-list.component.html b/src/portal/src/app/base/left-side-nav/job-service-dashboard/worker-list/worker-list.component.html index 844f8603ea53..35730529ea15 100644 --- a/src/portal/src/app/base/left-side-nav/job-service-dashboard/worker-list/worker-list.component.html +++ b/src/portal/src/app/base/left-side-nav/job-service-dashboard/worker-list/worker-list.component.html @@ -105,6 +105,7 @@