Skip to content

Commit

Permalink
feat: support kubeconfig validate api (#112)
Browse files Browse the repository at this point in the history
## What type of PR is this?
/kind feature

## What this PR does / why we need it:
Support kubeconfig validate API.

Screenshots:

![image](https://github.com/KusionStack/karbour/assets/9360247/c8171dd7-6409-413b-bc6a-eb4056062b88)
  • Loading branch information
elliotxx authored Dec 14, 2023
1 parent a36a7d3 commit 7fb6b5f
Show file tree
Hide file tree
Showing 11 changed files with 497 additions and 65 deletions.
73 changes: 73 additions & 0 deletions api/openapispec/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,71 @@ var doc = `{
}
}
}
},
"/api/v1/cluster/config/validate": {
"post": {
"description": "Validates the provided KubeConfig using cluster manager methods.",
"consumes": [
"text/plain",
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"cluster"
],
"summary": "Validate KubeConfig",
"parameters": [
{
"description": "KubeConfig payload to validate",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/cluster.ValidatePayload"
}
}
],
"responses": {
"200": {
"description": "Verification passed server version",
"schema": {
"type": "string"
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "string"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "string"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "string"
}
},
"429": {
"description": "Too Many Requests",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "string"
}
}
}
}
}
},
"definitions": {
Expand Down Expand Up @@ -249,6 +314,14 @@ var doc = `{
}
}
},
"cluster.ValidatePayload": {
"type": "object",
"properties": {
"kubeConfig": {
"type": "string"
}
}
},
"scanner.Issue": {
"type": "object",
"properties": {
Expand Down
73 changes: 73 additions & 0 deletions api/openapispec/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,71 @@
}
}
}
},
"/api/v1/cluster/config/validate": {
"post": {
"description": "Validates the provided KubeConfig using cluster manager methods.",
"consumes": [
"text/plain",
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"cluster"
],
"summary": "Validate KubeConfig",
"parameters": [
{
"description": "KubeConfig payload to validate",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/cluster.ValidatePayload"
}
}
],
"responses": {
"200": {
"description": "Verification passed server version",
"schema": {
"type": "string"
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "string"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "string"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "string"
}
},
"429": {
"description": "Too Many Requests",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "string"
}
}
}
}
}
},
"definitions": {
Expand Down Expand Up @@ -230,6 +295,14 @@
}
}
},
"cluster.ValidatePayload": {
"type": "object",
"properties": {
"kubeConfig": {
"type": "string"
}
}
},
"scanner.Issue": {
"type": "object",
"properties": {
Expand Down
48 changes: 48 additions & 0 deletions api/openapispec/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ definitions:
fileSize:
type: integer
type: object
cluster.ValidatePayload:
properties:
kubeConfig:
type: string
type: object
scanner.Issue:
properties:
message:
Expand Down Expand Up @@ -183,4 +188,47 @@ paths:
summary: Upload kubeConfig file for cluster
tags:
- cluster
/api/v1/cluster/config/validate:
post:
consumes:
- text/plain
- application/json
description: Validates the provided KubeConfig using cluster manager methods.
parameters:
- description: KubeConfig payload to validate
in: body
name: request
required: true
schema:
$ref: '#/definitions/cluster.ValidatePayload'
produces:
- application/json
responses:
"200":
description: Verification passed server version
schema:
type: string
"400":
description: Bad Request
schema:
type: string
"401":
description: Unauthorized
schema:
type: string
"404":
description: Not Found
schema:
type: string
"429":
description: Too Many Requests
schema:
type: string
"500":
description: Internal Server Error
schema:
type: string
summary: Validate KubeConfig
tags:
- cluster
swagger: "2.0"
46 changes: 46 additions & 0 deletions pkg/core/handler/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,52 @@ func UpdateKubeConfig(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, handler.SuccessResponse(ctx, data))
}

// ValidateKubeConfig returns an HTTP handler function to validate a KubeConfig.
//
// @Summary Validate KubeConfig
// @Description Validates the provided KubeConfig using cluster manager methods.
// @Tags cluster
// @Accept plain
// @Accept json
// @Produce json
// @Param request body ValidatePayload true "KubeConfig payload to validate"
// @Success 200 {string} string "Verification passed server version"
// @Failure 400 {object} string "Bad Request"
// @Failure 401 {object} string "Unauthorized"
// @Failure 429 {object} string "Too Many Requests"
// @Failure 404 {object} string "Not Found"
// @Failure 500 {object} string "Internal Server Error"
// @Router /api/v1/cluster/config/validate [post]
func ValidateKubeConfig(clusterMgr *cluster.ClusterManager) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Extract the context and logger from the request.
ctx := r.Context()
log := ctxutil.GetLogger(ctx)

// Begin the auditing process, logging the start.
log.Info("Starting validate kubeconfig in handler...")

// Decode the request body into the payload.
payload := &ValidatePayload{}
if err := decode(r, payload); err != nil {
render.Render(w, r, handler.FailureResponse(ctx, err))
return
}

// Log successful decoding of the request body.
sanitizeConfig, _ := clusterMgr.SanitizeKubeConfigWithYAML(ctx, payload.KubeConfig)
log.Info("Successfully decoded the request body to payload, and sanitize the kubeconfig in payload",
"sanitizeKubeConfig", sanitizeConfig)

// Validate the specified kube config.
if info, err := clusterMgr.ValidateKubeConfigWithYAML(ctx, payload.KubeConfig); err == nil {
render.JSON(w, r, handler.SuccessResponse(ctx, info))
} else {
render.Render(w, r, handler.FailureResponse(ctx, err))
}
}
}

// isAllowedExtension checks if the provided file name has a permitted extension.
func isAllowedExtension(filename string) bool {
allowedExtensions := []string{"", ".yaml", ".yml", ".json", ".kubeconfig", ".kubeconf"}
Expand Down
4 changes: 4 additions & 0 deletions pkg/core/handler/cluster/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ type UploadData struct {
FileSize int `json:"fileSize"`
Content string `json:"content"`
}

type ValidatePayload struct {
KubeConfig string `json:"kubeConfig"`
}
50 changes: 50 additions & 0 deletions pkg/core/handler/cluster/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright The Karbour Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cluster

import (
"io"
"net/http"

"github.com/go-chi/render"
"github.com/pkg/errors"
)

// decode detects the correct decoder for use on an HTTP request and
// marshals into a given interface.
func decode(r *http.Request, payload *ValidatePayload) error {
// Check if the content type is plain text, read it as such.
contentType := render.GetRequestContentType(r)
switch contentType {
case render.ContentTypePlainText:
// Read the request body.
body, err := io.ReadAll(r.Body)
defer r.Body.Close() // Ensure the body is closed after reading.
if err != nil {
return errors.Wrapf(err, "failed to read the request body")
}
// Set the read content as the manifest payload.
payload.KubeConfig = string(body)
case render.ContentTypeJSON:
// For non-plain text, decode the JSON body into the payload.
if err := render.DecodeJSON(r.Body, payload); err != nil {
return err
}
default:
return errors.New("unsupported media type")
}

return nil
}
Loading

0 comments on commit 7fb6b5f

Please sign in to comment.