mirror of
https://github.com/outbackdingo/matchbox.git
synced 2026-01-27 18:19:36 +00:00
bootcfg/api,server: Refactor Group and Profile selection into server
* Add gRPC SelectGroup and SelectProfile service endpoints * HTTP and gRPC servers should both allow labels to be resolved to the matching Group or Profile. Move logic to bootcfg core server. * Add gRPC wrapper selectServer to avoid cluttering core server with response types which are currently only useful for gRPC
This commit is contained in:
@@ -1,80 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/storage"
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoMatchingGroup = errors.New("api: No matching Group")
|
||||
)
|
||||
|
||||
type groupsResource struct {
|
||||
store storage.Store
|
||||
}
|
||||
|
||||
func newGroupsResource(store storage.Store) *groupsResource {
|
||||
return &groupsResource{
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
// matchGroupHandler returns a ContextHandler that matches machine requests to
|
||||
// a Group, adds the Group to the ctx, and calls the next handler. The next
|
||||
// handler should handle the case that no matching Group is found.
|
||||
func (gr *groupsResource) matchGroupHandler(next ContextHandler) ContextHandler {
|
||||
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
attrs := labelsFromRequest(req)
|
||||
// match machine request
|
||||
group, err := gr.findMatch(attrs)
|
||||
if err == nil {
|
||||
// add the Group to the ctx for next handler
|
||||
ctx = withGroup(ctx, group)
|
||||
}
|
||||
next.ServeHTTP(ctx, w, req)
|
||||
}
|
||||
return ContextHandlerFunc(fn)
|
||||
}
|
||||
|
||||
// matchProfileHandler returns a ContextHandler that matches machine requests
|
||||
// to a Profile, adds Profile to the ctx, and calls the next handler. The
|
||||
// next handler should handle the case that no matching Profile is found.
|
||||
func (gr *groupsResource) matchProfileHandler(next ContextHandler) ContextHandler {
|
||||
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
attrs := labelsFromRequest(req)
|
||||
// match machine request
|
||||
group, err := gr.findMatch(attrs)
|
||||
if err == nil {
|
||||
// lookup Profile by id
|
||||
profile, err := gr.store.ProfileGet(group.Profile)
|
||||
if err == nil {
|
||||
// add the Profile to the ctx for next handler
|
||||
ctx = withProfile(ctx, profile)
|
||||
}
|
||||
}
|
||||
next.ServeHTTP(ctx, w, req)
|
||||
}
|
||||
return ContextHandlerFunc(fn)
|
||||
}
|
||||
|
||||
// findMatch returns the first Group whose Matcher is satisfied by the given
|
||||
// labels. Groups are attempted in sorted order, preferring those with
|
||||
// more matcher conditions, alphabetically.
|
||||
func (gr *groupsResource) findMatch(labels map[string]string) (*storagepb.Group, error) {
|
||||
groups, err := gr.store.GroupList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Sort(sort.Reverse(storagepb.ByReqs(groups)))
|
||||
for _, group := range groups {
|
||||
if group.Matches(labels) {
|
||||
return group, nil
|
||||
}
|
||||
}
|
||||
return nil, errNoMatchingGroup
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/storage"
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestNewGroupsResource(t *testing.T) {
|
||||
store := &fixedStore{}
|
||||
gr := newGroupsResource(store)
|
||||
assert.Equal(t, store, gr.store)
|
||||
}
|
||||
|
||||
func TestGroupsResource_MatchProfileHandler(t *testing.T) {
|
||||
store := &fixedStore{
|
||||
Groups: map[string]*storagepb.Group{testGroup.Id: testGroup},
|
||||
Profiles: map[string]*storagepb.Profile{testGroup.Profile: testProfile},
|
||||
}
|
||||
gr := newGroupsResource(store)
|
||||
next := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
profile, err := profileFromContext(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, testProfile, profile)
|
||||
fmt.Fprintf(w, "next handler called")
|
||||
}
|
||||
// assert that:
|
||||
// - request arguments are used to match uuid=a1b2c3d4 -> testGroup
|
||||
// - the group's Profile is found by id and added to the context
|
||||
// - next handler is called
|
||||
h := gr.matchProfileHandler(ContextHandlerFunc(next))
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "?uuid=a1b2c3d4", nil)
|
||||
h.ServeHTTP(context.Background(), w, req)
|
||||
assert.Equal(t, "next handler called", w.Body.String())
|
||||
}
|
||||
|
||||
func TestGroupsResource_MatchGroupHandler(t *testing.T) {
|
||||
store := &fixedStore{
|
||||
Groups: map[string]*storagepb.Group{testGroup.Id: testGroup},
|
||||
}
|
||||
gr := newGroupsResource(store)
|
||||
next := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
group, err := groupFromContext(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, testGroup, group)
|
||||
fmt.Fprintf(w, "next handler called")
|
||||
}
|
||||
// assert that:
|
||||
// - request arguments are used to match uuid=a1b2c3d4 -> testGroup
|
||||
// - next handler is called
|
||||
h := gr.matchGroupHandler(ContextHandlerFunc(next))
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "?uuid=a1b2c3d4", nil)
|
||||
h.ServeHTTP(context.Background(), w, req)
|
||||
assert.Equal(t, "next handler called", w.Body.String())
|
||||
}
|
||||
|
||||
func TestGroupsResource_FindMatch(t *testing.T) {
|
||||
store := &fixedStore{
|
||||
Groups: map[string]*storagepb.Group{testGroup.Id: testGroup},
|
||||
}
|
||||
cases := []struct {
|
||||
store storage.Store
|
||||
labels map[string]string
|
||||
expectedGroup *storagepb.Group
|
||||
expectedErr error
|
||||
}{
|
||||
{store, map[string]string{"uuid": "a1b2c3d4"}, testGroup, nil},
|
||||
{store, nil, nil, errNoMatchingGroup},
|
||||
// no groups in the store
|
||||
{&emptyStore{}, map[string]string{"a": "b"}, nil, errNoMatchingGroup},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
gr := newGroupsResource(c.store)
|
||||
group, err := gr.findMatch(c.labels)
|
||||
assert.Equal(t, c.expectedGroup, group)
|
||||
assert.Equal(t, c.expectedErr, err)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,11 @@ package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/server"
|
||||
pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb"
|
||||
)
|
||||
|
||||
// requireGET requires requests to be an HTTP GET. Otherwise, it responds with
|
||||
@@ -25,3 +30,37 @@ func logRequests(next http.Handler) http.Handler {
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
|
||||
// selectGroup selects the Group whose selectors match the query parameters,
|
||||
// adds the Group to the ctx, and calls the next handler. The next handler
|
||||
// should handle a missing Group.
|
||||
func selectGroup(srv server.Server, next ContextHandler) ContextHandler {
|
||||
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
attrs := labelsFromRequest(req)
|
||||
// match machine request
|
||||
group, err := srv.SelectGroup(ctx, &pb.SelectGroupRequest{Labels: attrs})
|
||||
if err == nil {
|
||||
// add the Group to the ctx for next handler
|
||||
ctx = withGroup(ctx, group)
|
||||
}
|
||||
next.ServeHTTP(ctx, w, req)
|
||||
}
|
||||
return ContextHandlerFunc(fn)
|
||||
}
|
||||
|
||||
// selectProfile selects the Profile for the given query parameters, adds the
|
||||
// Profile to the ctx, and calls the next handler. The next handler should
|
||||
// handle a missing profile.
|
||||
func selectProfile(srv server.Server, next ContextHandler) ContextHandler {
|
||||
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
attrs := labelsFromRequest(req)
|
||||
// match machine request
|
||||
profile, err := srv.SelectProfile(ctx, &pb.SelectProfileRequest{Labels: attrs})
|
||||
if err == nil {
|
||||
// add the Profile to the ctx for the next handler
|
||||
ctx = withProfile(ctx, profile)
|
||||
}
|
||||
next.ServeHTTP(ctx, w, req)
|
||||
}
|
||||
return ContextHandlerFunc(fn)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/server"
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb"
|
||||
)
|
||||
|
||||
func TestRequireGET(t *testing.T) {
|
||||
@@ -32,3 +36,48 @@ func TestRequireGET_WrongMethod(t *testing.T) {
|
||||
assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
|
||||
assert.Equal(t, "only HTTP GET is supported\n", w.Body.String())
|
||||
}
|
||||
|
||||
func TestSelectGroup(t *testing.T) {
|
||||
store := &fixedStore{
|
||||
Groups: map[string]*storagepb.Group{testGroup.Id: testGroup},
|
||||
}
|
||||
srv := server.NewServer(&server.Config{Store: store})
|
||||
next := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
group, err := groupFromContext(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, testGroup, group)
|
||||
fmt.Fprintf(w, "next handler called")
|
||||
}
|
||||
// assert that:
|
||||
// - query params are used to match uuid=a1b2c3d4 to testGroup
|
||||
// - the testGroup is added to the context
|
||||
// - next handler is called
|
||||
h := selectGroup(srv, ContextHandlerFunc(next))
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "?uuid=a1b2c3d4", nil)
|
||||
h.ServeHTTP(context.Background(), w, req)
|
||||
assert.Equal(t, "next handler called", w.Body.String())
|
||||
}
|
||||
|
||||
func TestSelectProfile(t *testing.T) {
|
||||
store := &fixedStore{
|
||||
Groups: map[string]*storagepb.Group{testGroup.Id: testGroup},
|
||||
Profiles: map[string]*storagepb.Profile{testGroup.Profile: testProfile},
|
||||
}
|
||||
srv := server.NewServer(&server.Config{Store: store})
|
||||
next := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
profile, err := profileFromContext(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, testProfile, profile)
|
||||
fmt.Fprintf(w, "next handler called")
|
||||
}
|
||||
// assert that:
|
||||
// - query params are used to match uuid=a1b2c3d4 to testGroup's testProfile
|
||||
// - the testProfile is added to the context
|
||||
// - next handler is called
|
||||
h := selectProfile(srv, ContextHandlerFunc(next))
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "?uuid=a1b2c3d4", nil)
|
||||
h.ServeHTTP(context.Background(), w, req)
|
||||
assert.Equal(t, "next handler called", w.Body.String())
|
||||
}
|
||||
|
||||
@@ -10,6 +10,17 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestNewHandler(t *testing.T) {
|
||||
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
fmt.Fprintf(w, "ContextHandler called")
|
||||
}
|
||||
h := NewHandler(ContextHandlerFunc(fn))
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
h.ServeHTTP(w, req)
|
||||
assert.Equal(t, "ContextHandler called", w.Body.String())
|
||||
}
|
||||
|
||||
func TestLabelsFromRequest(t *testing.T) {
|
||||
emptyMap := map[string]string{}
|
||||
cases := []struct {
|
||||
@@ -33,14 +44,3 @@ func TestLabelsFromRequest(t *testing.T) {
|
||||
assert.Equal(t, c.labels, labelsFromRequest(req))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewHandler(t *testing.T) {
|
||||
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
fmt.Fprintf(w, "ContextHandler called")
|
||||
}
|
||||
h := NewHandler(ContextHandlerFunc(fn))
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
h.ServeHTTP(w, req)
|
||||
assert.Equal(t, "ContextHandler called", w.Body.String())
|
||||
}
|
||||
|
||||
@@ -4,14 +4,17 @@ import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/storage"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/server"
|
||||
pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb"
|
||||
)
|
||||
|
||||
// pixiecoreHandler returns a handler that renders the boot config JSON for
|
||||
// the requester, to implement the Pixiecore API specification.
|
||||
// https://github.com/danderson/pixiecore/blob/master/README.api.md
|
||||
func pixiecoreHandler(gr *groupsResource, store storage.Store) http.Handler {
|
||||
fn := func(w http.ResponseWriter, req *http.Request) {
|
||||
func pixiecoreHandler(srv server.Server) ContextHandler {
|
||||
fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
macAddr, err := parseMAC(filepath.Base(req.URL.Path))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -19,17 +22,17 @@ func pixiecoreHandler(gr *groupsResource, store storage.Store) http.Handler {
|
||||
}
|
||||
// pixiecore only provides MAC addresses
|
||||
attrs := map[string]string{"mac": macAddr.String()}
|
||||
group, err := gr.findMatch(attrs)
|
||||
group, err := srv.SelectGroup(ctx, &pb.SelectGroupRequest{Labels: attrs})
|
||||
if err != nil {
|
||||
http.NotFound(w, req)
|
||||
return
|
||||
}
|
||||
profile, err := store.ProfileGet(group.Profile)
|
||||
resp, err := srv.ProfileGet(ctx, &pb.ProfileGetRequest{Id: group.Profile})
|
||||
if err != nil {
|
||||
http.NotFound(w, req)
|
||||
return
|
||||
}
|
||||
renderJSON(w, profile.Boot)
|
||||
renderJSON(w, resp.Profile.Boot)
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
return ContextHandlerFunc(fn)
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/server"
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb"
|
||||
)
|
||||
|
||||
func TestPixiecoreHandler(t *testing.T) {
|
||||
@@ -14,12 +17,13 @@ func TestPixiecoreHandler(t *testing.T) {
|
||||
Groups: map[string]*storagepb.Group{testGroupWithMAC.Id: testGroupWithMAC},
|
||||
Profiles: map[string]*storagepb.Profile{testGroupWithMAC.Profile: testProfile},
|
||||
}
|
||||
h := pixiecoreHandler(newGroupsResource(store), store)
|
||||
srv := server.NewServer(&server.Config{Store: store})
|
||||
h := pixiecoreHandler(srv)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/"+validMACStr, nil)
|
||||
h.ServeHTTP(w, req)
|
||||
h.ServeHTTP(context.Background(), w, req)
|
||||
// assert that:
|
||||
// - MAC address argument is used for Group matching
|
||||
// - MAC address parameter is used for Group matching
|
||||
// - the Profile's NetBoot config is rendered as Pixiecore JSON
|
||||
expectedJSON := `{"kernel":"/image/kernel","initrd":["/image/initrd_a","/image/initrd_b"],"cmdline":{"a":"b","c":""}}`
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
@@ -28,19 +32,21 @@ func TestPixiecoreHandler(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPixiecoreHandler_InvalidMACAddress(t *testing.T) {
|
||||
h := pixiecoreHandler(&groupsResource{}, &emptyStore{})
|
||||
srv := server.NewServer(&server.Config{Store: &emptyStore{}})
|
||||
h := pixiecoreHandler(srv)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
h.ServeHTTP(w, req)
|
||||
h.ServeHTTP(context.Background(), w, req)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
assert.Equal(t, "invalid MAC address /\n", w.Body.String())
|
||||
}
|
||||
|
||||
func TestPixiecoreHandler_NoMatchingGroup(t *testing.T) {
|
||||
h := pixiecoreHandler(newGroupsResource(&emptyStore{}), &emptyStore{})
|
||||
srv := server.NewServer(&server.Config{Store: &emptyStore{}})
|
||||
h := pixiecoreHandler(srv)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/"+validMACStr, nil)
|
||||
h.ServeHTTP(w, req)
|
||||
h.ServeHTTP(context.Background(), w, req)
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
@@ -48,9 +54,10 @@ func TestPixiecoreHandler_NoMatchingProfile(t *testing.T) {
|
||||
store := &fixedStore{
|
||||
Groups: map[string]*storagepb.Group{testGroup.Id: testGroup},
|
||||
}
|
||||
h := pixiecoreHandler(newGroupsResource(store), &emptyStore{})
|
||||
srv := server.NewServer(&server.Config{Store: store})
|
||||
h := pixiecoreHandler(srv)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/"+validMACStr, nil)
|
||||
h.ServeHTTP(w, req)
|
||||
h.ServeHTTP(context.Background(), w, req)
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
@@ -3,14 +3,11 @@ package api
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/coreos/pkg/capnslog"
|
||||
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/server"
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/sign"
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/storage"
|
||||
"github.com/coreos/pkg/capnslog"
|
||||
)
|
||||
|
||||
const (
|
||||
// APIVersion of the api server and its config types.
|
||||
APIVersion = "v1alpha1"
|
||||
)
|
||||
|
||||
var log = capnslog.NewPackageLogger("github.com/coreos/coreos-baremetal/bootcfg", "api")
|
||||
@@ -26,7 +23,7 @@ type Config struct {
|
||||
ArmoredSigner sign.Signer
|
||||
}
|
||||
|
||||
// Server serves boot and provisioning configs to machines.
|
||||
// Server serves boot and provisioning configs to machines via HTTP.
|
||||
type Server struct {
|
||||
store storage.Store
|
||||
assetsPath string
|
||||
@@ -47,49 +44,49 @@ func NewServer(config *Config) *Server {
|
||||
// HTTPHandler returns a HTTP handler for the server.
|
||||
func (s *Server) HTTPHandler() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
gr := newGroupsResource(s.store)
|
||||
srv := server.NewServer(&server.Config{s.store})
|
||||
|
||||
// Boot via GRUB
|
||||
mux.Handle("/grub", logRequests(NewHandler(gr.matchProfileHandler(grubHandler()))))
|
||||
mux.Handle("/grub", logRequests(NewHandler(selectProfile(srv, grubHandler()))))
|
||||
// Boot via iPXE
|
||||
mux.Handle("/boot.ipxe", logRequests(ipxeInspect()))
|
||||
mux.Handle("/boot.ipxe.0", logRequests(ipxeInspect()))
|
||||
mux.Handle("/ipxe", logRequests(NewHandler(gr.matchProfileHandler(ipxeHandler()))))
|
||||
mux.Handle("/ipxe", logRequests(NewHandler(selectProfile(srv, ipxeHandler()))))
|
||||
// Boot via Pixiecore
|
||||
mux.Handle("/pixiecore/v1/boot/", logRequests(pixiecoreHandler(gr, s.store)))
|
||||
mux.Handle("/pixiecore/v1/boot/", logRequests(NewHandler(pixiecoreHandler(srv))))
|
||||
// Ignition Config
|
||||
mux.Handle("/ignition", logRequests(NewHandler(gr.matchGroupHandler(ignitionHandler(s.store)))))
|
||||
mux.Handle("/ignition", logRequests(NewHandler(selectGroup(srv, ignitionHandler(s.store)))))
|
||||
// Cloud-Config
|
||||
mux.Handle("/cloud", logRequests(NewHandler(gr.matchGroupHandler(cloudHandler(s.store)))))
|
||||
mux.Handle("/cloud", logRequests(NewHandler(selectGroup(srv, cloudHandler(s.store)))))
|
||||
// metadata
|
||||
mux.Handle("/metadata", logRequests(NewHandler(gr.matchGroupHandler(metadataHandler()))))
|
||||
mux.Handle("/metadata", logRequests(NewHandler(selectGroup(srv, metadataHandler()))))
|
||||
|
||||
// Signatures
|
||||
if s.signer != nil {
|
||||
signerChain := func(next http.Handler) http.Handler {
|
||||
return logRequests(sign.SignatureHandler(s.signer, next))
|
||||
}
|
||||
mux.Handle("/grub.sig", signerChain(NewHandler(gr.matchProfileHandler(grubHandler()))))
|
||||
mux.Handle("/grub.sig", signerChain(NewHandler(selectProfile(srv, grubHandler()))))
|
||||
mux.Handle("/boot.ipxe.sig", signerChain(ipxeInspect()))
|
||||
mux.Handle("/boot.ipxe.0.sig", signerChain(ipxeInspect()))
|
||||
mux.Handle("/ipxe.sig", signerChain(NewHandler(gr.matchProfileHandler(ipxeHandler()))))
|
||||
mux.Handle("/pixiecore/v1/boot.sig/", signerChain(pixiecoreHandler(gr, s.store)))
|
||||
mux.Handle("/ignition.sig", signerChain(NewHandler(gr.matchGroupHandler(ignitionHandler(s.store)))))
|
||||
mux.Handle("/cloud.sig", signerChain(NewHandler(gr.matchGroupHandler(cloudHandler(s.store)))))
|
||||
mux.Handle("/metadata.sig", signerChain(NewHandler(gr.matchGroupHandler(metadataHandler()))))
|
||||
mux.Handle("/ipxe.sig", signerChain(NewHandler(selectProfile(srv, ipxeHandler()))))
|
||||
mux.Handle("/pixiecore/v1/boot.sig/", signerChain(NewHandler(pixiecoreHandler(srv))))
|
||||
mux.Handle("/ignition.sig", signerChain(NewHandler(selectGroup(srv, ignitionHandler(s.store)))))
|
||||
mux.Handle("/cloud.sig", signerChain(NewHandler(selectGroup(srv, cloudHandler(s.store)))))
|
||||
mux.Handle("/metadata.sig", signerChain(NewHandler(selectGroup(srv, metadataHandler()))))
|
||||
}
|
||||
if s.armoredSigner != nil {
|
||||
signerChain := func(next http.Handler) http.Handler {
|
||||
return logRequests(sign.SignatureHandler(s.armoredSigner, next))
|
||||
}
|
||||
mux.Handle("/grub.asc", signerChain(NewHandler(gr.matchProfileHandler(grubHandler()))))
|
||||
mux.Handle("/grub.asc", signerChain(NewHandler(selectProfile(srv, grubHandler()))))
|
||||
mux.Handle("/boot.ipxe.asc", signerChain(ipxeInspect()))
|
||||
mux.Handle("/boot.ipxe.0.asc", signerChain(ipxeInspect()))
|
||||
mux.Handle("/ipxe.asc", signerChain(NewHandler(gr.matchProfileHandler(ipxeHandler()))))
|
||||
mux.Handle("/pixiecore/v1/boot.asc/", signerChain(pixiecoreHandler(gr, s.store)))
|
||||
mux.Handle("/ignition.asc", signerChain(NewHandler(gr.matchGroupHandler(ignitionHandler(s.store)))))
|
||||
mux.Handle("/cloud.asc", signerChain(NewHandler(gr.matchGroupHandler(cloudHandler(s.store)))))
|
||||
mux.Handle("/metadata.asc", signerChain(NewHandler(gr.matchGroupHandler(metadataHandler()))))
|
||||
mux.Handle("/ipxe.asc", signerChain(NewHandler(selectProfile(srv, ipxeHandler()))))
|
||||
mux.Handle("/pixiecore/v1/boot.asc/", signerChain(NewHandler(pixiecoreHandler(srv))))
|
||||
mux.Handle("/ignition.asc", signerChain(NewHandler(selectGroup(srv, ignitionHandler(s.store)))))
|
||||
mux.Handle("/cloud.asc", signerChain(NewHandler(selectGroup(srv, cloudHandler(s.store)))))
|
||||
mux.Handle("/metadata.asc", signerChain(NewHandler(selectGroup(srv, metadataHandler()))))
|
||||
}
|
||||
|
||||
// kernel, initrd, and TLS assets
|
||||
|
||||
@@ -12,5 +12,6 @@ func NewServer(s server.Server, opts ...grpc.ServerOption) (*grpc.Server, error)
|
||||
grpcServer := grpc.NewServer(opts...)
|
||||
pb.RegisterGroupsServer(grpcServer, s)
|
||||
pb.RegisterProfilesServer(grpcServer, s)
|
||||
pb.RegisterSelectServer(grpcServer, newSelectServer(s))
|
||||
return grpcServer, nil
|
||||
}
|
||||
|
||||
30
bootcfg/rpc/select.go
Normal file
30
bootcfg/rpc/select.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/server"
|
||||
pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb"
|
||||
)
|
||||
|
||||
// selectServer wraps a bootcfg Server to be suitable for gRPC registration.
|
||||
type selectServer struct {
|
||||
// bootcfg Server
|
||||
srv server.Server
|
||||
}
|
||||
|
||||
func newSelectServer(s server.Server) pb.SelectServer {
|
||||
return &selectServer{
|
||||
srv: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *selectServer) SelectGroup(ctx context.Context, req *pb.SelectGroupRequest) (*pb.SelectGroupResponse, error) {
|
||||
group, err := s.srv.SelectGroup(ctx, req)
|
||||
return &pb.SelectGroupResponse{Group: group}, err
|
||||
}
|
||||
|
||||
func (s *selectServer) SelectProfile(ctx context.Context, req *pb.SelectProfileRequest) (*pb.SelectProfileResponse, error) {
|
||||
profile, err := s.srv.SelectProfile(ctx, req)
|
||||
return &pb.SelectProfileResponse{Profile: profile}, err
|
||||
}
|
||||
@@ -1,16 +1,27 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb"
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/storage"
|
||||
"github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoMatchingGroup = errors.New("bootcfg: No matching Group")
|
||||
errNoProfileFound = errors.New("bootcfg: No Profile found")
|
||||
)
|
||||
|
||||
// Server defines a bootcfg Server.
|
||||
type Server interface {
|
||||
pb.GroupsServer
|
||||
pb.ProfilesServer
|
||||
SelectGroup(ctx context.Context, req *pb.SelectGroupRequest) (*storagepb.Group, error)
|
||||
SelectProfile(ctx context.Context, req *pb.SelectProfileRequest) (*storagepb.Profile, error)
|
||||
}
|
||||
|
||||
// Config configures a server implementation.
|
||||
@@ -72,3 +83,33 @@ func (s *server) ProfileList(ctx context.Context, req *pb.ProfileListRequest) (*
|
||||
}
|
||||
return &pb.ProfileListResponse{Profiles: profiles}, nil
|
||||
}
|
||||
|
||||
// SelectGroup selects the Group whose selector matches the given labels.
|
||||
// Groups are evaluated in sorted order from most selectors to least, using
|
||||
// alphabetical order as a deterministic tie-breaker.
|
||||
func (s *server) SelectGroup(ctx context.Context, req *pb.SelectGroupRequest) (*storagepb.Group, error) {
|
||||
groups, err := s.store.GroupList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Sort(sort.Reverse(storagepb.ByReqs(groups)))
|
||||
for _, group := range groups {
|
||||
if group.Matches(req.Labels) {
|
||||
return group, nil
|
||||
}
|
||||
}
|
||||
return nil, errNoMatchingGroup
|
||||
}
|
||||
|
||||
func (s *server) SelectProfile(ctx context.Context, req *pb.SelectProfileRequest) (*storagepb.Profile, error) {
|
||||
group, err := s.SelectGroup(ctx, &pb.SelectGroupRequest{Labels: req.Labels})
|
||||
if err == nil {
|
||||
// lookup the Profile by id
|
||||
resp, err := s.ProfileGet(ctx, &pb.ProfileGetRequest{Id: group.Profile})
|
||||
if err == nil {
|
||||
return resp.Profile, nil
|
||||
}
|
||||
return nil, errNoProfileFound
|
||||
}
|
||||
return nil, errNoMatchingGroup
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ It is generated from these files:
|
||||
rpc.proto
|
||||
|
||||
It has these top-level messages:
|
||||
SelectGroupRequest
|
||||
SelectGroupResponse
|
||||
SelectProfileRequest
|
||||
SelectProfileResponse
|
||||
GroupGetRequest
|
||||
GroupListRequest
|
||||
GroupGetResponse
|
||||
@@ -41,6 +45,70 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
const _ = proto.ProtoPackageIsVersion1
|
||||
|
||||
type SelectGroupRequest struct {
|
||||
Labels map[string]string `protobuf:"bytes,1,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
}
|
||||
|
||||
func (m *SelectGroupRequest) Reset() { *m = SelectGroupRequest{} }
|
||||
func (m *SelectGroupRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*SelectGroupRequest) ProtoMessage() {}
|
||||
func (*SelectGroupRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *SelectGroupRequest) GetLabels() map[string]string {
|
||||
if m != nil {
|
||||
return m.Labels
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SelectGroupResponse struct {
|
||||
Group *storagepb.Group `protobuf:"bytes,1,opt,name=group" json:"group,omitempty"`
|
||||
}
|
||||
|
||||
func (m *SelectGroupResponse) Reset() { *m = SelectGroupResponse{} }
|
||||
func (m *SelectGroupResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*SelectGroupResponse) ProtoMessage() {}
|
||||
func (*SelectGroupResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *SelectGroupResponse) GetGroup() *storagepb.Group {
|
||||
if m != nil {
|
||||
return m.Group
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SelectProfileRequest struct {
|
||||
Labels map[string]string `protobuf:"bytes,1,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
}
|
||||
|
||||
func (m *SelectProfileRequest) Reset() { *m = SelectProfileRequest{} }
|
||||
func (m *SelectProfileRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*SelectProfileRequest) ProtoMessage() {}
|
||||
func (*SelectProfileRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
|
||||
func (m *SelectProfileRequest) GetLabels() map[string]string {
|
||||
if m != nil {
|
||||
return m.Labels
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SelectProfileResponse struct {
|
||||
Profile *storagepb.Profile `protobuf:"bytes,1,opt,name=profile" json:"profile,omitempty"`
|
||||
}
|
||||
|
||||
func (m *SelectProfileResponse) Reset() { *m = SelectProfileResponse{} }
|
||||
func (m *SelectProfileResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*SelectProfileResponse) ProtoMessage() {}
|
||||
func (*SelectProfileResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||
|
||||
func (m *SelectProfileResponse) GetProfile() *storagepb.Profile {
|
||||
if m != nil {
|
||||
return m.Profile
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GroupGetRequest struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
}
|
||||
@@ -48,7 +116,7 @@ type GroupGetRequest struct {
|
||||
func (m *GroupGetRequest) Reset() { *m = GroupGetRequest{} }
|
||||
func (m *GroupGetRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*GroupGetRequest) ProtoMessage() {}
|
||||
func (*GroupGetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
func (*GroupGetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||
|
||||
type GroupListRequest struct {
|
||||
}
|
||||
@@ -56,7 +124,7 @@ type GroupListRequest struct {
|
||||
func (m *GroupListRequest) Reset() { *m = GroupListRequest{} }
|
||||
func (m *GroupListRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*GroupListRequest) ProtoMessage() {}
|
||||
func (*GroupListRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
func (*GroupListRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
|
||||
|
||||
type GroupGetResponse struct {
|
||||
Group *storagepb.Group `protobuf:"bytes,1,opt,name=group" json:"group,omitempty"`
|
||||
@@ -65,7 +133,7 @@ type GroupGetResponse struct {
|
||||
func (m *GroupGetResponse) Reset() { *m = GroupGetResponse{} }
|
||||
func (m *GroupGetResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*GroupGetResponse) ProtoMessage() {}
|
||||
func (*GroupGetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
func (*GroupGetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
|
||||
|
||||
func (m *GroupGetResponse) GetGroup() *storagepb.Group {
|
||||
if m != nil {
|
||||
@@ -81,7 +149,7 @@ type GroupListResponse struct {
|
||||
func (m *GroupListResponse) Reset() { *m = GroupListResponse{} }
|
||||
func (m *GroupListResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*GroupListResponse) ProtoMessage() {}
|
||||
func (*GroupListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||
func (*GroupListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
|
||||
|
||||
func (m *GroupListResponse) GetGroups() []*storagepb.Group {
|
||||
if m != nil {
|
||||
@@ -97,7 +165,7 @@ type ProfilePutRequest struct {
|
||||
func (m *ProfilePutRequest) Reset() { *m = ProfilePutRequest{} }
|
||||
func (m *ProfilePutRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ProfilePutRequest) ProtoMessage() {}
|
||||
func (*ProfilePutRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||
func (*ProfilePutRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
|
||||
|
||||
func (m *ProfilePutRequest) GetProfile() *storagepb.Profile {
|
||||
if m != nil {
|
||||
@@ -112,7 +180,7 @@ type ProfilePutResponse struct {
|
||||
func (m *ProfilePutResponse) Reset() { *m = ProfilePutResponse{} }
|
||||
func (m *ProfilePutResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ProfilePutResponse) ProtoMessage() {}
|
||||
func (*ProfilePutResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
|
||||
func (*ProfilePutResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
|
||||
|
||||
type ProfileGetRequest struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
@@ -121,7 +189,7 @@ type ProfileGetRequest struct {
|
||||
func (m *ProfileGetRequest) Reset() { *m = ProfileGetRequest{} }
|
||||
func (m *ProfileGetRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ProfileGetRequest) ProtoMessage() {}
|
||||
func (*ProfileGetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
|
||||
func (*ProfileGetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
|
||||
|
||||
type ProfileGetResponse struct {
|
||||
Profile *storagepb.Profile `protobuf:"bytes,1,opt,name=profile" json:"profile,omitempty"`
|
||||
@@ -130,7 +198,7 @@ type ProfileGetResponse struct {
|
||||
func (m *ProfileGetResponse) Reset() { *m = ProfileGetResponse{} }
|
||||
func (m *ProfileGetResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ProfileGetResponse) ProtoMessage() {}
|
||||
func (*ProfileGetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
|
||||
func (*ProfileGetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
|
||||
|
||||
func (m *ProfileGetResponse) GetProfile() *storagepb.Profile {
|
||||
if m != nil {
|
||||
@@ -145,7 +213,7 @@ type ProfileListRequest struct {
|
||||
func (m *ProfileListRequest) Reset() { *m = ProfileListRequest{} }
|
||||
func (m *ProfileListRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ProfileListRequest) ProtoMessage() {}
|
||||
func (*ProfileListRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
|
||||
func (*ProfileListRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
|
||||
|
||||
type ProfileListResponse struct {
|
||||
Profiles []*storagepb.Profile `protobuf:"bytes,1,rep,name=profiles" json:"profiles,omitempty"`
|
||||
@@ -154,7 +222,7 @@ type ProfileListResponse struct {
|
||||
func (m *ProfileListResponse) Reset() { *m = ProfileListResponse{} }
|
||||
func (m *ProfileListResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ProfileListResponse) ProtoMessage() {}
|
||||
func (*ProfileListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
|
||||
func (*ProfileListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
|
||||
|
||||
func (m *ProfileListResponse) GetProfiles() []*storagepb.Profile {
|
||||
if m != nil {
|
||||
@@ -164,6 +232,10 @@ func (m *ProfileListResponse) GetProfiles() []*storagepb.Profile {
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*SelectGroupRequest)(nil), "serverpb.SelectGroupRequest")
|
||||
proto.RegisterType((*SelectGroupResponse)(nil), "serverpb.SelectGroupResponse")
|
||||
proto.RegisterType((*SelectProfileRequest)(nil), "serverpb.SelectProfileRequest")
|
||||
proto.RegisterType((*SelectProfileResponse)(nil), "serverpb.SelectProfileResponse")
|
||||
proto.RegisterType((*GroupGetRequest)(nil), "serverpb.GroupGetRequest")
|
||||
proto.RegisterType((*GroupListRequest)(nil), "serverpb.GroupListRequest")
|
||||
proto.RegisterType((*GroupGetResponse)(nil), "serverpb.GroupGetResponse")
|
||||
@@ -389,29 +461,127 @@ var _Profiles_serviceDesc = grpc.ServiceDesc{
|
||||
Streams: []grpc.StreamDesc{},
|
||||
}
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 368 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x53, 0x4d, 0x4e, 0xeb, 0x30,
|
||||
0x10, 0x7e, 0x79, 0x88, 0x92, 0x4e, 0x25, 0x68, 0x0d, 0x0b, 0x08, 0x45, 0x02, 0x23, 0xa1, 0x2e,
|
||||
0xc0, 0x95, 0xca, 0x0e, 0x89, 0x05, 0x20, 0xa8, 0x40, 0x5d, 0x54, 0xb9, 0x41, 0x13, 0xdc, 0x10,
|
||||
0xa9, 0xc5, 0xc6, 0x76, 0xb8, 0x09, 0xd7, 0xe4, 0x0c, 0xb4, 0x8e, 0xed, 0x9a, 0x26, 0xb0, 0x60,
|
||||
0x65, 0x6b, 0xbe, 0x1f, 0xcf, 0xcc, 0x97, 0x40, 0x53, 0xf0, 0x94, 0x70, 0xc1, 0x14, 0x43, 0xa1,
|
||||
0xa4, 0xe2, 0x9d, 0x0a, 0x9e, 0x44, 0x4f, 0x59, 0xae, 0x5e, 0x8a, 0x84, 0xa4, 0x6c, 0xde, 0x4f,
|
||||
0x99, 0xa0, 0x4c, 0x9a, 0xe3, 0x22, 0x99, 0x08, 0x3a, 0xa7, 0x6a, 0x32, 0xeb, 0x27, 0x8c, 0xa9,
|
||||
0x74, 0x9a, 0xf5, 0xa5, 0x62, 0x62, 0x92, 0x51, 0x7b, 0xf2, 0xc4, 0xde, 0x4a, 0x57, 0x7c, 0x02,
|
||||
0x3b, 0x43, 0xc1, 0x0a, 0x3e, 0xa4, 0x2a, 0xa6, 0x6f, 0x05, 0x95, 0x0a, 0x6d, 0xc3, 0xff, 0xfc,
|
||||
0x79, 0x3f, 0x38, 0x0e, 0x7a, 0xcd, 0x78, 0x71, 0xc3, 0x08, 0xda, 0x9a, 0x32, 0xca, 0xa5, 0xe5,
|
||||
0xe0, 0x2b, 0x53, 0xd3, 0x32, 0xc9, 0xd9, 0xab, 0xa4, 0xe8, 0x0c, 0x36, 0xb3, 0x65, 0x4d, 0x4b,
|
||||
0x5b, 0x83, 0x36, 0x71, 0x6f, 0x12, 0xcd, 0x8d, 0x4b, 0x18, 0x5f, 0x43, 0xc7, 0xf3, 0x33, 0xe2,
|
||||
0x1e, 0x34, 0x34, 0x2a, 0x17, 0xea, 0x8d, 0x5a, 0xb5, 0xc1, 0xf1, 0x0d, 0x74, 0xc6, 0x82, 0x4d,
|
||||
0xf3, 0x19, 0x1d, 0x17, 0xae, 0xe7, 0x73, 0xd8, 0xe2, 0x65, 0xd1, 0xbc, 0x8e, 0x3c, 0xbd, 0xa1,
|
||||
0xc7, 0x96, 0x82, 0xf7, 0x00, 0xf9, 0x16, 0x65, 0x0b, 0xf8, 0xd4, 0x19, 0xff, 0xb2, 0x8c, 0x5b,
|
||||
0x27, 0xf5, 0x47, 0xff, 0xeb, 0xf3, 0xfe, 0x4a, 0xef, 0x61, 0xf7, 0x5b, 0xd5, 0x58, 0x13, 0x08,
|
||||
0x8d, 0xce, 0xae, 0xa6, 0xce, 0xdb, 0x71, 0x06, 0x1f, 0x01, 0x34, 0xf4, 0xc2, 0x24, 0xba, 0x83,
|
||||
0xd0, 0x86, 0x84, 0x0e, 0x88, 0xfd, 0x7c, 0xc8, 0x5a, 0xde, 0x51, 0x54, 0x07, 0x99, 0x9d, 0xfc,
|
||||
0x43, 0x0f, 0xd0, 0x74, 0x69, 0xa1, 0x75, 0xaa, 0xd7, 0x7f, 0x74, 0x58, 0x8b, 0x59, 0x9f, 0xc1,
|
||||
0x67, 0x00, 0xa1, 0xe9, 0x56, 0xa2, 0x47, 0x80, 0x55, 0x00, 0xc8, 0x53, 0x56, 0x92, 0x8d, 0xba,
|
||||
0xf5, 0xa0, 0xeb, 0x6f, 0x65, 0xb5, 0x1c, 0xb3, 0x6a, 0xe5, 0x0d, 0xda, 0xad, 0x07, 0x9d, 0xd5,
|
||||
0x08, 0x5a, 0x5e, 0x02, 0xa8, 0x4a, 0xf7, 0xc7, 0x3d, 0xfa, 0x01, 0xb5, 0x6e, 0x49, 0x43, 0xff,
|
||||
0x60, 0x97, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf4, 0x2a, 0x65, 0x01, 0xc3, 0x03, 0x00, 0x00,
|
||||
// Client API for Select service
|
||||
|
||||
type SelectClient interface {
|
||||
// SelectGroup returns the Group matching the given labels.
|
||||
SelectGroup(ctx context.Context, in *SelectGroupRequest, opts ...grpc.CallOption) (*SelectGroupResponse, error)
|
||||
// SelectProfile returns the Profile matching the given labels.
|
||||
SelectProfile(ctx context.Context, in *SelectProfileRequest, opts ...grpc.CallOption) (*SelectProfileResponse, error)
|
||||
}
|
||||
|
||||
type selectClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewSelectClient(cc *grpc.ClientConn) SelectClient {
|
||||
return &selectClient{cc}
|
||||
}
|
||||
|
||||
func (c *selectClient) SelectGroup(ctx context.Context, in *SelectGroupRequest, opts ...grpc.CallOption) (*SelectGroupResponse, error) {
|
||||
out := new(SelectGroupResponse)
|
||||
err := grpc.Invoke(ctx, "/serverpb.Select/SelectGroup", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *selectClient) SelectProfile(ctx context.Context, in *SelectProfileRequest, opts ...grpc.CallOption) (*SelectProfileResponse, error) {
|
||||
out := new(SelectProfileResponse)
|
||||
err := grpc.Invoke(ctx, "/serverpb.Select/SelectProfile", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Select service
|
||||
|
||||
type SelectServer interface {
|
||||
// SelectGroup returns the Group matching the given labels.
|
||||
SelectGroup(context.Context, *SelectGroupRequest) (*SelectGroupResponse, error)
|
||||
// SelectProfile returns the Profile matching the given labels.
|
||||
SelectProfile(context.Context, *SelectProfileRequest) (*SelectProfileResponse, error)
|
||||
}
|
||||
|
||||
func RegisterSelectServer(s *grpc.Server, srv SelectServer) {
|
||||
s.RegisterService(&_Select_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Select_SelectGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
||||
in := new(SelectGroupRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := srv.(SelectServer).SelectGroup(ctx, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func _Select_SelectProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
||||
in := new(SelectProfileRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := srv.(SelectServer).SelectProfile(ctx, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
var _Select_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "serverpb.Select",
|
||||
HandlerType: (*SelectServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "SelectGroup",
|
||||
Handler: _Select_SelectGroup_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SelectProfile",
|
||||
Handler: _Select_SelectProfile_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
}
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 520 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x54, 0x5d, 0x6e, 0xd3, 0x40,
|
||||
0x10, 0xc6, 0xa9, 0x1a, 0x92, 0x89, 0x80, 0x74, 0x1a, 0xa4, 0x62, 0xca, 0x9f, 0x91, 0x50, 0x84,
|
||||
0xc0, 0x91, 0xc2, 0x0b, 0x54, 0xaa, 0x04, 0x45, 0xa1, 0x02, 0xe5, 0xa1, 0x0a, 0x27, 0xb0, 0xcd,
|
||||
0x34, 0x44, 0xb8, 0x5d, 0xb3, 0xbb, 0xae, 0xd4, 0x63, 0xf0, 0xc0, 0x25, 0x38, 0x1c, 0x67, 0xc0,
|
||||
0x59, 0xef, 0x6e, 0x36, 0xb6, 0x5b, 0x29, 0xa8, 0x4f, 0x5e, 0xcf, 0xf7, 0xcd, 0xb7, 0xf3, 0xcd,
|
||||
0xd8, 0x03, 0x5d, 0x9e, 0x25, 0x61, 0xc6, 0x99, 0x64, 0xd8, 0x11, 0xc4, 0x2f, 0x88, 0x67, 0xb1,
|
||||
0xff, 0x65, 0xbe, 0x90, 0xdf, 0xf3, 0x38, 0x4c, 0xd8, 0xd9, 0x28, 0x61, 0x9c, 0x98, 0xd0, 0x8f,
|
||||
0xd7, 0x71, 0xc4, 0xe9, 0x8c, 0x64, 0x94, 0x8e, 0x62, 0xc6, 0x64, 0x72, 0x3a, 0x1f, 0x09, 0xc9,
|
||||
0x78, 0x34, 0x27, 0xf3, 0xcc, 0x62, 0x73, 0x2a, 0x55, 0x83, 0x5f, 0x1e, 0xe0, 0x57, 0x4a, 0x29,
|
||||
0x91, 0xc7, 0x9c, 0xe5, 0xd9, 0x8c, 0x7e, 0xe6, 0x24, 0x24, 0xbe, 0x87, 0x76, 0x1a, 0xc5, 0x94,
|
||||
0x8a, 0x3d, 0xef, 0xe9, 0xd6, 0xb0, 0x37, 0x1e, 0x86, 0xe6, 0xf6, 0xb0, 0xce, 0x0e, 0xa7, 0x8a,
|
||||
0x3a, 0x39, 0x97, 0xfc, 0x72, 0xa6, 0xf3, 0xfc, 0x77, 0xd0, 0x73, 0xc2, 0xd8, 0x87, 0xad, 0x1f,
|
||||
0x74, 0x59, 0xa8, 0x79, 0xc3, 0xee, 0x6c, 0x79, 0xc4, 0x01, 0x6c, 0x5f, 0x44, 0x69, 0x4e, 0x7b,
|
||||
0x2d, 0x15, 0x2b, 0x5f, 0x0e, 0x5a, 0x6f, 0xbd, 0xe0, 0x10, 0x76, 0xd7, 0x2e, 0x11, 0x19, 0x3b,
|
||||
0x17, 0x84, 0x2f, 0x60, 0x7b, 0xbe, 0x0c, 0x28, 0x91, 0xde, 0xb8, 0x1f, 0x5a, 0x4f, 0x61, 0x49,
|
||||
0x2c, 0xe1, 0xe0, 0xb7, 0x07, 0x83, 0x32, 0xff, 0x84, 0xb3, 0xd3, 0x45, 0x4a, 0xc6, 0xd4, 0x51,
|
||||
0xc5, 0xd4, 0xcb, 0xaa, 0xa9, 0x75, 0xfe, 0x4d, 0xdb, 0x9a, 0xc0, 0xfd, 0xca, 0x35, 0xda, 0xd8,
|
||||
0x2b, 0xb8, 0x9d, 0x95, 0x21, 0x6d, 0x0d, 0x1d, 0x6b, 0x86, 0x6c, 0x28, 0xc1, 0x33, 0xb8, 0xa7,
|
||||
0xec, 0x1e, 0x93, 0x34, 0xc6, 0xee, 0x42, 0x6b, 0xf1, 0x4d, 0x17, 0x51, 0x9c, 0x02, 0x84, 0xbe,
|
||||
0xa2, 0x4c, 0x17, 0xc2, 0x70, 0x82, 0x03, 0x1d, 0x53, 0x69, 0x1b, 0x76, 0xf4, 0x10, 0x76, 0x1c,
|
||||
0x3d, 0x9d, 0x3c, 0x84, 0xb6, 0x42, 0x4d, 0x37, 0xeb, 0xd9, 0x1a, 0x0f, 0x3e, 0xc0, 0x8e, 0x76,
|
||||
0x71, 0x92, 0xdb, 0x9a, 0x37, 0x33, 0x3d, 0x00, 0x74, 0x25, 0xca, 0x12, 0x82, 0xe7, 0x56, 0xf8,
|
||||
0x9a, 0x66, 0x1c, 0xd9, 0x54, 0xd7, 0xfa, 0xff, 0x5e, 0xef, 0xb6, 0x74, 0x02, 0xbb, 0x6b, 0x51,
|
||||
0x2d, 0x1d, 0x42, 0x47, 0xe7, 0x99, 0xd6, 0x34, 0x69, 0x5b, 0xce, 0xb8, 0xf8, 0x5e, 0xdb, 0xaa,
|
||||
0x61, 0x02, 0x3f, 0x42, 0xc7, 0x0c, 0x09, 0x1f, 0xac, 0xbe, 0xce, 0xca, 0xbc, 0x7d, 0xbf, 0x09,
|
||||
0xd2, 0x3d, 0xb9, 0x85, 0x9f, 0xa0, 0x6b, 0xa7, 0x85, 0x55, 0xaa, 0x53, 0xbf, 0xff, 0xb0, 0x11,
|
||||
0x33, 0x3a, 0xe3, 0xbf, 0x1e, 0x74, 0x74, 0xb5, 0x02, 0x3f, 0x03, 0xac, 0x06, 0x80, 0x4e, 0x66,
|
||||
0x6d, 0xb2, 0xfe, 0x7e, 0x33, 0x68, 0xeb, 0x5b, 0x49, 0x2d, 0x6d, 0xd6, 0xa5, 0x1c, 0xa3, 0xfb,
|
||||
0xcd, 0xa0, 0x95, 0x9a, 0x42, 0xcf, 0x99, 0x00, 0xd6, 0xe9, 0xae, 0xdd, 0x47, 0x57, 0xa0, 0xd6,
|
||||
0xf0, 0x9f, 0x62, 0x10, 0xe5, 0x1f, 0xba, 0x14, 0x76, 0x56, 0x90, 0x2b, 0x5c, 0x5f, 0x7f, 0xae,
|
||||
0x70, 0xc3, 0xde, 0x2a, 0xca, 0x9c, 0xc1, 0x9d, 0xb5, 0x3f, 0x1f, 0x1f, 0x5f, 0xbf, 0x79, 0xfc,
|
||||
0x27, 0x57, 0xe2, 0x46, 0x33, 0x6e, 0xab, 0xfd, 0xfd, 0xe6, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0x30, 0x49, 0x0e, 0x1d, 0x22, 0x06, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -19,6 +19,29 @@ service Profiles {
|
||||
rpc ProfileList(ProfileListRequest) returns (ProfileListResponse) {};
|
||||
}
|
||||
|
||||
service Select {
|
||||
// SelectGroup returns the Group matching the given labels.
|
||||
rpc SelectGroup(SelectGroupRequest) returns (SelectGroupResponse) {};
|
||||
// SelectProfile returns the Profile matching the given labels.
|
||||
rpc SelectProfile(SelectProfileRequest) returns (SelectProfileResponse) {};
|
||||
}
|
||||
|
||||
message SelectGroupRequest {
|
||||
map<string, string> labels = 1;
|
||||
}
|
||||
|
||||
message SelectGroupResponse {
|
||||
storagepb.Group group = 1;
|
||||
}
|
||||
|
||||
message SelectProfileRequest {
|
||||
map<string, string> labels = 1;
|
||||
}
|
||||
|
||||
message SelectProfileResponse {
|
||||
storagepb.Profile profile = 1;
|
||||
}
|
||||
|
||||
message GroupGetRequest {
|
||||
string id = 1;
|
||||
}
|
||||
@@ -52,7 +75,3 @@ message ProfileListRequest {}
|
||||
message ProfileListResponse {
|
||||
repeated storagepb.Profile profiles = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ func (g *Group) requirementString() string {
|
||||
return strings.Join(reqs, ",")
|
||||
}
|
||||
|
||||
// byReqs defines a collection of Group structs which have a deterministic
|
||||
// ByReqs defines a collection of Group structs which have a deterministic
|
||||
// sorted order by increasing number of Requirements, then by sorted key/value
|
||||
// strings. For example, a Group with Requirements {a:b, c:d} should be ordered
|
||||
// after one with {a:b} and before one with {a:d, c:d}.
|
||||
|
||||
Reference in New Issue
Block a user