From 161941d544c505d1261d3fd09745aa0238de8ff2 Mon Sep 17 00:00:00 2001 From: Lenin Alevski Date: Thu, 2 Apr 2020 09:57:59 -0700 Subject: [PATCH] Profiling endpoints for mcs (#1) start and stop profiling endpoints, test includes and many fixes for policies deleting duplicated file --- .gitignore | 2 + Makefile | 8 +- models/profiler_type.go | 95 ++++++++ models/profiling_start_request.go | 83 +++++++ models/start_profiling_item.go | 66 ++++++ models/start_profiling_list.go | 100 ++++++++ restapi/admin_policies.go | 32 +-- restapi/admin_policies_test.go | 31 ++- restapi/admin_profiliing_test.go | 118 +++++++++ restapi/admin_profiling.go | 146 ++++++++++++ restapi/client-admin.go | 15 +- restapi/configure_mcs.go | 2 + restapi/doc.go | 1 + restapi/embedded_spec.go | 224 ++++++++++++++++++ .../operations/admin_api/profiling_start.go | 88 +++++++ .../admin_api/profiling_start_parameters.go | 94 ++++++++ .../admin_api/profiling_start_responses.go | 133 +++++++++++ .../admin_api/profiling_start_urlbuilder.go | 101 ++++++++ .../operations/admin_api/profiling_stop.go | 88 +++++++ .../admin_api/profiling_stop_parameters.go | 62 +++++ .../admin_api/profiling_stop_responses.go | 132 +++++++++++ .../admin_api/profiling_stop_urlbuilder.go | 101 ++++++++ restapi/operations/mcs_api.go | 33 +++ restapi/user_login.go | 1 - swagger.yml | 79 +++++- 25 files changed, 1801 insertions(+), 34 deletions(-) create mode 100644 models/profiler_type.go create mode 100644 models/profiling_start_request.go create mode 100644 models/start_profiling_item.go create mode 100644 models/start_profiling_list.go create mode 100644 restapi/admin_profiliing_test.go create mode 100644 restapi/admin_profiling.go create mode 100644 restapi/operations/admin_api/profiling_start.go create mode 100644 restapi/operations/admin_api/profiling_start_parameters.go create mode 100644 restapi/operations/admin_api/profiling_start_responses.go create mode 100644 restapi/operations/admin_api/profiling_start_urlbuilder.go create mode 100644 restapi/operations/admin_api/profiling_stop.go create mode 100644 restapi/operations/admin_api/profiling_stop_parameters.go create mode 100644 restapi/operations/admin_api/profiling_stop_responses.go create mode 100644 restapi/operations/admin_api/profiling_stop_urlbuilder.go diff --git a/.gitignore b/.gitignore index a9b8827f9..f7da97cdc 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,6 @@ vendor/ # Ignore executables target/ mcs-server +mcs +!mcs/ !mcs-server/ diff --git a/Makefile b/Makefile index 5ccb55b14..e2204885d 100644 --- a/Makefile +++ b/Makefile @@ -7,11 +7,17 @@ mcs: swagger-gen: @echo "Generating swagger server code from yaml" - @swagger generate server -A mcs -f ./swagger.yml -r minio_copyright.txt + @swagger generate server -A mcs -f ./swagger.yml -r NOTICE build: @(cd portal-ui; yarn install; make build; cd ..) @(CGO_ENABLED=0 go build --tags kqueue --ldflags "-s -w" -o mcs ./cmd/mcs-server) +test: + @(go test ./restapi -v) + +coverage: + @(go test ./restapi -v -coverprofile=coverage.out && go tool cover -html=coverage.out && open coverage.html) + clean: @rm -vf mcs diff --git a/models/profiler_type.go b/models/profiler_type.go new file mode 100644 index 000000000..da388fc57 --- /dev/null +++ b/models/profiler_type.go @@ -0,0 +1,95 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" +) + +// ProfilerType profiler type +// +// swagger:model profilerType +type ProfilerType string + +const ( + + // ProfilerTypeCPU captures enum value "cpu" + ProfilerTypeCPU ProfilerType = "cpu" + + // ProfilerTypeMem captures enum value "mem" + ProfilerTypeMem ProfilerType = "mem" + + // ProfilerTypeBlock captures enum value "block" + ProfilerTypeBlock ProfilerType = "block" + + // ProfilerTypeMutex captures enum value "mutex" + ProfilerTypeMutex ProfilerType = "mutex" + + // ProfilerTypeTrace captures enum value "trace" + ProfilerTypeTrace ProfilerType = "trace" + + // ProfilerTypeThreads captures enum value "threads" + ProfilerTypeThreads ProfilerType = "threads" + + // ProfilerTypeGoroutines captures enum value "goroutines" + ProfilerTypeGoroutines ProfilerType = "goroutines" +) + +// for schema +var profilerTypeEnum []interface{} + +func init() { + var res []ProfilerType + if err := json.Unmarshal([]byte(`["cpu","mem","block","mutex","trace","threads","goroutines"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + profilerTypeEnum = append(profilerTypeEnum, v) + } +} + +func (m ProfilerType) validateProfilerTypeEnum(path, location string, value ProfilerType) error { + if err := validate.Enum(path, location, value, profilerTypeEnum); err != nil { + return err + } + return nil +} + +// Validate validates this profiler type +func (m ProfilerType) Validate(formats strfmt.Registry) error { + var res []error + + // value enum + if err := m.validateProfilerTypeEnum("", "body", m); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/models/profiling_start_request.go b/models/profiling_start_request.go new file mode 100644 index 000000000..c17106710 --- /dev/null +++ b/models/profiling_start_request.go @@ -0,0 +1,83 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// ProfilingStartRequest profiling start request +// +// swagger:model profilingStartRequest +type ProfilingStartRequest struct { + + // type + // Required: true + Type ProfilerType `json:"type"` +} + +// Validate validates this profiling start request +func (m *ProfilingStartRequest) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateType(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ProfilingStartRequest) validateType(formats strfmt.Registry) error { + + if err := m.Type.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("type") + } + return err + } + + return nil +} + +// MarshalBinary interface implementation +func (m *ProfilingStartRequest) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ProfilingStartRequest) UnmarshalBinary(b []byte) error { + var res ProfilingStartRequest + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/start_profiling_item.go b/models/start_profiling_item.go new file mode 100644 index 000000000..5ecb3a565 --- /dev/null +++ b/models/start_profiling_item.go @@ -0,0 +1,66 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// StartProfilingItem start profiling item +// +// swagger:model startProfilingItem +type StartProfilingItem struct { + + // error + Error string `json:"error,omitempty"` + + // node name + NodeName string `json:"nodeName,omitempty"` + + // success + Success bool `json:"success,omitempty"` +} + +// Validate validates this start profiling item +func (m *StartProfilingItem) Validate(formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *StartProfilingItem) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *StartProfilingItem) UnmarshalBinary(b []byte) error { + var res StartProfilingItem + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/start_profiling_list.go b/models/start_profiling_list.go new file mode 100644 index 000000000..0e2202e29 --- /dev/null +++ b/models/start_profiling_list.go @@ -0,0 +1,100 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// StartProfilingList start profiling list +// +// swagger:model startProfilingList +type StartProfilingList struct { + + // start results + StartResults []*StartProfilingItem `json:"startResults"` + + // number of start results + Total int64 `json:"total,omitempty"` +} + +// Validate validates this start profiling list +func (m *StartProfilingList) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateStartResults(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *StartProfilingList) validateStartResults(formats strfmt.Registry) error { + + if swag.IsZero(m.StartResults) { // not required + return nil + } + + for i := 0; i < len(m.StartResults); i++ { + if swag.IsZero(m.StartResults[i]) { // not required + continue + } + + if m.StartResults[i] != nil { + if err := m.StartResults[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("startResults" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *StartProfilingList) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *StartProfilingList) UnmarshalBinary(b []byte) error { + var res StartProfilingList + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/restapi/admin_policies.go b/restapi/admin_policies.go index dd405c65a..fd82a69b1 100644 --- a/restapi/admin_policies.go +++ b/restapi/admin_policies.go @@ -111,8 +111,7 @@ func parseRawPolicy(rawPolicy *rawPolicy) *models.Policy { // listPolicies() converts the map[string][]byte returned by client.listPolicies() // to []*models.Policy by iterating over each key in policyRawMap and // then using Unmarshal on the raw bytes to create a *models.Policy -func listPolicies(client MinioAdmin) ([]*models.Policy, error) { - ctx := context.Background() +func listPolicies(ctx context.Context, client MinioAdmin) ([]*models.Policy, error) { policyRawMap, err := client.listPolicies(ctx) var policies []*models.Policy if err != nil { @@ -132,6 +131,7 @@ func listPolicies(client MinioAdmin) ([]*models.Policy, error) { // getListPoliciesResponse performs listPolicies() and serializes it to the handler's output func getListPoliciesResponse() (*models.ListPoliciesResponse, error) { + ctx := context.Background() mAdmin, err := newMAdminClient() if err != nil { log.Println("error creating Madmin Client:", err) @@ -141,7 +141,7 @@ func getListPoliciesResponse() (*models.ListPoliciesResponse, error) { // defining the client to be used adminClient := adminClient{client: mAdmin} - policies, err := listPolicies(adminClient) + policies, err := listPolicies(ctx, adminClient) if err != nil { log.Println("error listing policies:", err) return nil, err @@ -155,8 +155,7 @@ func getListPoliciesResponse() (*models.ListPoliciesResponse, error) { } // removePolicy() calls MinIO server to remove a policy based on name. -func removePolicy(client MinioAdmin, name string) error { - ctx := context.Background() +func removePolicy(ctx context.Context, client MinioAdmin, name string) error { err := client.removePolicy(ctx, name) if err != nil { return err @@ -166,6 +165,7 @@ func removePolicy(client MinioAdmin, name string) error { // getRemovePolicyResponse() performs removePolicy() and serializes it to the handler's output func getRemovePolicyResponse(params admin_api.RemovePolicyParams) error { + ctx := context.Background() if params.Name == "" { log.Println("error policy name not in request") return errors.New(500, "error policy name not in request") @@ -179,7 +179,7 @@ func getRemovePolicyResponse(params admin_api.RemovePolicyParams) error { // defining the client to be used adminClient := adminClient{client: mAdmin} - if err := removePolicy(adminClient, params.Name); err != nil { + if err := removePolicy(ctx, adminClient, params.Name); err != nil { log.Println("error removing policy:", err) return err } @@ -190,12 +190,11 @@ func getRemovePolicyResponse(params admin_api.RemovePolicyParams) error { // addPolicy() takes name and policy in string format, policy // policy must be string in json format, in the future this will change // to a Policy struct{} - https://github.com/minio/minio/issues/9171 -func addPolicy(client MinioAdmin, name, policy string) (*models.Policy, error) { - ctx := context.Background() +func addPolicy(ctx context.Context, client MinioAdmin, name, policy string) (*models.Policy, error) { if err := client.addPolicy(ctx, name, policy); err != nil { return nil, err } - policyObject, err := policyInfo(client, name) + policyObject, err := policyInfo(ctx, client, name) if err != nil { return nil, err } @@ -204,6 +203,7 @@ func addPolicy(client MinioAdmin, name, policy string) (*models.Policy, error) { // getAddPolicyResponse performs addPolicy() and serializes it to the handler's output func getAddPolicyResponse(params *models.AddPolicyRequest) (*models.Policy, error) { + ctx := context.Background() if params == nil { log.Println("error AddPolicy body not in request") return nil, errors.New(500, "error AddPolicy body not in request") @@ -217,7 +217,7 @@ func getAddPolicyResponse(params *models.AddPolicyRequest) (*models.Policy, erro // create a MinIO Admin Client interface implementation // defining the client to be used adminClient := adminClient{client: mAdmin} - policy, err := addPolicy(adminClient, *params.Name, params.Definition) + policy, err := addPolicy(ctx, adminClient, *params.Name, params.Definition) if err != nil { log.Println("error adding policy") return nil, err @@ -229,8 +229,7 @@ func getAddPolicyResponse(params *models.AddPolicyRequest) (*models.Policy, erro // policyInfo() takes a policy name, obtains an []byte (represents a string in JSON format) // from the MinIO server and then convert it to *models.Policy , in the future this will change // to a Policy struct{} - https://github.com/minio/minio/issues/9171 -func policyInfo(client MinioAdmin, name string) (*models.Policy, error) { - ctx := context.Background() +func policyInfo(ctx context.Context, client MinioAdmin, name string) (*models.Policy, error) { policyRaw, err := client.getPolicy(ctx, name) if err != nil { return nil, err @@ -246,6 +245,7 @@ func policyInfo(client MinioAdmin, name string) (*models.Policy, error) { // getPolicyInfoResponse performs policyInfo() and serializes it to the handler's output func getPolicyInfoResponse(params admin_api.PolicyInfoParams) (*models.Policy, error) { + ctx := context.Background() mAdmin, err := newMAdminClient() if err != nil { log.Println("error creating Madmin Client:", err) @@ -254,7 +254,7 @@ func getPolicyInfoResponse(params admin_api.PolicyInfoParams) (*models.Policy, e // create a MinIO Admin Client interface implementation // defining the client to be used adminClient := adminClient{client: mAdmin} - policy, err := policyInfo(adminClient, params.Name) + policy, err := policyInfo(ctx, adminClient, params.Name) if err != nil { log.Println("error getting group info:", err) return nil, err @@ -263,12 +263,11 @@ func getPolicyInfoResponse(params admin_api.PolicyInfoParams) (*models.Policy, e } // setPolicy() calls MinIO server to assign policy to a group or user. -func setPolicy(client MinioAdmin, name, entityName string, entityType models.PolicyEntity) error { +func setPolicy(ctx context.Context, client MinioAdmin, name, entityName string, entityType models.PolicyEntity) error { isGroup := false if entityType == "group" { isGroup = true } - ctx := context.Background() if err := client.setPolicy(ctx, name, entityName, isGroup); err != nil { return err } @@ -277,6 +276,7 @@ func setPolicy(client MinioAdmin, name, entityName string, entityType models.Pol // getSetPolicyResponse() performs setPolicy() and serializes it to the handler's output func getSetPolicyResponse(name string, params *models.SetPolicyRequest) error { + ctx := context.Background() if name == "" { log.Println("error policy name not in request") return errors.New(500, "error policy name not in request") @@ -290,7 +290,7 @@ func getSetPolicyResponse(name string, params *models.SetPolicyRequest) error { // defining the client to be used adminClient := adminClient{client: mAdmin} - if err := setPolicy(adminClient, name, *params.EntityName, params.EntityType); err != nil { + if err := setPolicy(ctx, adminClient, name, *params.EntityName, params.EntityType); err != nil { log.Println("error setting policy:", err) return err } diff --git a/restapi/admin_policies_test.go b/restapi/admin_policies_test.go index 7b43340fd..3cbe4dda0 100644 --- a/restapi/admin_policies_test.go +++ b/restapi/admin_policies_test.go @@ -60,6 +60,7 @@ func (ac adminClientMock) setPolicy(ctx context.Context, policyName, entityName } func TestListPolicies(t *testing.T) { + ctx := context.Background() assert := assert.New(t) adminClient := adminClientMock{} mockPoliciesList := map[string][]byte{ @@ -116,7 +117,7 @@ func TestListPolicies(t *testing.T) { } // Test-1 : listPolicies() Get response from minio client with three Canned Policies and return the same number on listPolicies() function := "listPolicies()" - policiesList, err := listPolicies(adminClient) + policiesList, err := listPolicies(ctx, adminClient) if err != nil { t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) } @@ -147,7 +148,7 @@ func TestListPolicies(t *testing.T) { minioListPoliciesMock = func() (map[string][]byte, error) { return nil, errors.New("error") } - _, err = listPolicies(adminClient) + _, err = listPolicies(ctx, adminClient) if assert.Error(err) { assert.Equal("error", err.Error()) } @@ -158,13 +159,15 @@ func TestListPolicies(t *testing.T) { } return malformedData, nil } - _, err = listPolicies(adminClient) + _, err = listPolicies(ctx, adminClient) if assert.Error(err) { assert.NotEmpty(err.Error()) } } func TestRemovePolicy(t *testing.T) { + ctx := context.Background() + assert := assert.New(t) adminClient := adminClientMock{} // Test-1 : removePolicy() remove an existing policy @@ -173,19 +176,20 @@ func TestRemovePolicy(t *testing.T) { return nil } function := "removePolicy()" - if err := removePolicy(adminClient, policyToRemove); err != nil { + if err := removePolicy(ctx, adminClient, policyToRemove); err != nil { t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) } // Test-2 : removePolicy() Return error and see that the error is handled correctly and returned minioRemovePolicyMock = func(name string) error { return errors.New("error") } - if err := removePolicy(adminClient, policyToRemove); assert.Error(err) { + if err := removePolicy(ctx, adminClient, policyToRemove); assert.Error(err) { assert.Equal("error", err.Error()) } } func TestAddPolicy(t *testing.T) { + ctx := context.Background() assert := assert.New(t) adminClient := adminClientMock{} policyName := "new-policy" @@ -209,7 +213,7 @@ func TestAddPolicy(t *testing.T) { } // Test-1 : addPolicy() adds a new policy function := "addPolicy()" - policy, err := addPolicy(adminClient, policyName, policyDefinition) + policy, err := addPolicy(ctx, adminClient, policyName, policyDefinition) if err != nil { t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) } @@ -220,7 +224,7 @@ func TestAddPolicy(t *testing.T) { minioAddPolicyMock = func(name, policy string) error { return errors.New("error") } - if _, err := addPolicy(adminClient, policyName, policyDefinition); assert.Error(err) { + if _, err := addPolicy(ctx, adminClient, policyName, policyDefinition); assert.Error(err) { assert.Equal("error", err.Error()) } // Test-3 : addPolicy() got an error while retrieving policy @@ -230,19 +234,20 @@ func TestAddPolicy(t *testing.T) { minioGetPolicyMock = func(name string) (bytes []byte, err error) { return nil, errors.New("error") } - if _, err := addPolicy(adminClient, policyName, policyDefinition); assert.Error(err) { + if _, err := addPolicy(ctx, adminClient, policyName, policyDefinition); assert.Error(err) { assert.Equal("error", err.Error()) } // Test-4 : addPolicy() got an error while parsing policy minioGetPolicyMock = func(name string) (bytes []byte, err error) { return []byte("eaeaeaeae"), nil } - if _, err := addPolicy(adminClient, policyName, policyDefinition); assert.Error(err) { + if _, err := addPolicy(ctx, adminClient, policyName, policyDefinition); assert.Error(err) { assert.NotEmpty(err.Error()) } } func TestSetPolicy(t *testing.T) { + ctx := context.Background() assert := assert.New(t) adminClient := adminClientMock{} policyName := "readOnly" @@ -253,13 +258,13 @@ func TestSetPolicy(t *testing.T) { } // Test-1 : setPolicy() set policy to user function := "setPolicy()" - err := setPolicy(adminClient, policyName, entityName, entityObject) + err := setPolicy(ctx, adminClient, policyName, entityName, entityObject) if err != nil { t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) } // Test-2 : setPolicy() set policy to group entityObject = models.PolicyEntityGroup - err = setPolicy(adminClient, policyName, entityName, entityObject) + err = setPolicy(ctx, adminClient, policyName, entityName, entityObject) if err != nil { t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) } @@ -268,7 +273,7 @@ func TestSetPolicy(t *testing.T) { minioSetPolicyMock = func(policyName, entityName string, isGroup bool) error { return errors.New("error") } - if err := setPolicy(adminClient, policyName, entityName, entityObject); assert.Error(err) { + if err := setPolicy(ctx, adminClient, policyName, entityName, entityObject); assert.Error(err) { assert.Equal("error", err.Error()) } // Test-4 : setPolicy() set policy to group and get error @@ -276,7 +281,7 @@ func TestSetPolicy(t *testing.T) { minioSetPolicyMock = func(policyName, entityName string, isGroup bool) error { return errors.New("error") } - if err := setPolicy(adminClient, policyName, entityName, entityObject); assert.Error(err) { + if err := setPolicy(ctx, adminClient, policyName, entityName, entityObject); assert.Error(err) { assert.Equal("error", err.Error()) } } diff --git a/restapi/admin_profiliing_test.go b/restapi/admin_profiliing_test.go new file mode 100644 index 000000000..264121068 --- /dev/null +++ b/restapi/admin_profiliing_test.go @@ -0,0 +1,118 @@ +// This file is part of MinIO Kubernetes Cloud +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package restapi + +import ( + "bytes" + "context" + "io" + "testing" + + "errors" + + "github.com/minio/m3/mcs/models" + "github.com/minio/minio/pkg/madmin" + "github.com/stretchr/testify/assert" +) + +var minioStartProfiling func(profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) +var minioStopProfiling func() (io.ReadCloser, error) + +// mock function of startProfiling() +func (ac adminClientMock) startProfiling(ctx context.Context, profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) { + return minioStartProfiling(profiler) +} + +// mock function of stopProfiling() +func (ac adminClientMock) stopProfiling(ctx context.Context) (io.ReadCloser, error) { + return minioStopProfiling() +} + +func TestStartProfiling(t *testing.T) { + ctx := context.Background() + assert := assert.New(t) + adminClient := adminClientMock{} + // Test-1 : startProfiling() Get response from Minio server with one profiling object + // mock function response from startProfiling() + minioStartProfiling = func(profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) { + return []madmin.StartProfilingResult{ + { + NodeName: "http://127.0.0.1:9000/", + Success: true, + Error: "", + }, + { + NodeName: "http://127.0.0.1:9001/", + Success: true, + Error: "", + }, + }, nil + } + function := "startProfiling()" + cpuProfiler := models.ProfilerType("cpu") + startProfilingResults, err := startProfiling(ctx, adminClient, cpuProfiler) + if err != nil { + t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) + } + assert.Equal(2, len(startProfilingResults)) + //Test-2 : startProfiling() Correctly handles errors returned by Minio + //mock function response from startProfiling() + minioStartProfiling = func(profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) { + return nil, errors.New("error") + } + _, err = startProfiling(ctx, adminClient, cpuProfiler) + if assert.Error(err) { + assert.Equal("error", err.Error()) + } +} + +// Implementing fake closingBuffer need it to mock stopProfiling() (io.ReadCloser, error) +type ClosingBuffer struct { + *bytes.Buffer +} + +// Implementing a fake Close function for io.ReadCloser +func (cb *ClosingBuffer) Close() error { + return nil +} + +func TestStopProfiling(t *testing.T) { + ctx := context.Background() + assert := assert.New(t) + adminClient := adminClientMock{} + // Test-1 : stopProfiling() Get response from Minio server and that response is a readCloser interface + // mock function response from startProfiling() + minioStopProfiling = func() (io.ReadCloser, error) { + return &ClosingBuffer{bytes.NewBufferString("In memory string eaeae")}, nil + } + function := "stopProfiling()" + readCloserInterface, err := stopProfiling(ctx, adminClient) + if err != nil { + t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) + } + // Check return type of stopProfiling is io.ReadCloser by doing a cast + assert.NotPanics(func() { readCloserInterface.(io.ReadCloser).Close() }) + // Test-2 : stopProfiling() Correctly handles errors returned by Minio + // mock function response from stopProfiling() + minioStopProfiling = func() (io.ReadCloser, error) { + return nil, errors.New("error") + } + _, err = stopProfiling(ctx, adminClient) + if assert.Error(err) { + assert.Equal("error", err.Error()) + } +} diff --git a/restapi/admin_profiling.go b/restapi/admin_profiling.go new file mode 100644 index 000000000..643b66bb9 --- /dev/null +++ b/restapi/admin_profiling.go @@ -0,0 +1,146 @@ +// This file is part of MinIO Kubernetes Cloud +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package restapi + +import ( + "context" + "io" + "log" + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/swag" + "github.com/minio/m3/mcs/models" + "github.com/minio/m3/mcs/restapi/operations" + "github.com/minio/m3/mcs/restapi/operations/admin_api" + "github.com/minio/minio/pkg/madmin" +) + +func registerProfilingHandler(api *operations.McsAPI) { + // Start Profiling + api.AdminAPIProfilingStartHandler = admin_api.ProfilingStartHandlerFunc(func(params admin_api.ProfilingStartParams, principal interface{}) middleware.Responder { + profilingStartResponse, err := getProfilingStartResponse(params.Body) + if err != nil { + return admin_api.NewProfilingStartDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())}) + } + return admin_api.NewProfilingStartCreated().WithPayload(profilingStartResponse) + }) + // Stop and download profiling data + api.AdminAPIProfilingStopHandler = admin_api.ProfilingStopHandlerFunc(func(params admin_api.ProfilingStopParams, principal interface{}) middleware.Responder { + profilingStopResponse, err := getProfilingStopResponse() + if err != nil { + return admin_api.NewProfilingStopDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())}) + } + // Custom response writer to set the content-disposition header to tell the + // HTTP client the name and extension of the file we are returning + return middleware.ResponderFunc(func(w http.ResponseWriter, _ runtime.Producer) { + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Disposition", "attachment; filename=profile.zip") + if _, err := io.Copy(w, profilingStopResponse); err != nil { + log.Println(err) + } else { + if err := profilingStopResponse.Close(); err != nil { + log.Println(err) + } + } + }) + }) +} + +// startProfiling() starts the profiling on the Minio server +// Enable 1 of the 7 profiling mechanisms: "cpu","mem","block","mutex","trace","threads","goroutines" +// in the Minio server, returns []*models.StartProfilingItem that contains individual status of this operation +// for each Minio node, ie: +// +// { +// "Success": true, +// "nodeName": "127.0.0.1:9000" +// "error": "" +// } +func startProfiling(ctx context.Context, client MinioAdmin, profilerType models.ProfilerType) ([]*models.StartProfilingItem, error) { + profilingResults, err := client.startProfiling(ctx, madmin.ProfilerType(profilerType)) + if err != nil { + return nil, err + } + var items []*models.StartProfilingItem + for _, result := range profilingResults { + items = append(items, &models.StartProfilingItem{ + Success: result.Success, + Error: result.Error, + NodeName: result.NodeName, + }) + } + return items, nil +} + +// getProfilingStartResponse performs startProfiling() and serializes it to the handler's output +func getProfilingStartResponse(params *models.ProfilingStartRequest) (*models.StartProfilingList, error) { + ctx := context.Background() + if params == nil { + log.Println("error profiling type not in body request") + return nil, errors.New(500, "error AddPolicy body not in request") + } + mAdmin, err := newMAdminClient() + if err != nil { + log.Println("error creating Madmin Client:", err) + return nil, err + } + // create a MinIO Admin Client interface implementation + // defining the client to be used + adminClient := adminClient{client: mAdmin} + profilingItems, err := startProfiling(ctx, adminClient, params.Type) + if err != nil { + log.Println("error starting profiling:", err) + return nil, err + } + profilingList := &models.StartProfilingList{ + StartResults: profilingItems, + Total: int64(len(profilingItems)), + } + return profilingList, nil +} + +// stopProfiling() stop the profiling on the Minio server and returns +// the generated Zip file as io.ReadCloser +func stopProfiling(ctx context.Context, client MinioAdmin) (io.ReadCloser, error) { + profilingData, err := client.stopProfiling(ctx) + if err != nil { + return nil, err + } + return profilingData, nil +} + +// getProfilingStopResponse() performs setPolicy() and serializes it to the handler's output +func getProfilingStopResponse() (io.ReadCloser, error) { + ctx := context.Background() + mAdmin, err := newMAdminClient() + if err != nil { + log.Println("error creating Madmin Client:", err) + return nil, err + } + // create a MinIO Admin Client interface implementation + // defining the client to be used + adminClient := adminClient{client: mAdmin} + profilingData, err := stopProfiling(ctx, adminClient) + if err != nil { + log.Println("error stopping profiling:", err) + return nil, err + } + return profilingData, nil +} diff --git a/restapi/client-admin.go b/restapi/client-admin.go index 0a935f57b..4c6daf2fa 100644 --- a/restapi/client-admin.go +++ b/restapi/client-admin.go @@ -20,6 +20,7 @@ import ( "context" "crypto/tls" "hash/fnv" + "io" "net" "net/http" "net/url" @@ -141,7 +142,7 @@ type MinioAdmin interface { addUser(ctx context.Context, acessKey, SecretKey string) error listGroups(ctx context.Context) ([]string, error) updateGroupMembers(ctx context.Context, greq madmin.GroupAddRemove) error - getGroupDescription(ctx context.Context, grouo string) (*madmin.GroupDesc, error) + getGroupDescription(ctx context.Context, group string) (*madmin.GroupDesc, error) setGroupStatus(ctx context.Context, group string, status madmin.GroupStatus) error listPolicies(ctx context.Context) (map[string][]byte, error) getPolicy(ctx context.Context, name string) ([]byte, error) @@ -153,6 +154,8 @@ type MinioAdmin interface { setConfigKV(ctx context.Context, kv string) (err error) serviceRestart(ctx context.Context) error serverInfo(ctx context.Context) (madmin.InfoMessage, error) + startProfiling(ctx context.Context, profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) + stopProfiling(ctx context.Context) (io.ReadCloser, error) } // Interface implementation @@ -243,6 +246,16 @@ func (ac adminClient) serverInfo(ctx context.Context) (madmin.InfoMessage, error return ac.client.ServerInfo(ctx) } +// implements madmin.StartProfiling() +func (ac adminClient) startProfiling(ctx context.Context, profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) { + return ac.client.StartProfiling(ctx, profiler) +} + +// implements madmin.DownloadProfilingData() +func (ac adminClient) stopProfiling(ctx context.Context) (io.ReadCloser, error) { + return ac.client.DownloadProfilingData(ctx) +} + func newMAdminClient() (*madmin.AdminClient, error) { endpoint := getMinIOServer() accessKeyID := getAccessKey() diff --git a/restapi/configure_mcs.go b/restapi/configure_mcs.go index a8871fc27..31957d486 100644 --- a/restapi/configure_mcs.go +++ b/restapi/configure_mcs.go @@ -83,6 +83,8 @@ func configureAPI(api *operations.McsAPI) http.Handler { registerBucketEventsHandlers(api) // Register service handlers registerServiceHandlers(api) + // Register profiling handlers + registerProfilingHandler(api) api.PreServerShutdown = func() {} diff --git a/restapi/doc.go b/restapi/doc.go index bd18dcc26..957007825 100644 --- a/restapi/doc.go +++ b/restapi/doc.go @@ -28,6 +28,7 @@ // - application/json // // Produces: +// - application/octet-stream // - application/json // // swagger:meta diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go index 49ac859a4..cc8451737 100644 --- a/restapi/embedded_spec.go +++ b/restapi/embedded_spec.go @@ -701,6 +701,65 @@ func init() { } } }, + "/api/v1/profiling/start": { + "post": { + "tags": [ + "AdminAPI" + ], + "summary": "Start recording profile data", + "operationId": "ProfilingStart", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/profilingStartRequest" + } + } + ], + "responses": { + "201": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/startProfilingList" + } + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + } + }, + "/api/v1/profiling/stop": { + "post": { + "produces": [ + "application/octet-stream" + ], + "tags": [ + "AdminAPI" + ], + "summary": "Stop and download profile data", + "operationId": "ProfilingStop", + "responses": { + "201": { + "description": "A successful response.", + "schema": { + "type": "file" + } + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + } + }, "/api/v1/service/restart": { "post": { "tags": [ @@ -1185,6 +1244,29 @@ func init() { "principal": { "type": "string" }, + "profilerType": { + "type": "string", + "enum": [ + "cpu", + "mem", + "block", + "mutex", + "trace", + "threads", + "goroutines" + ] + }, + "profilingStartRequest": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/definitions/profilerType" + } + } + }, "setBucketPolicyRequest": { "type": "object", "required": [ @@ -1230,6 +1312,36 @@ func init() { } } }, + "startProfilingItem": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "nodeName": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, + "startProfilingList": { + "type": "object", + "properties": { + "startResults": { + "type": "array", + "items": { + "$ref": "#/definitions/startProfilingItem" + } + }, + "total": { + "type": "integer", + "format": "int64", + "title": "number of start results" + } + } + }, "statement": { "type": "object", "properties": { @@ -1970,6 +2082,65 @@ func init() { } } }, + "/api/v1/profiling/start": { + "post": { + "tags": [ + "AdminAPI" + ], + "summary": "Start recording profile data", + "operationId": "ProfilingStart", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/profilingStartRequest" + } + } + ], + "responses": { + "201": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/startProfilingList" + } + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + } + }, + "/api/v1/profiling/stop": { + "post": { + "produces": [ + "application/octet-stream" + ], + "tags": [ + "AdminAPI" + ], + "summary": "Stop and download profile data", + "operationId": "ProfilingStop", + "responses": { + "201": { + "description": "A successful response.", + "schema": { + "type": "file" + } + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + } + }, "/api/v1/service/restart": { "post": { "tags": [ @@ -2454,6 +2625,29 @@ func init() { "principal": { "type": "string" }, + "profilerType": { + "type": "string", + "enum": [ + "cpu", + "mem", + "block", + "mutex", + "trace", + "threads", + "goroutines" + ] + }, + "profilingStartRequest": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/definitions/profilerType" + } + } + }, "setBucketPolicyRequest": { "type": "object", "required": [ @@ -2499,6 +2693,36 @@ func init() { } } }, + "startProfilingItem": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "nodeName": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, + "startProfilingList": { + "type": "object", + "properties": { + "startResults": { + "type": "array", + "items": { + "$ref": "#/definitions/startProfilingItem" + } + }, + "total": { + "type": "integer", + "format": "int64", + "title": "number of start results" + } + } + }, "statement": { "type": "object", "properties": { diff --git a/restapi/operations/admin_api/profiling_start.go b/restapi/operations/admin_api/profiling_start.go new file mode 100644 index 000000000..14fa5d27c --- /dev/null +++ b/restapi/operations/admin_api/profiling_start.go @@ -0,0 +1,88 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package admin_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// ProfilingStartHandlerFunc turns a function with the right signature into a profiling start handler +type ProfilingStartHandlerFunc func(ProfilingStartParams, interface{}) middleware.Responder + +// Handle executing the request and returning a response +func (fn ProfilingStartHandlerFunc) Handle(params ProfilingStartParams, principal interface{}) middleware.Responder { + return fn(params, principal) +} + +// ProfilingStartHandler interface for that can handle valid profiling start params +type ProfilingStartHandler interface { + Handle(ProfilingStartParams, interface{}) middleware.Responder +} + +// NewProfilingStart creates a new http.Handler for the profiling start operation +func NewProfilingStart(ctx *middleware.Context, handler ProfilingStartHandler) *ProfilingStart { + return &ProfilingStart{Context: ctx, Handler: handler} +} + +/*ProfilingStart swagger:route POST /api/v1/profiling/start AdminAPI profilingStart + +Start recording profile data + +*/ +type ProfilingStart struct { + Context *middleware.Context + Handler ProfilingStartHandler +} + +func (o *ProfilingStart) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + r = rCtx + } + var Params = NewProfilingStartParams() + + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + r = aCtx + } + var principal interface{} + if uprinc != nil { + principal = uprinc + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/restapi/operations/admin_api/profiling_start_parameters.go b/restapi/operations/admin_api/profiling_start_parameters.go new file mode 100644 index 000000000..99f021529 --- /dev/null +++ b/restapi/operations/admin_api/profiling_start_parameters.go @@ -0,0 +1,94 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package admin_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "io" + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + + "github.com/minio/m3/mcs/models" +) + +// NewProfilingStartParams creates a new ProfilingStartParams object +// no default values defined in spec. +func NewProfilingStartParams() ProfilingStartParams { + + return ProfilingStartParams{} +} + +// ProfilingStartParams contains all the bound params for the profiling start operation +// typically these are obtained from a http.Request +// +// swagger:parameters ProfilingStart +type ProfilingStartParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: body + */ + Body *models.ProfilingStartRequest +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewProfilingStartParams() beforehand. +func (o *ProfilingStartParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.ProfilingStartRequest + if err := route.Consumer.Consume(r.Body, &body); err != nil { + if err == io.EOF { + res = append(res, errors.Required("body", "body")) + } else { + res = append(res, errors.NewParseError("body", "body", "", err)) + } + } else { + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.Body = &body + } + } + } else { + res = append(res, errors.Required("body", "body")) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/restapi/operations/admin_api/profiling_start_responses.go b/restapi/operations/admin_api/profiling_start_responses.go new file mode 100644 index 000000000..bee1e8c03 --- /dev/null +++ b/restapi/operations/admin_api/profiling_start_responses.go @@ -0,0 +1,133 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package admin_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/minio/m3/mcs/models" +) + +// ProfilingStartCreatedCode is the HTTP code returned for type ProfilingStartCreated +const ProfilingStartCreatedCode int = 201 + +/*ProfilingStartCreated A successful response. + +swagger:response profilingStartCreated +*/ +type ProfilingStartCreated struct { + + /* + In: Body + */ + Payload *models.StartProfilingList `json:"body,omitempty"` +} + +// NewProfilingStartCreated creates ProfilingStartCreated with default headers values +func NewProfilingStartCreated() *ProfilingStartCreated { + + return &ProfilingStartCreated{} +} + +// WithPayload adds the payload to the profiling start created response +func (o *ProfilingStartCreated) WithPayload(payload *models.StartProfilingList) *ProfilingStartCreated { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the profiling start created response +func (o *ProfilingStartCreated) SetPayload(payload *models.StartProfilingList) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ProfilingStartCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(201) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +/*ProfilingStartDefault Generic error response. + +swagger:response profilingStartDefault +*/ +type ProfilingStartDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewProfilingStartDefault creates ProfilingStartDefault with default headers values +func NewProfilingStartDefault(code int) *ProfilingStartDefault { + if code <= 0 { + code = 500 + } + + return &ProfilingStartDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the profiling start default response +func (o *ProfilingStartDefault) WithStatusCode(code int) *ProfilingStartDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the profiling start default response +func (o *ProfilingStartDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the profiling start default response +func (o *ProfilingStartDefault) WithPayload(payload *models.Error) *ProfilingStartDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the profiling start default response +func (o *ProfilingStartDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ProfilingStartDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/restapi/operations/admin_api/profiling_start_urlbuilder.go b/restapi/operations/admin_api/profiling_start_urlbuilder.go new file mode 100644 index 000000000..89b9c72a4 --- /dev/null +++ b/restapi/operations/admin_api/profiling_start_urlbuilder.go @@ -0,0 +1,101 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package admin_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// ProfilingStartURL generates an URL for the profiling start operation +type ProfilingStartURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *ProfilingStartURL) WithBasePath(bp string) *ProfilingStartURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *ProfilingStartURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *ProfilingStartURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/api/v1/profiling/start" + + _basePath := o._basePath + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *ProfilingStartURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *ProfilingStartURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *ProfilingStartURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on ProfilingStartURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on ProfilingStartURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *ProfilingStartURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/restapi/operations/admin_api/profiling_stop.go b/restapi/operations/admin_api/profiling_stop.go new file mode 100644 index 000000000..2d0677be7 --- /dev/null +++ b/restapi/operations/admin_api/profiling_stop.go @@ -0,0 +1,88 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package admin_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// ProfilingStopHandlerFunc turns a function with the right signature into a profiling stop handler +type ProfilingStopHandlerFunc func(ProfilingStopParams, interface{}) middleware.Responder + +// Handle executing the request and returning a response +func (fn ProfilingStopHandlerFunc) Handle(params ProfilingStopParams, principal interface{}) middleware.Responder { + return fn(params, principal) +} + +// ProfilingStopHandler interface for that can handle valid profiling stop params +type ProfilingStopHandler interface { + Handle(ProfilingStopParams, interface{}) middleware.Responder +} + +// NewProfilingStop creates a new http.Handler for the profiling stop operation +func NewProfilingStop(ctx *middleware.Context, handler ProfilingStopHandler) *ProfilingStop { + return &ProfilingStop{Context: ctx, Handler: handler} +} + +/*ProfilingStop swagger:route POST /api/v1/profiling/stop AdminAPI profilingStop + +Stop and download profile data + +*/ +type ProfilingStop struct { + Context *middleware.Context + Handler ProfilingStopHandler +} + +func (o *ProfilingStop) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + r = rCtx + } + var Params = NewProfilingStopParams() + + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + r = aCtx + } + var principal interface{} + if uprinc != nil { + principal = uprinc + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/restapi/operations/admin_api/profiling_stop_parameters.go b/restapi/operations/admin_api/profiling_stop_parameters.go new file mode 100644 index 000000000..3330f9197 --- /dev/null +++ b/restapi/operations/admin_api/profiling_stop_parameters.go @@ -0,0 +1,62 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package admin_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" +) + +// NewProfilingStopParams creates a new ProfilingStopParams object +// no default values defined in spec. +func NewProfilingStopParams() ProfilingStopParams { + + return ProfilingStopParams{} +} + +// ProfilingStopParams contains all the bound params for the profiling stop operation +// typically these are obtained from a http.Request +// +// swagger:parameters ProfilingStop +type ProfilingStopParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewProfilingStopParams() beforehand. +func (o *ProfilingStopParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/restapi/operations/admin_api/profiling_stop_responses.go b/restapi/operations/admin_api/profiling_stop_responses.go new file mode 100644 index 000000000..8ad300f1b --- /dev/null +++ b/restapi/operations/admin_api/profiling_stop_responses.go @@ -0,0 +1,132 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package admin_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "io" + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/minio/m3/mcs/models" +) + +// ProfilingStopCreatedCode is the HTTP code returned for type ProfilingStopCreated +const ProfilingStopCreatedCode int = 201 + +/*ProfilingStopCreated A successful response. + +swagger:response profilingStopCreated +*/ +type ProfilingStopCreated struct { + + /* + In: Body + */ + Payload io.ReadCloser `json:"body,omitempty"` +} + +// NewProfilingStopCreated creates ProfilingStopCreated with default headers values +func NewProfilingStopCreated() *ProfilingStopCreated { + + return &ProfilingStopCreated{} +} + +// WithPayload adds the payload to the profiling stop created response +func (o *ProfilingStopCreated) WithPayload(payload io.ReadCloser) *ProfilingStopCreated { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the profiling stop created response +func (o *ProfilingStopCreated) SetPayload(payload io.ReadCloser) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ProfilingStopCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(201) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +/*ProfilingStopDefault Generic error response. + +swagger:response profilingStopDefault +*/ +type ProfilingStopDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewProfilingStopDefault creates ProfilingStopDefault with default headers values +func NewProfilingStopDefault(code int) *ProfilingStopDefault { + if code <= 0 { + code = 500 + } + + return &ProfilingStopDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the profiling stop default response +func (o *ProfilingStopDefault) WithStatusCode(code int) *ProfilingStopDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the profiling stop default response +func (o *ProfilingStopDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the profiling stop default response +func (o *ProfilingStopDefault) WithPayload(payload *models.Error) *ProfilingStopDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the profiling stop default response +func (o *ProfilingStopDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ProfilingStopDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/restapi/operations/admin_api/profiling_stop_urlbuilder.go b/restapi/operations/admin_api/profiling_stop_urlbuilder.go new file mode 100644 index 000000000..44cab739a --- /dev/null +++ b/restapi/operations/admin_api/profiling_stop_urlbuilder.go @@ -0,0 +1,101 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +package admin_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// ProfilingStopURL generates an URL for the profiling stop operation +type ProfilingStopURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *ProfilingStopURL) WithBasePath(bp string) *ProfilingStopURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *ProfilingStopURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *ProfilingStopURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/api/v1/profiling/stop" + + _basePath := o._basePath + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *ProfilingStopURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *ProfilingStopURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *ProfilingStopURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on ProfilingStopURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on ProfilingStopURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *ProfilingStopURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/restapi/operations/mcs_api.go b/restapi/operations/mcs_api.go index dcfccdb52..18bf61bac 100644 --- a/restapi/operations/mcs_api.go +++ b/restapi/operations/mcs_api.go @@ -59,6 +59,7 @@ func NewMcsAPI(spec *loads.Document) *McsAPI { JSONConsumer: runtime.JSONConsumer(), + BinProducer: runtime.ByteStreamProducer(), JSONProducer: runtime.JSONProducer(), AdminAPIAddGroupHandler: admin_api.AddGroupHandlerFunc(func(params admin_api.AddGroupParams, principal interface{}) middleware.Responder { @@ -115,6 +116,12 @@ func NewMcsAPI(spec *loads.Document) *McsAPI { AdminAPIPolicyInfoHandler: admin_api.PolicyInfoHandlerFunc(func(params admin_api.PolicyInfoParams, principal interface{}) middleware.Responder { return middleware.NotImplemented("operation admin_api.PolicyInfo has not yet been implemented") }), + AdminAPIProfilingStartHandler: admin_api.ProfilingStartHandlerFunc(func(params admin_api.ProfilingStartParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation admin_api.ProfilingStart has not yet been implemented") + }), + AdminAPIProfilingStopHandler: admin_api.ProfilingStopHandlerFunc(func(params admin_api.ProfilingStopParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation admin_api.ProfilingStop has not yet been implemented") + }), AdminAPIRemoveGroupHandler: admin_api.RemoveGroupHandlerFunc(func(params admin_api.RemoveGroupParams, principal interface{}) middleware.Responder { return middleware.NotImplemented("operation admin_api.RemoveGroup has not yet been implemented") }), @@ -168,6 +175,9 @@ type McsAPI struct { // - application/json JSONConsumer runtime.Consumer + // BinProducer registers a producer for the following mime types: + // - application/octet-stream + BinProducer runtime.Producer // JSONProducer registers a producer for the following mime types: // - application/json JSONProducer runtime.Producer @@ -215,6 +225,10 @@ type McsAPI struct { UserAPIMakeBucketHandler user_api.MakeBucketHandler // AdminAPIPolicyInfoHandler sets the operation handler for the policy info operation AdminAPIPolicyInfoHandler admin_api.PolicyInfoHandler + // AdminAPIProfilingStartHandler sets the operation handler for the profiling start operation + AdminAPIProfilingStartHandler admin_api.ProfilingStartHandler + // AdminAPIProfilingStopHandler sets the operation handler for the profiling stop operation + AdminAPIProfilingStopHandler admin_api.ProfilingStopHandler // AdminAPIRemoveGroupHandler sets the operation handler for the remove group operation AdminAPIRemoveGroupHandler admin_api.RemoveGroupHandler // AdminAPIRemovePolicyHandler sets the operation handler for the remove policy operation @@ -289,6 +303,9 @@ func (o *McsAPI) Validate() error { unregistered = append(unregistered, "JSONConsumer") } + if o.BinProducer == nil { + unregistered = append(unregistered, "BinProducer") + } if o.JSONProducer == nil { unregistered = append(unregistered, "JSONProducer") } @@ -351,6 +368,12 @@ func (o *McsAPI) Validate() error { if o.AdminAPIPolicyInfoHandler == nil { unregistered = append(unregistered, "admin_api.PolicyInfoHandler") } + if o.AdminAPIProfilingStartHandler == nil { + unregistered = append(unregistered, "admin_api.ProfilingStartHandler") + } + if o.AdminAPIProfilingStopHandler == nil { + unregistered = append(unregistered, "admin_api.ProfilingStopHandler") + } if o.AdminAPIRemoveGroupHandler == nil { unregistered = append(unregistered, "admin_api.RemoveGroupHandler") } @@ -423,6 +446,8 @@ func (o *McsAPI) ProducersFor(mediaTypes []string) map[string]runtime.Producer { result := make(map[string]runtime.Producer, len(mediaTypes)) for _, mt := range mediaTypes { switch mt { + case "application/octet-stream": + result["application/octet-stream"] = o.BinProducer case "application/json": result["application/json"] = o.JSONProducer } @@ -537,6 +562,14 @@ func (o *McsAPI) initHandlerCache() { o.handlers["GET"] = make(map[string]http.Handler) } o.handlers["GET"]["/api/v1/policies/{name}"] = admin_api.NewPolicyInfo(o.context, o.AdminAPIPolicyInfoHandler) + if o.handlers["POST"] == nil { + o.handlers["POST"] = make(map[string]http.Handler) + } + o.handlers["POST"]["/api/v1/profiling/start"] = admin_api.NewProfilingStart(o.context, o.AdminAPIProfilingStartHandler) + if o.handlers["POST"] == nil { + o.handlers["POST"] = make(map[string]http.Handler) + } + o.handlers["POST"]["/api/v1/profiling/stop"] = admin_api.NewProfilingStop(o.context, o.AdminAPIProfilingStopHandler) if o.handlers["DELETE"] == nil { o.handlers["DELETE"] = make(map[string]http.Handler) } diff --git a/restapi/user_login.go b/restapi/user_login.go index 79e04d72c..5d4a0c7e6 100644 --- a/restapi/user_login.go +++ b/restapi/user_login.go @@ -69,7 +69,6 @@ func login(mc McCmd, accessKey, secretKey *string) (*string, error) { // Probe the credentials cfg, pErr := mc.BuildS3Config(getMinIOServer(), *accessKey, *secretKey, "", "auto") if pErr != nil { - log.Println(pErr) return nil, ErrInvalidCredentials } // if we made it here, the credentials work, generate a session diff --git a/swagger.yml b/swagger.yml index d038a650a..1a5e4c226 100644 --- a/swagger.yml +++ b/swagger.yml @@ -270,7 +270,7 @@ paths: tags: - AdminAPI delete: - summary: Remove group + summary: Remove group operationId: RemoveGroup parameters: - name: name @@ -536,6 +536,44 @@ paths: security: [] tags: - UserAPI + /api/v1/profiling/start: + post: + summary: Start recording profile data + operationId: ProfilingStart + parameters: + - name: body + in: body + required: true + schema: + $ref: '#/definitions/profilingStartRequest' + responses: + 201: + description: A successful response. + schema: + $ref: '#/definitions/startProfilingList' + default: + description: Generic error response. + schema: + $ref: "#/definitions/error" + tags: + - AdminAPI + /api/v1/profiling/stop: + post: + summary: Stop and download profile data + operationId: ProfilingStop + produces: + - application/octet-stream + responses: + 201: + description: A successful response. + schema: + type: file + default: + description: Generic error response. + schema: + $ref: "#/definitions/error" + tags: + - AdminAPI definitions: bucketAccess: type: string @@ -819,7 +857,7 @@ definitions: - access properties: access: - $ref: "#/definitions/bucketAccess" + $ref: "#/definitions/bucketAccess" loginDetails: type: object properties: @@ -846,3 +884,40 @@ definitions: # Structure that holds the `Bearer {TOKEN}` present on authenticated requests principal: type: string + startProfilingItem: + type: object + properties: + nodeName: + type: string + success: + type: boolean + error: + type: string + startProfilingList: + type: object + properties: + total: + type: integer + format: int64 + title: number of start results + startResults: + type: array + items: + $ref: "#/definitions/startProfilingItem" + profilerType: + type: string + enum: + - cpu + - mem + - block + - mutex + - trace + - threads + - goroutines + profilingStartRequest: + type: object + required: + - type + properties: + type: + $ref: "#/definitions/profilerType" \ No newline at end of file