Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query director for available namespaces #185

35 changes: 35 additions & 0 deletions director/director_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/***************************************************************
*
* Copyright (C) 2023, Pelican Project, Morgridge Institute for Research
*
* 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 director

// List all namespaces from origins registered at the director
func ListNamespacesFromOrigins() []NamespaceAd {

serverAdMutex.RLock()
defer serverAdMutex.RUnlock()

serverAdItems := serverAds.Items()
namespaces := make([]NamespaceAd, 0, len(serverAdItems))
for _, item := range serverAdItems {
if item.Key().Type == OriginType {
namespaces = append(namespaces, item.Value()...)
}
}
return namespaces
}
120 changes: 120 additions & 0 deletions director/director_api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package director

import (
"fmt"
"net/url"
"testing"

"github.com/jellydator/ttlcache/v3"
"github.com/stretchr/testify/assert"
)

func TestListNamespaces(t *testing.T) {
var mockOriginServerAd ServerAd = ServerAd{
Name: "test-origin-server",
AuthURL: url.URL{},
URL: url.URL{},
Type: OriginType,
Latitude: 123.05,
Longitude: 456.78,
}

var mockCacheServerAd ServerAd = ServerAd{
Name: "test-cache-server",
AuthURL: url.URL{},
URL: url.URL{},
Type: CacheType,
Latitude: 45.67,
Longitude: 123.05,
}

const pathPreix string = "/foo/bar/"

mockNamespaceAds := func(size int, serverPrefix string) []NamespaceAd {
namespaceAds := make([]NamespaceAd, size)
for i := 0; i < size; i++ {
namespaceAds[i] = NamespaceAd{
RequireToken: true,
Path: pathPreix + serverPrefix + "/" + fmt.Sprint(i),
Issuer: url.URL{},
MaxScopeDepth: 1,
Strategy: "",
BasePath: "",
VaultServer: "",
}
}
return namespaceAds
}

namespaceAdContainsPath := func(ns []NamespaceAd, path string) bool {
for _, v := range ns {
if v.Path == path {
return true
}
}
return false
}

setup := func() {
serverAds.DeleteAll()
}

teardown := func() {
serverAds.DeleteAll()
}

t.Run("empty-entry", func(t *testing.T) {
setup()
defer teardown()
ns := ListNamespacesFromOrigins()

// Initially there should be 0 namespaces registered
assert.Equal(t, 0, len(ns), "List is not empty for empty namespace cache.")
})
t.Run("one-origin-namespace-entry", func(t *testing.T) {
setup()
defer teardown()
serverAds.Set(mockOriginServerAd, mockNamespaceAds(1, "origin1"), ttlcache.DefaultTTL)
ns := ListNamespacesFromOrigins()

// Only one entry added
assert.Equal(t, 1, len(ns), "List has length not equal to 1 for namespace cache with 1 entry.")
assert.True(t, namespaceAdContainsPath(ns, pathPreix+"origin1/"+fmt.Sprint(0)), "Returned namespace path does not match what's added")
})
t.Run("multiple-origin-namespace-entries-from-same-origin", func(t *testing.T) {
setup()
defer teardown()
serverAds.Set(mockOriginServerAd, mockNamespaceAds(10, "origin1"), ttlcache.DefaultTTL)
ns := ListNamespacesFromOrigins()

assert.Equal(t, 10, len(ns), "List has length not equal to 10 for namespace cache with 10 entries.")
assert.True(t, namespaceAdContainsPath(ns, pathPreix+"origin1/"+fmt.Sprint(5)), "Returned namespace path does not match what's added")
})
t.Run("multiple-origin-namespace-entries-from-different-origins", func(t *testing.T) {
setup()
defer teardown()

serverAds.Set(mockOriginServerAd, mockNamespaceAds(10, "origin1"), ttlcache.DefaultTTL)

// change the name field of serverAD as same name will cause cache to merge
oldServerName := mockOriginServerAd.Name
mockOriginServerAd.Name = "test-origin-server-2"

serverAds.Set(mockOriginServerAd, mockNamespaceAds(10, "origin2"), ttlcache.DefaultTTL)
ns := ListNamespacesFromOrigins()

assert.Equal(t, 20, len(ns), "List has length not equal to 10 for namespace cache with 10 entries.")
assert.True(t, namespaceAdContainsPath(ns, pathPreix+"origin1/"+fmt.Sprint(5)), "Returned namespace path does not match what's added")
assert.True(t, namespaceAdContainsPath(ns, pathPreix+"origin2/"+fmt.Sprint(9)), "Returned namespace path does not match what's added")
mockOriginServerAd.Name = oldServerName
})
t.Run("one-cache-namespace-entry", func(t *testing.T) {
setup()
defer teardown()
serverAds.Set(mockCacheServerAd, mockNamespaceAds(1, "cache1"), ttlcache.DefaultTTL)
ns := ListNamespacesFromOrigins()

// Should not show namespace from cache server
assert.Equal(t, 0, len(ns), "List is not empty for namespace cache with entry from cache server.")
})
}
17 changes: 17 additions & 0 deletions director/redirect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ func (m *MockCache) Register(u string, options ...jwk.RegisterOption) error {
return m.RegisterFn(m)
}

func NamespaceAdContainsPath(ns []NamespaceAd, path string) bool {
for _, v := range ns {
if v.Path == path {
return true
}
}
return false
}

func TestDirectorRegistration(t *testing.T) {
/*
* Tests the RegisterOrigin endpoint. Specifically it creates a keypair and
Expand Down Expand Up @@ -142,6 +151,10 @@ func TestDirectorRegistration(t *testing.T) {
// Check to see that the code exits with status code 200 after given it a good token
assert.Equal(t, 200, w.Result().StatusCode, "Expected status code of 200")

namaspaceADs := ListNamespacesFromOrigins()
assert.True(t, NamespaceAdContainsPath(namaspaceADs, "/foo/bar"), "Coudln't find namespace in the director cache.")
serverAds.DeleteAll()

// Now repeat the above test, but with an invalid token
// Setup httptest recorder and context for the the unit test
wInv := httptest.NewRecorder()
Expand Down Expand Up @@ -190,4 +203,8 @@ func TestDirectorRegistration(t *testing.T) {
assert.Equal(t, 400, wInv.Result().StatusCode, "Expected failing status code of 400")
body, _ := io.ReadAll(wInv.Result().Body)
assert.Equal(t, `{"error":"Authorization token verification failed"}`, string(body), "Failure wasn't because token verification failed")

namaspaceADs = ListNamespacesFromOrigins()
assert.False(t, NamespaceAdContainsPath(namaspaceADs, "/foo/bar"), "Found namespace in the director cache even if the token validation failed.")
serverAds.DeleteAll()
}
Loading