Skip to content

Commit

Permalink
[versioning] add new versioning endpoint (#1052)
Browse files Browse the repository at this point in the history
* [versioning] implement versioning endpoint

* remove args, return existing version, basic test
  • Loading branch information
Shastick authored Jul 9, 2024
1 parent 1d0568f commit b8db6d7
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 7 deletions.
5 changes: 4 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@
url = https://github.com/uastech/standards
[submodule "interfaces/rid/v2"]
path = interfaces/rid/v2
url = https://github.com/uastech/standards
url = https://github.com/uastech/standards
[submodule "interfaces/automated_testing_interfaces"]
path = interfaces/automated_testing_interfaces
url = https://github.com/interuss/automated_testing_interfaces.git
13 changes: 13 additions & 0 deletions build/dev/read_version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

set -eo pipefail

# Retrieve token from dummy OAuth server
ACCESS_TOKEN=$(curl --silent \
"http://localhost:8085/token?grant_type=client_credentials&scope=interuss.versioning.read_system_versions&intended_audience=localhost&issuer=localhost&sub=check_scd" \
| python extract_json_field.py 'access_token')

curl --silent -X GET \
"http://localhost:8082/versions/local.test.identity" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" -H "Content-Type: application/json"

22 changes: 16 additions & 6 deletions cmds/core-service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
apiridv1 "github.com/interuss/dss/pkg/api/ridv1"
apiridv2 "github.com/interuss/dss/pkg/api/ridv2"
apiscdv1 "github.com/interuss/dss/pkg/api/scdv1"
apiversioningv1 "github.com/interuss/dss/pkg/api/versioningv1"
"github.com/interuss/dss/pkg/auth"
aux "github.com/interuss/dss/pkg/aux_"
"github.com/interuss/dss/pkg/build"
Expand All @@ -33,6 +34,7 @@ import (
"github.com/interuss/dss/pkg/scd"
scdc "github.com/interuss/dss/pkg/scd/store/cockroach"
"github.com/interuss/dss/pkg/version"
"github.com/interuss/dss/pkg/versioning"
"github.com/interuss/stacktrace"
"github.com/robfig/cron/v3"
"go.uber.org/zap"
Expand Down Expand Up @@ -219,11 +221,12 @@ func RunHTTPServer(ctx context.Context, ctxCanceler func(), address, locality st
}

var (
err error
ridV1Server *rid_v1.Server
ridV2Server *rid_v2.Server
scdV1Server *scd.Server
auxV1Server = &aux.Server{}
err error
ridV1Server *rid_v1.Server
ridV2Server *rid_v2.Server
scdV1Server *scd.Server
auxV1Server = &aux.Server{}
versioningV1Server = &versioning.Server{}
)

// Initialize remote ID
Expand Down Expand Up @@ -253,9 +256,16 @@ func RunHTTPServer(ctx context.Context, ctxCanceler func(), address, locality st
}

auxV1Router := apiauxv1.MakeAPIRouter(auxV1Server, authorizer)
versioningV1Router := apiversioningv1.MakeAPIRouter(versioningV1Server, authorizer)
ridV1Router := apiridv1.MakeAPIRouter(ridV1Server, authorizer)
ridV2Router := apiridv2.MakeAPIRouter(ridV2Server, authorizer)
multiRouter := api.MultiRouter{Routers: []api.PartialRouter{&auxV1Router, &ridV1Router, &ridV2Router}}
multiRouter := api.MultiRouter{
Routers: []api.PartialRouter{
&auxV1Router,
&versioningV1Router,
&ridV1Router,
&ridV2Router,
}}

// Initialize strategic conflict detection
if *enableSCD {
Expand Down
1 change: 1 addition & 0 deletions interfaces/automated_testing_interfaces
47 changes: 47 additions & 0 deletions pkg/api/versioningv1/interface.gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// This file is auto-generated; do not change as any changes will be overwritten
package versioning

import (
"context"
"github.com/interuss/dss/pkg/api"
)

var (
InterussVersioningReadSystemVersionsScope = api.RequiredScope("interuss.versioning.read_system_versions")
GetVersionSecurity = []api.AuthorizationOption{
{
"Authority": {InterussVersioningReadSystemVersionsScope},
},
}
)

type GetVersionRequest struct {
// The system identity/boundary for which a version should be provided, if known.
SystemIdentity SystemBoundaryIdentifier

// The result of attempting to authorize this request
Auth api.AuthorizationResult
}
type GetVersionResponseSet struct {
// This interface successfully provided the version of the system identity/boundary that was requested.
Response200 *GetVersionResponse

// Bearer access token was not provided in Authorization header, token could not be decoded, or token was invalid.
Response401 *api.EmptyResponseBody

// The access token was decoded successfully but did not include a scope appropriate to this endpoint.
Response403 *api.EmptyResponseBody

// The requested system identity/boundary is not known, or the versioning automated testing interface is not available.
Response404 *api.EmptyResponseBody

// Auto-generated internal server error response
Response500 *api.InternalServerErrorBody
}

type Implementation interface {
// System version
// ---
// Get the requested system version.
GetVersion(ctx context.Context, req *GetVersionRequest) GetVersionResponseSet
}
74 changes: 74 additions & 0 deletions pkg/api/versioningv1/server.gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// This file is auto-generated; do not change as any changes will be overwritten
package versioning

import (
"context"
"github.com/interuss/dss/pkg/api"
"net/http"
"regexp"
)

type APIRouter struct {
Routes []*api.Route
Implementation Implementation
Authorizer api.Authorizer
}

// *versioning.APIRouter (type defined above) implements the api.PartialRouter interface
func (s *APIRouter) Handle(w http.ResponseWriter, r *http.Request) bool {
for _, route := range s.Routes {
if route.Method == r.Method && route.Pattern.MatchString(r.URL.Path) {
route.Handler(route.Pattern, w, r)
return true
}
}
return false
}

func (s *APIRouter) GetVersion(exp *regexp.Regexp, w http.ResponseWriter, r *http.Request) {
var req GetVersionRequest

// Authorize request
req.Auth = s.Authorizer.Authorize(w, r, GetVersionSecurity)

// Parse path parameters
pathMatch := exp.FindStringSubmatch(r.URL.Path)
req.SystemIdentity = SystemBoundaryIdentifier(pathMatch[1])

// Call implementation
ctx, cancel := context.WithCancel(r.Context())
defer cancel()
response := s.Implementation.GetVersion(ctx, &req)

// Write response to client
if response.Response200 != nil {
api.WriteJSON(w, 200, response.Response200)
return
}
if response.Response401 != nil {
api.WriteJSON(w, 401, response.Response401)
return
}
if response.Response403 != nil {
api.WriteJSON(w, 403, response.Response403)
return
}
if response.Response404 != nil {
api.WriteJSON(w, 404, response.Response404)
return
}
if response.Response500 != nil {
api.WriteJSON(w, 500, response.Response500)
return
}
api.WriteJSON(w, 500, api.InternalServerErrorBody{ErrorMessage: "Handler implementation did not set a response"})
}

func MakeAPIRouter(impl Implementation, auth api.Authorizer) APIRouter {
router := APIRouter{Implementation: impl, Authorizer: auth, Routes: make([]*api.Route, 1)}

pattern := regexp.MustCompile("^/versions/(?P<system_identity>[^/]*)$")
router.Routes[0] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetVersion}

return router
}
16 changes: 16 additions & 0 deletions pkg/api/versioningv1/types.gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// This file is auto-generated; do not change as any changes will be overwritten
package versioning

// Identifier of a system boundary, known to both the client and the USS separate from this API, for which this interface can provide a version. While the format is not prescribed by this API, any value must be URL-safe. It is recommended to use an approach similar to reverse-order Internet domain names and Java packages where the global scope is described with increasingly-precise identifiers joined by periods. For instance, the system boundary containing the mandatory Network Identification U-space service might be identified with `gov.eu.uspace.v1.netid` because the authority defining this system boundary is a governmental organization (specifically, the European Union) with requirements imposed on the system under test by the U-space regulation (first version) -- specifically, the Network Identification Service section.
type SystemBoundaryIdentifier string

// Identifier of a particular version of a system (defined by a known system boundary). While the format is not prescribed by this API, a semantic version (https://semver.org/) prefixed with a `v` is recommended.
type VersionIdentifier string

type GetVersionResponse struct {
// The requested system identity/boundary.
SystemIdentity *SystemBoundaryIdentifier `json:"system_identity,omitempty"`

// The version of the system with the specified system identity/boundary.
SystemVersion *VersionIdentifier `json:"system_version,omitempty"`
}
44 changes: 44 additions & 0 deletions pkg/versioning/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package versioning

import (
"context"
"github.com/interuss/dss/pkg/api"
versioning "github.com/interuss/dss/pkg/api/versioningv1"
dsserr "github.com/interuss/dss/pkg/errors"
"github.com/interuss/dss/pkg/version"
"github.com/interuss/stacktrace"
)

type Server struct {
}

func (s *Server) GetVersion(ctx context.Context, req *versioning.GetVersionRequest) versioning.GetVersionResponseSet {
// This should take care of unauthenticated requests as well as
// any request without the proper scope.
if req.Auth.Error != nil {
resp := versioning.GetVersionResponseSet{}
setAuthError(ctx, stacktrace.Propagate(req.Auth.Error, "Auth failed"), &resp.Response401, &resp.Response403, &resp.Response500)
return resp
}

// The DSS has no notion of particular system identities: whatever the request, we will
// always return the current version of the DSS binary.
versionStr := version.Current().String()
return versioning.GetVersionResponseSet{
Response200: &versioning.GetVersionResponse{
SystemIdentity: &req.SystemIdentity,
SystemVersion: (*versioning.VersionIdentifier)(&versionStr),
},
}
}

func setAuthError(ctx context.Context, authErr error, resp401, resp403 **api.EmptyResponseBody, resp500 **api.InternalServerErrorBody) {
switch stacktrace.GetCode(authErr) {
case dsserr.Unauthenticated:
*resp401 = &api.EmptyResponseBody{}
case dsserr.PermissionDenied:
*resp403 = &api.EmptyResponseBody{}
default:
*resp500 = &api.InternalServerErrorBody{ErrorMessage: *dsserr.Handle(ctx, stacktrace.Propagate(authErr, "Could not perform authorization"))}
}
}
31 changes: 31 additions & 0 deletions pkg/versioning/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package versioning

import (
"context"
versioning "github.com/interuss/dss/pkg/api/versioningv1"
"github.com/interuss/dss/pkg/version"
"github.com/stretchr/testify/assert"
"testing"
)

func TestServer_GetVersion(t *testing.T) {
s := &Server{}

got := s.GetVersion(context.Background(),
&versioning.GetVersionRequest{
SystemIdentity: "empty",
}).Response200

assert.Equal(
t,
version.Current().String(),
string(*got.SystemVersion),
)

assert.Equal(
t,
"empty",
string(*got.SystemIdentity),
)

}

0 comments on commit b8db6d7

Please sign in to comment.