...

Source file src/edge-infra.dev/pkg/sds/emergencyaccess/middleware/checks.go

Documentation: edge-infra.dev/pkg/sds/emergencyaccess/middleware

     1  package middleware
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"github.com/gin-gonic/gin"
     9  	"github.com/hashicorp/go-version"
    10  
    11  	"edge-infra.dev/pkg/lib/fog"
    12  	"edge-infra.dev/pkg/sds/emergencyaccess/apierror"
    13  	errorhandler "edge-infra.dev/pkg/sds/emergencyaccess/apierror/handler"
    14  	"edge-infra.dev/pkg/sds/emergencyaccess/eaconst"
    15  )
    16  
    17  // Returns a gin HandlerFunc which can be used as the /health endpoint.
    18  // When invoked runs all of the supplied check functions. If any check function
    19  // fails with an error the endpoint returns a failing status code. If no checks
    20  // are supplied the endpoint returns an ok status code.
    21  // check functions must not be nil.
    22  //
    23  //	// List of checks to be run during a health check
    24  //	checks := []func() error{func() error { return nil }}
    25  //
    26  //	// Register endpoint
    27  //	gin.Any("/health", middleware.HealthCheck(checks...))
    28  func HealthCheck(checks ...func() error) gin.HandlerFunc {
    29  	return func(ctx *gin.Context) {
    30  		for _, check := range checks {
    31  			if err := check(); err != nil {
    32  				log := fog.FromContext(ctx).WithName("health")
    33  				log.Error(err, "failed health check")
    34  				ctx.String(http.StatusServiceUnavailable, "failed health check: %s", err)
    35  				return
    36  			}
    37  		}
    38  		ctx.String(http.StatusOK, "ok")
    39  	}
    40  }
    41  
    42  // Looks at the incoming request header for a RCLI-API-VERSION header and compares it
    43  // with the server's version. It also adds the server's version to the response
    44  // RCLI-API-SERVER-VERSION header. The version is in a major.minor format.
    45  // Only the major value needs to be equal between the incoming and server versions.
    46  // If they do not match, a user error is returned.
    47  //
    48  // This middleware functionality is optional - if no RCLI-API-VERSION header
    49  // is in the request, it moves on to the next handler.
    50  func VerifyAPIVersion(currentAPIVersionStr string) gin.HandlerFunc {
    51  	// Only comparing the major version for equality
    52  	currentAPIVersion, err := version.NewVersion(currentAPIVersionStr)
    53  	if err != nil {
    54  		panic(fmt.Errorf("unable to parse current API version: %w", err))
    55  	}
    56  	currentMajorAPIVersion := currentAPIVersion.Segments()[0]
    57  	constraint := fmt.Sprintf("~>%d.0", currentMajorAPIVersion)
    58  	versionConstraint, err := version.NewConstraint(constraint)
    59  	if err != nil {
    60  		panic(fmt.Errorf("unable to parse versionConstraint: %w", err))
    61  	}
    62  
    63  	return func(c *gin.Context) {
    64  		c.Header(eaconst.APIServerVersionKey, currentAPIVersionStr)
    65  
    66  		v := c.Request.Header.Get(eaconst.APIVersionKey)
    67  		if v == "" {
    68  			// APIVersion header is optional and should not return error if absent
    69  			c.Next()
    70  			return
    71  		}
    72  		incomingVersion, err := version.NewVersion(v)
    73  		if err != nil {
    74  			errorhandler.ErrorHandler(c, apierror.E(apierror.ErrAPIVersion))
    75  			return
    76  		}
    77  
    78  		if !versionConstraint.Check(incomingVersion) {
    79  			errorhandler.ErrorHandler(
    80  				c,
    81  				apierror.E(
    82  					apierror.ErrAPIVersion,
    83  					fmt.Sprintf("The current RemoteCLI version is incompatible with the server version. Please download the appropriate remotecli binary for the environment. Server API Version %s", currentAPIVersion),
    84  					errors.New("remotecli API version is not compatible with server"),
    85  				),
    86  			)
    87  			return
    88  		}
    89  		c.Next()
    90  	}
    91  }
    92  

View as plain text