github.com/minio/console@v1.4.1/api/user_support.go (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2023 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 package api 18 19 import ( 20 "errors" 21 "fmt" 22 23 "github.com/go-openapi/runtime/middleware" 24 "github.com/minio/console/api/operations" 25 "github.com/minio/console/api/operations/support" 26 "github.com/minio/console/models" 27 "github.com/minio/console/pkg/subnet" 28 "golang.org/x/net/context" 29 ) 30 31 type ConfigurationSetItem struct { 32 Value string 33 Enable bool 34 } 35 36 func registerSupportHandlers(api *operations.ConsoleAPI) { 37 // callhome handlers 38 api.SupportGetCallHomeOptionValueHandler = support.GetCallHomeOptionValueHandlerFunc(func(params support.GetCallHomeOptionValueParams, session *models.Principal) middleware.Responder { 39 callhomeResp, err := getCallHomeOptionResponse(session, params) 40 if err != nil { 41 return support.NewGetCallHomeOptionValueDefault(err.Code).WithPayload(err.APIError) 42 } 43 44 return support.NewGetCallHomeOptionValueOK().WithPayload(callhomeResp) 45 }) 46 47 api.SupportSetCallHomeStatusHandler = support.SetCallHomeStatusHandlerFunc(func(params support.SetCallHomeStatusParams, session *models.Principal) middleware.Responder { 48 err := editCallHomeOptionResponse(session, params) 49 if err != nil { 50 return support.NewSetCallHomeStatusDefault(err.Code).WithPayload(err.APIError) 51 } 52 53 return support.NewSetCallHomeStatusNoContent() 54 }) 55 } 56 57 // getCallHomeOptionResponse returns the selected option value 58 func getCallHomeOptionResponse(session *models.Principal, params support.GetCallHomeOptionValueParams) (*models.CallHomeGetResponse, *CodedAPIError) { 59 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 60 defer cancel() 61 62 mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session) 63 if err != nil { 64 return nil, ErrorWithContext(ctx, err) 65 } 66 67 minioClient := AdminClient{Client: mAdmin} 68 69 response, err := getCallHomeRule(ctx, minioClient) 70 if err != nil { 71 return nil, ErrorWithContext(ctx, err) 72 } 73 74 return response, nil 75 } 76 77 func getCallHomeRule(ctx context.Context, client MinioAdmin) (*models.CallHomeGetResponse, error) { 78 // We verify if callhome SubSys is supported 79 supportedSubSys, err := minioConfigSupportsSubSys(ctx, client, "callhome") 80 if err != nil { 81 return nil, err 82 } 83 84 var returnResponse models.CallHomeGetResponse 85 86 // SubSys is not supported, hence callhome is disabled. 87 if !supportedSubSys { 88 returnResponse.DiagnosticsStatus = false 89 returnResponse.LogsStatus = false 90 91 return &returnResponse, nil 92 } 93 94 diagnosticsProps, err := getConfig(ctx, client, "callhome") 95 if err != nil { 96 return nil, err 97 } 98 99 diagnosticsSt := true 100 101 for _, properties := range diagnosticsProps { 102 for _, property := range properties.KeyValues { 103 if property.Key == "enable" { 104 diagnosticsSt = property.Value == "on" 105 } 106 } 107 } 108 109 loggerSt := true 110 111 loggerProps, err := getConfig(ctx, client, "logger_webhook:subnet") 112 113 // Logger not defined, then it is disabled. 114 if err != nil { 115 loggerSt = false 116 } else { 117 for _, logger := range loggerProps { 118 for _, property := range logger.KeyValues { 119 if property.Key == "enable" { 120 loggerSt = property.Value == "on" 121 } 122 } 123 } 124 } 125 126 returnModel := models.CallHomeGetResponse{DiagnosticsStatus: diagnosticsSt, LogsStatus: loggerSt} 127 128 return &returnModel, nil 129 } 130 131 // editCallHomeOptionResponse returns if there was an error setting the option 132 func editCallHomeOptionResponse(session *models.Principal, params support.SetCallHomeStatusParams) *CodedAPIError { 133 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 134 defer cancel() 135 136 mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session) 137 if err != nil { 138 return ErrorWithContext(ctx, err) 139 } 140 141 minioClient := AdminClient{Client: mAdmin} 142 143 err = setCallHomeConfiguration(ctx, minioClient, *params.Body.DiagState, *params.Body.LogsState) 144 if err != nil { 145 return ErrorWithContext(ctx, err) 146 } 147 148 return nil 149 } 150 151 func configureCallHomeDiagnostics(ctx context.Context, client MinioAdmin, diagState bool) error { 152 // We verify if callhome SubSys is supported 153 supportedSubSys, err := minioConfigSupportsSubSys(ctx, client, "callhome") 154 if err != nil { 155 return err 156 } 157 158 // SubSys is not supported, hence callhome not available 159 if !supportedSubSys { 160 return errors.New("your version of MinIO doesn't support this configuration") 161 } 162 163 enableStr := "off" 164 if diagState { 165 enableStr = "on" 166 } 167 configStr := "callhome enable=" + enableStr 168 _, err = client.setConfigKV(ctx, configStr) 169 if err != nil { 170 return err 171 } 172 173 return nil 174 } 175 176 func configureCallHomeLogs(ctx context.Context, client MinioAdmin, logState bool, apiKey string) error { 177 var configStr string 178 if logState { 179 configStr = fmt.Sprintf("logger_webhook:subnet endpoint=%s auth_token=%s enable=on", 180 subnet.LogWebhookURL(), apiKey) 181 } else { 182 configStr = "logger_webhook:subnet enable=off" 183 } 184 185 // Call set config API 186 _, err := client.setConfigKV(ctx, configStr) 187 if err != nil { 188 return err 189 } 190 191 return nil 192 } 193 194 func setCallHomeConfiguration(ctx context.Context, client MinioAdmin, diagState, logsState bool) error { 195 tokenConfig, err := GetSubnetKeyFromMinIOConfig(ctx, client) 196 if err != nil { 197 return err 198 } 199 200 apiKey := tokenConfig.APIKey 201 202 if len(apiKey) == 0 { 203 return errors.New("please register this cluster in subnet to continue") 204 } 205 206 err = configureCallHomeDiagnostics(ctx, client, diagState) 207 if err != nil { 208 return err 209 } 210 211 err = configureCallHomeLogs(ctx, client, logsState, apiKey) 212 if err != nil { 213 return err 214 } 215 216 return nil 217 } 218 219 func minioConfigSupportsSubSys(ctx context.Context, client MinioAdmin, subSys string) (bool, error) { 220 help, err := client.helpConfigKVGlobal(ctx, false) 221 if err != nil { 222 return false, err 223 } 224 225 for _, h := range help.KeysHelp { 226 if h.Key == subSys { 227 return true, nil 228 } 229 } 230 231 return false, nil 232 }