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  }