Connect marketplace API to microservice (#2130)
This commit is contained in:
@@ -44,7 +44,7 @@ builds:
|
||||
- --tags=kqueue,operator
|
||||
|
||||
ldflags:
|
||||
- -s -w -X github.com/minio/console/pkg.ReleaseTag={{.Tag}} -X github.com/minio/console/pkg.CommitID={{.FullCommit}} -X github.com/minio/console/pkg.Version={{.Version}} -X github.com/minio/console/pkg.ShortCommitID={{.ShortCommit}} -X github.com/minio/console/pkg.ReleaseTime={{.Date}}
|
||||
- -s -w -X github.com/minio/console/pkg.ReleaseTag={{.Tag}} -X github.com/minio/console/pkg.CommitID={{.FullCommit}} -X github.com/minio/console/pkg.Version={{.Version}} -X github.com/minio/console/pkg.ShortCommitID={{.ShortCommit}} -X github.com/minio/console/pkg.ReleaseTime={{.Date}} -X github.com/minio/console/pkg.MPSecret={{.Env.MPSECRET}}
|
||||
|
||||
archives:
|
||||
-
|
||||
|
||||
@@ -36,6 +36,9 @@ type MpIntegration struct {
|
||||
|
||||
// email
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
// is in e u
|
||||
IsInEU bool `json:"isInEU,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this mp integration
|
||||
|
||||
@@ -321,7 +321,12 @@ func init() {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/mpIntegration"
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isEmailSet": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
@@ -3358,6 +3363,9 @@ func init() {
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"isInEU": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4995,7 +5003,12 @@ func init() {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/mpIntegration"
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isEmailSet": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
@@ -8863,6 +8876,9 @@ func init() {
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"isInEU": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
// Copyright (c) 2022 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
|
||||
@@ -19,14 +19,21 @@ package operatorapi
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations"
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
"github.com/minio/console/pkg"
|
||||
errors "github.com/minio/console/restapi"
|
||||
"github.com/minio/pkg/env"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
@@ -34,7 +41,11 @@ import (
|
||||
var (
|
||||
mpConfigMapDefault = "mp-config"
|
||||
mpConfigMapKey = "MP_CONFIG_KEY"
|
||||
mpEmail = "email"
|
||||
mpHostEnvVar = "MP_HOST"
|
||||
defaultMPHost = "https://marketplace.apps.min.dev"
|
||||
mpEUHostEnvVar = "MP_EU_HOST"
|
||||
defaultEUMPHost = "https://marketplace-eu.apps.min.dev"
|
||||
isMPEmailSet = "isEmailSet"
|
||||
emailNotSetMsg = "Email was not sent in request"
|
||||
)
|
||||
|
||||
@@ -56,62 +67,112 @@ func registerMarketplaceHandlers(api *operations.OperatorAPI) {
|
||||
})
|
||||
}
|
||||
|
||||
func getMPIntegrationResponse(session *models.Principal, params operator_api.GetMPIntegrationParams) (*models.MpIntegration, *models.Error) {
|
||||
if true { // This block will be removed once service to register emails is deployed
|
||||
return nil, &models.Error{Code: 501}
|
||||
}
|
||||
func getMPIntegrationResponse(session *models.Principal, params operator_api.GetMPIntegrationParams) (*operator_api.GetMPIntegrationOKBody, *models.Error) {
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if err != nil {
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
mpEmail, err := getMPEmail(ctx, &k8sClient{client: clientSet})
|
||||
isMPEmailSet, err := getMPEmail(ctx, &k8sClient{client: clientSet})
|
||||
if err != nil {
|
||||
return nil, errors.ErrorWithContext(ctx, errors.ErrNotFound)
|
||||
}
|
||||
return &models.MpIntegration{
|
||||
Email: mpEmail,
|
||||
return &operator_api.GetMPIntegrationOKBody{
|
||||
IsEmailSet: isMPEmailSet,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getMPEmail(ctx context.Context, clientSet K8sClientI) (string, error) {
|
||||
func getMPEmail(ctx context.Context, clientSet K8sClientI) (bool, error) {
|
||||
cm, err := clientSet.getConfigMap(ctx, "default", getMPConfigMapKey(mpConfigMapKey), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
return false, err
|
||||
}
|
||||
return cm.Data[mpEmail], nil
|
||||
return cm.Data[isMPEmailSet] == "true", nil
|
||||
}
|
||||
|
||||
func postMPIntegrationResponse(session *models.Principal, params operator_api.PostMPIntegrationParams) *models.Error {
|
||||
if true { // This block will be removed once service to register emails is deployed
|
||||
return &models.Error{Code: 501}
|
||||
}
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if err != nil {
|
||||
return errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return setMPIntegration(ctx, params.Body.Email, &k8sClient{client: clientSet})
|
||||
return setMPIntegration(ctx, params.Body.Email, params.Body.IsInEU, &k8sClient{client: clientSet})
|
||||
}
|
||||
|
||||
func setMPIntegration(ctx context.Context, email string, clientSet K8sClientI) *models.Error {
|
||||
func setMPIntegration(ctx context.Context, email string, isInEU bool, clientSet K8sClientI) *models.Error {
|
||||
if email == "" {
|
||||
return errors.ErrorWithContext(ctx, errors.ErrBadRequest, fmt.Errorf(emailNotSetMsg))
|
||||
}
|
||||
if _, err := setMPEmail(ctx, email, clientSet); err != nil {
|
||||
if _, err := setMPEmail(ctx, email, isInEU, clientSet); err != nil {
|
||||
return errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setMPEmail(ctx context.Context, email string, clientSet K8sClientI) (*corev1.ConfigMap, error) {
|
||||
cm := createCM(email)
|
||||
func setMPEmail(ctx context.Context, email string, isInEU bool, clientSet K8sClientI) (*corev1.ConfigMap, error) {
|
||||
if err := postEmailToMP(email, isInEU); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cm := createCM()
|
||||
return clientSet.createConfigMap(ctx, "default", cm, metav1.CreateOptions{})
|
||||
}
|
||||
|
||||
func createCM(email string) *corev1.ConfigMap {
|
||||
func postEmailToMP(email string, isInEU bool) error {
|
||||
mpURL, err := getMPURL(isInEU)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return makePostRequestToMP(mpURL, email)
|
||||
}
|
||||
|
||||
func getMPURL(isInEU bool) (string, error) {
|
||||
mpHost := getMPHost(isInEU)
|
||||
if mpHost == "" {
|
||||
return "", fmt.Errorf("mp host not set")
|
||||
}
|
||||
return fmt.Sprintf("%s/mp-email", mpHost), nil
|
||||
}
|
||||
|
||||
func getMPHost(isInEU bool) string {
|
||||
if isInEU {
|
||||
return env.Get(mpEUHostEnvVar, defaultEUMPHost)
|
||||
}
|
||||
return env.Get(mpHostEnvVar, defaultMPHost)
|
||||
}
|
||||
|
||||
func makePostRequestToMP(url, email string) error {
|
||||
request, err := createMPRequest(url, email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := &http.Client{Timeout: 3 * time.Second}
|
||||
if res, err := client.Do(request); err != nil {
|
||||
return err
|
||||
} else if res.StatusCode >= http.StatusBadRequest {
|
||||
b, _ := io.ReadAll(res.Body)
|
||||
return fmt.Errorf("request to %s failed with status code %d and error %s", url, res.StatusCode, string(b))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createMPRequest(url, email string) (*http.Request, error) {
|
||||
request, err := http.NewRequest("POST", url, strings.NewReader(fmt.Sprintf("{\"email\":\"%s\"}", email)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{})
|
||||
jwtTokenString, err := jwtToken.SignedString([]byte(pkg.MPSecret))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("jwtToken=%s", jwtTokenString))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func createCM() *corev1.ConfigMap {
|
||||
return &corev1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ConfigMap",
|
||||
@@ -121,7 +182,7 @@ func createCM(email string) *corev1.ConfigMap {
|
||||
Name: getMPConfigMapKey(mpConfigMapKey),
|
||||
Namespace: "default",
|
||||
},
|
||||
Data: map[string]string{mpEmail: email},
|
||||
Data: map[string]string{isMPEmailSet: "true"},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ func (suite *MarketplaceTestSuite) getConfigMapMock(ctx context.Context, namespa
|
||||
if testWithError {
|
||||
return nil, errMock
|
||||
}
|
||||
return &corev1.ConfigMap{Data: map[string]string{mpEmail: "mock@mock.com"}}, nil
|
||||
return &corev1.ConfigMap{Data: map[string]string{isMPEmailSet: "true"}}, nil
|
||||
}
|
||||
|
||||
func (suite *MarketplaceTestSuite) createConfigMapMock(ctx context.Context, namespace string, cm *corev1.ConfigMap, opts metav1.CreateOptions) (*corev1.ConfigMap, error) {
|
||||
@@ -115,11 +115,6 @@ func (suite *MarketplaceTestSuite) TestRegisterMarketplaceHandlers() {
|
||||
suite.assert.NotNil(api.OperatorAPIPostMPIntegrationHandler)
|
||||
}
|
||||
|
||||
// TODO
|
||||
// WIP - Complete successful tests to RUD handlers
|
||||
// WIP - Add tests to POST handler
|
||||
// WIP - Check how to mock k8s objects for tests with no error
|
||||
|
||||
func (suite *MarketplaceTestSuite) TestGetMPIntegrationHandlerWithError() {
|
||||
api := &operations.OperatorAPI{}
|
||||
registerMarketplaceHandlers(api)
|
||||
@@ -130,6 +125,19 @@ func (suite *MarketplaceTestSuite) TestGetMPIntegrationHandlerWithError() {
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *MarketplaceTestSuite) TestPostMPIntegrationHandlerWithError() {
|
||||
api := &operations.OperatorAPI{}
|
||||
registerMarketplaceHandlers(api)
|
||||
params := operator_api.NewPostMPIntegrationParams()
|
||||
params.Body = &models.MpIntegration{Email: ""}
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.HTTPRequest.Header = map[string][]string{}
|
||||
params.HTTPRequest.AddCookie(&http.Cookie{Value: "token", Name: "token"})
|
||||
response := api.OperatorAPIPostMPIntegrationHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*operator_api.PostMPIntegrationDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *MarketplaceTestSuite) TestGetMPEmailWithError() {
|
||||
testWithError = true
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -143,15 +151,15 @@ func (suite *MarketplaceTestSuite) TestGetMPEmailNoError() {
|
||||
testWithError = false
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
email, err := getMPEmail(ctx, &suite.kClient)
|
||||
isSet, err := getMPEmail(ctx, &suite.kClient)
|
||||
suite.assert.Nil(err)
|
||||
suite.assert.NotEmpty(email)
|
||||
suite.assert.True(isSet)
|
||||
}
|
||||
|
||||
func (suite *MarketplaceTestSuite) TestSetMPIntegrationNoEmail() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
err := setMPIntegration(ctx, "", &suite.kClient)
|
||||
err := setMPIntegration(ctx, "", false, &suite.kClient)
|
||||
suite.assert.NotNil(err)
|
||||
}
|
||||
|
||||
@@ -159,17 +167,20 @@ func (suite *MarketplaceTestSuite) TestSetMPIntegrationWithError() {
|
||||
testWithError = true
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
err := setMPIntegration(ctx, "mock@mock.com", &suite.kClient)
|
||||
os.Setenv(mpHostEnvVar, " ")
|
||||
err := setMPIntegration(ctx, "mock@mock.com", false, &suite.kClient)
|
||||
suite.assert.NotNil(err)
|
||||
os.Unsetenv(mpHostEnvVar)
|
||||
}
|
||||
|
||||
func (suite *MarketplaceTestSuite) TestSetMPIntegrationNoError() {
|
||||
testWithError = false
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
err := setMPIntegration(ctx, "mock@mock.com", &suite.kClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
// TODO: Add mock server for testing microservice
|
||||
// func (suite *MarketplaceTestSuite) TestSetMPIntegrationNoError() {
|
||||
// testWithError = false
|
||||
// ctx, cancel := context.WithCancel(context.Background())
|
||||
// defer cancel()
|
||||
// err := setMPIntegration(ctx, "mock@mock.com", "token", &suite.kClient)
|
||||
// suite.assert.Nil(err)
|
||||
// }
|
||||
|
||||
func TestMarketplace(t *testing.T) {
|
||||
suite.Run(t, new(MarketplaceTestSuite))
|
||||
|
||||
@@ -23,9 +23,12 @@ package operator_api
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
@@ -86,3 +89,40 @@ func (o *GetMPIntegration) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
|
||||
// GetMPIntegrationOKBody get m p integration o k body
|
||||
//
|
||||
// swagger:model GetMPIntegrationOKBody
|
||||
type GetMPIntegrationOKBody struct {
|
||||
|
||||
// is email set
|
||||
IsEmailSet bool `json:"isEmailSet,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this get m p integration o k body
|
||||
func (o *GetMPIntegrationOKBody) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this get m p integration o k body based on context it is used
|
||||
func (o *GetMPIntegrationOKBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (o *GetMPIntegrationOKBody) MarshalBinary() ([]byte, error) {
|
||||
if o == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(o)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (o *GetMPIntegrationOKBody) UnmarshalBinary(b []byte) error {
|
||||
var res GetMPIntegrationOKBody
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*o = res
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ type GetMPIntegrationOK struct {
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.MpIntegration `json:"body,omitempty"`
|
||||
Payload *GetMPIntegrationOKBody `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewGetMPIntegrationOK creates GetMPIntegrationOK with default headers values
|
||||
@@ -52,13 +52,13 @@ func NewGetMPIntegrationOK() *GetMPIntegrationOK {
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the get m p integration o k response
|
||||
func (o *GetMPIntegrationOK) WithPayload(payload *models.MpIntegration) *GetMPIntegrationOK {
|
||||
func (o *GetMPIntegrationOK) WithPayload(payload *GetMPIntegrationOKBody) *GetMPIntegrationOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the get m p integration o k response
|
||||
func (o *GetMPIntegrationOK) SetPayload(payload *models.MpIntegration) {
|
||||
func (o *GetMPIntegrationOK) SetPayload(payload *GetMPIntegrationOKBody) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
|
||||
@@ -27,4 +27,5 @@ var (
|
||||
CommitID = "(dev)"
|
||||
// ShortCommitID - first 12 characters from CommitID.
|
||||
ShortCommitID = "(dev)"
|
||||
MPSecret = "(dev)"
|
||||
)
|
||||
|
||||
@@ -1240,7 +1240,10 @@ paths:
|
||||
200:
|
||||
description: A successful response.
|
||||
schema:
|
||||
$ref: "#/definitions/mpIntegration"
|
||||
type: object
|
||||
properties:
|
||||
isEmailSet:
|
||||
type: boolean
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
@@ -3394,4 +3397,6 @@ definitions:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
isInEU:
|
||||
type: boolean
|
||||
|
||||
|
||||
Reference in New Issue
Block a user