From 798be8bdb842e5035a1aad7e0a44b0dcbc671d4f Mon Sep 17 00:00:00 2001 From: Dalton Hubble Date: Mon, 29 Feb 2016 13:28:32 -0800 Subject: [PATCH] bootcfg: Add bootcfg gRPC client and server packages * Add standalone gRPC binary example --- bootcfg/client/client.go | 52 +++++++ bootcfg/server/server.go | 41 +++++ bootcfg/server/serverpb/doc.go | 2 + bootcfg/server/serverpb/server.pb.go | 204 +++++++++++++++++++++++++ bootcfg/server/serverpb/server.proto | 27 ++++ bootcfg/storage/storage.go | 61 ++++++++ bootcfg/storage/storagepb/doc.go | 2 + bootcfg/storage/storagepb/groups.pb.go | 81 ++++++++++ bootcfg/storage/storagepb/groups.proto | 15 ++ build | 3 + cmd/bootcfg-rpc/main.go | 85 +++++++++++ cmd/bootcfg/main.go | 11 +- config/config.go | 30 ++++ scripts/codegen.sh | 19 +++ test | 7 +- 15 files changed, 633 insertions(+), 7 deletions(-) create mode 100644 bootcfg/client/client.go create mode 100644 bootcfg/server/server.go create mode 100644 bootcfg/server/serverpb/doc.go create mode 100644 bootcfg/server/serverpb/server.pb.go create mode 100644 bootcfg/server/serverpb/server.proto create mode 100644 bootcfg/storage/storage.go create mode 100644 bootcfg/storage/storagepb/doc.go create mode 100644 bootcfg/storage/storagepb/groups.pb.go create mode 100644 bootcfg/storage/storagepb/groups.proto create mode 100644 cmd/bootcfg-rpc/main.go create mode 100755 scripts/codegen.sh diff --git a/bootcfg/client/client.go b/bootcfg/client/client.go new file mode 100644 index 00000000..8a8b01f2 --- /dev/null +++ b/bootcfg/client/client.go @@ -0,0 +1,52 @@ +package client + +import ( + "google.golang.org/grpc" + + pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb" +) + +// Config configures a Client. +type Config struct { + // List of endpoint URLs + Endpoints []string +} + +// Client provides a bootcfg client RPC session. +type Client struct { + Groups pb.GroupsClient + conn *grpc.ClientConn +} + +// New creates a new Client from the given Config. +func New(config *Config) (*Client, error) { + return newClient(config) +} + +func newClient(config *Config) (*Client, error) { + conn, err := retryDialer(config) + if err != nil { + return nil, err + } + client := &Client{ + Groups: pb.NewGroupsClient(conn), + conn: conn, + } + return client, nil +} + +// retryDialer attemps to Dial each endpoint until a client connection +// is established. +func retryDialer(config *Config) (*grpc.ClientConn, error) { + opts := []grpc.DialOption{ + grpc.WithInsecure(), + } + var err error + for _, endp := range config.Endpoints { + conn, err := grpc.Dial(endp, opts...) + if err == nil { + return conn, nil + } + } + return nil, err +} diff --git a/bootcfg/server/server.go b/bootcfg/server/server.go new file mode 100644 index 00000000..3704e51a --- /dev/null +++ b/bootcfg/server/server.go @@ -0,0 +1,41 @@ +package server + +import ( + "golang.org/x/net/context" + + "github.com/coreos/coreos-baremetal/bootcfg/storage" + pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb" +) + +// Config configures an RPC Server. +type Config struct { + Store storage.Store +} + +// server implements the grpc GroupsServer interface. +type server struct { + store storage.Store +} + +// NewServer returns a new server. +func NewServer(config *Config) pb.GroupsServer { + return &server{ + store: config.Store, + } +} + +func (s *server) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) { + group, err := s.store.GetGroup(req.Id) + if err != nil { + return nil, err + } + return &pb.GetResponse{Group: group}, nil +} + +func (s *server) List(ctx context.Context, req *pb.ListRequest) (*pb.ListResponse, error) { + groups, err := s.store.ListGroups() + if err != nil { + return nil, err + } + return &pb.ListResponse{Groups: groups}, nil +} \ No newline at end of file diff --git a/bootcfg/server/serverpb/doc.go b/bootcfg/server/serverpb/doc.go new file mode 100644 index 00000000..37c65662 --- /dev/null +++ b/bootcfg/server/serverpb/doc.go @@ -0,0 +1,2 @@ +// Package serverpb provides bootcfg protobuf client and server interfaces. +package serverpb diff --git a/bootcfg/server/serverpb/server.pb.go b/bootcfg/server/serverpb/server.pb.go new file mode 100644 index 00000000..653892d6 --- /dev/null +++ b/bootcfg/server/serverpb/server.pb.go @@ -0,0 +1,204 @@ +// Code generated by protoc-gen-go. +// source: server.proto +// DO NOT EDIT! + +/* +Package serverpb is a generated protocol buffer package. + +It is generated from these files: + server.proto + +It has these top-level messages: + GetRequest + ListRequest + GetResponse + ListResponse +*/ +package serverpb + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import storagepb "github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +const _ = proto.ProtoPackageIsVersion1 + +type GetRequest struct { + Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` +} + +func (m *GetRequest) Reset() { *m = GetRequest{} } +func (m *GetRequest) String() string { return proto.CompactTextString(m) } +func (*GetRequest) ProtoMessage() {} +func (*GetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type ListRequest struct { +} + +func (m *ListRequest) Reset() { *m = ListRequest{} } +func (m *ListRequest) String() string { return proto.CompactTextString(m) } +func (*ListRequest) ProtoMessage() {} +func (*ListRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +type GetResponse struct { + Group *storagepb.Group `protobuf:"bytes,1,opt,name=group" json:"group,omitempty"` +} + +func (m *GetResponse) Reset() { *m = GetResponse{} } +func (m *GetResponse) String() string { return proto.CompactTextString(m) } +func (*GetResponse) ProtoMessage() {} +func (*GetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *GetResponse) GetGroup() *storagepb.Group { + if m != nil { + return m.Group + } + return nil +} + +type ListResponse struct { + Groups []*storagepb.Group `protobuf:"bytes,1,rep,name=groups" json:"groups,omitempty"` +} + +func (m *ListResponse) Reset() { *m = ListResponse{} } +func (m *ListResponse) String() string { return proto.CompactTextString(m) } +func (*ListResponse) ProtoMessage() {} +func (*ListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *ListResponse) GetGroups() []*storagepb.Group { + if m != nil { + return m.Groups + } + return nil +} + +func init() { + proto.RegisterType((*GetRequest)(nil), "serverpb.GetRequest") + proto.RegisterType((*ListRequest)(nil), "serverpb.ListRequest") + proto.RegisterType((*GetResponse)(nil), "serverpb.GetResponse") + proto.RegisterType((*ListResponse)(nil), "serverpb.ListResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// Client API for Groups service + +type GroupsClient interface { + // Get a machine Group by id. + Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) + // List all machine Groups. + List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) +} + +type groupsClient struct { + cc *grpc.ClientConn +} + +func NewGroupsClient(cc *grpc.ClientConn) GroupsClient { + return &groupsClient{cc} +} + +func (c *groupsClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) { + out := new(GetResponse) + err := grpc.Invoke(ctx, "/serverpb.Groups/Get", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupsClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) { + out := new(ListResponse) + err := grpc.Invoke(ctx, "/serverpb.Groups/List", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Groups service + +type GroupsServer interface { + // Get a machine Group by id. + Get(context.Context, *GetRequest) (*GetResponse, error) + // List all machine Groups. + List(context.Context, *ListRequest) (*ListResponse, error) +} + +func RegisterGroupsServer(s *grpc.Server, srv GroupsServer) { + s.RegisterService(&_Groups_serviceDesc, srv) +} + +func _Groups_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { + in := new(GetRequest) + if err := dec(in); err != nil { + return nil, err + } + out, err := srv.(GroupsServer).Get(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + +func _Groups_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { + in := new(ListRequest) + if err := dec(in); err != nil { + return nil, err + } + out, err := srv.(GroupsServer).List(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + +var _Groups_serviceDesc = grpc.ServiceDesc{ + ServiceName: "serverpb.Groups", + HandlerType: (*GroupsServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Get", + Handler: _Groups_Get_Handler, + }, + { + MethodName: "List", + Handler: _Groups_List_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, +} + +var fileDescriptor0 = []byte{ + // 241 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x50, 0xc1, 0x4a, 0x03, 0x31, + 0x10, 0xb5, 0x56, 0x17, 0x9d, 0xad, 0x22, 0x41, 0x45, 0x16, 0x0f, 0x92, 0x83, 0xf4, 0x62, 0x02, + 0x55, 0xd1, 0x3f, 0x28, 0x82, 0xa7, 0xfd, 0x83, 0xcd, 0x3a, 0xc6, 0x05, 0xeb, 0xc4, 0x4c, 0x56, + 0x7f, 0xdf, 0x6d, 0xb2, 0xd1, 0x52, 0x3c, 0x4d, 0xf2, 0xde, 0xbc, 0xf7, 0x66, 0x06, 0x66, 0x8c, + 0xfe, 0x0b, 0xbd, 0x72, 0x9e, 0x02, 0x89, 0x83, 0xf4, 0x73, 0xa6, 0x7a, 0xb2, 0x5d, 0x78, 0xeb, + 0x8d, 0x6a, 0x69, 0xa5, 0x5b, 0xf2, 0x48, 0x3c, 0x96, 0x1b, 0xd3, 0x78, 0x5c, 0x61, 0x68, 0xde, + 0xb5, 0x21, 0x0a, 0xed, 0xab, 0xd5, 0x1c, 0xc8, 0x37, 0x16, 0x73, 0x75, 0x46, 0x5b, 0x4f, 0xbd, + 0xe3, 0x64, 0x2a, 0x2f, 0x01, 0x96, 0x18, 0x6a, 0xfc, 0xec, 0x91, 0x83, 0x38, 0x86, 0xdd, 0xee, + 0xe5, 0x62, 0x72, 0x35, 0x99, 0x1f, 0xd6, 0xc3, 0x4b, 0x1e, 0x41, 0xf9, 0xdc, 0x71, 0xa6, 0xe5, + 0x3d, 0x94, 0xb1, 0x99, 0x1d, 0x7d, 0x30, 0x8a, 0x6b, 0xd8, 0x8f, 0x5e, 0x51, 0x50, 0x2e, 0x4e, + 0xd4, 0x6f, 0x86, 0x5a, 0xae, 0xf1, 0x3a, 0xd1, 0xf2, 0x11, 0x66, 0xc9, 0x65, 0xd4, 0xcd, 0xa1, + 0x48, 0x33, 0x0c, 0xc2, 0xe9, 0xbf, 0xc2, 0x91, 0x5f, 0x7c, 0x43, 0x11, 0x01, 0x16, 0x77, 0x30, + 0x1d, 0xa2, 0xc5, 0xa9, 0xca, 0x47, 0x50, 0x7f, 0x63, 0x57, 0x67, 0x5b, 0x68, 0xca, 0x91, 0x3b, + 0xe2, 0x01, 0xf6, 0xd6, 0xc9, 0x62, 0xa3, 0x61, 0x63, 0x9f, 0xea, 0x7c, 0x1b, 0xce, 0x42, 0x53, + 0xc4, 0xeb, 0xdc, 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x46, 0xb8, 0x4d, 0x82, 0x01, 0x00, + 0x00, +} diff --git a/bootcfg/server/serverpb/server.proto b/bootcfg/server/serverpb/server.proto new file mode 100644 index 00000000..7fee05eb --- /dev/null +++ b/bootcfg/server/serverpb/server.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package serverpb; + +import "github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb/groups.proto"; + +service Groups { + // Get a machine Group by id. + rpc Get(GetRequest) returns (GetResponse) {} + + // List all machine Groups. + rpc List(ListRequest) returns (ListResponse) {} +} + +message GetRequest { + string id = 1; +} + +message ListRequest { +} + +message GetResponse { + storagepb.Group group = 1; +} + +message ListResponse { + repeated storagepb.Group groups = 1; +} diff --git a/bootcfg/storage/storage.go b/bootcfg/storage/storage.go new file mode 100644 index 00000000..d039e1d3 --- /dev/null +++ b/bootcfg/storage/storage.go @@ -0,0 +1,61 @@ +package storage + +import ( + "errors" + + "github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb" +) + +// Errors querying a Store. +var ( + ErrGroupNotFound = errors.New("storage: No Group found") +) + +// A Store provides machine Groups. +type Store interface { + // Get a machine Group by id. + GetGroup(id string) (*storagepb.Group, error) + // List all machine Groups. + ListGroups() ([]*storagepb.Group, error) +} + +// Config initializes a memStore. +type Config struct { + Groups []*storagepb.Group +} + +// memStore implements ths Store interface. +type memStore struct { + groups map[string] *storagepb.Group +} + +// NewMemStore returns a new memory-backed Store. +func NewMemStore(config *Config) Store { + groups := make(map[string]*storagepb.Group) + for _, group := range config.Groups { + groups[group.Id] = group + } + return &memStore{ + groups: groups, + } +} + +// GetGroup returns a machine Group by id. +func (s *memStore) GetGroup(id string) (*storagepb.Group, error) { + val, ok := s.groups[id] + if !ok { + return nil, ErrGroupNotFound + } + return val, nil +} + +// ListGroups lists all machine Groups. +func (s *memStore) ListGroups() ([]*storagepb.Group, error) { + groups := make([]*storagepb.Group, len(s.groups)) + i := 0 + for _, g := range s.groups { + groups[i] = g + i++ + } + return groups, nil +} diff --git a/bootcfg/storage/storagepb/doc.go b/bootcfg/storage/storagepb/doc.go new file mode 100644 index 00000000..cf59064b --- /dev/null +++ b/bootcfg/storage/storagepb/doc.go @@ -0,0 +1,2 @@ +// Package storagepb provides storage protobuf client and server interfaces. +package storagepb \ No newline at end of file diff --git a/bootcfg/storage/storagepb/groups.pb.go b/bootcfg/storage/storagepb/groups.pb.go new file mode 100644 index 00000000..a3adfede --- /dev/null +++ b/bootcfg/storage/storagepb/groups.pb.go @@ -0,0 +1,81 @@ +// Code generated by protoc-gen-go. +// source: groups.proto +// DO NOT EDIT! + +/* +Package storagepb is a generated protocol buffer package. + +It is generated from these files: + groups.proto + +It has these top-level messages: + Group +*/ +package storagepb + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +const _ = proto.ProtoPackageIsVersion1 + +type Group struct { + // machine readable Id + Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + // human readable name + Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` + // profile id + Profile string `protobuf:"bytes,3,opt,name=profile" json:"profile,omitempty"` + // tags required to match the group + Requirements map[string]string `protobuf:"bytes,4,rep,name=requirements" json:"requirements,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // custom metadata + Metadata map[string]string `protobuf:"bytes,5,rep,name=metadata" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` +} + +func (m *Group) Reset() { *m = Group{} } +func (m *Group) String() string { return proto.CompactTextString(m) } +func (*Group) ProtoMessage() {} +func (*Group) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Group) GetRequirements() map[string]string { + if m != nil { + return m.Requirements + } + return nil +} + +func (m *Group) GetMetadata() map[string]string { + if m != nil { + return m.Metadata + } + return nil +} + +func init() { + proto.RegisterType((*Group)(nil), "storagepb.Group") +} + +var fileDescriptor0 = []byte{ + // 217 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x49, 0x2f, 0xca, 0x2f, + 0x2d, 0x28, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x2c, 0x2e, 0xc9, 0x2f, 0x4a, 0x4c, + 0x4f, 0x2d, 0x48, 0x52, 0x3a, 0xce, 0xc4, 0xc5, 0xea, 0x0e, 0x92, 0x13, 0xe2, 0xe3, 0x62, 0xca, + 0x4c, 0x91, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0xb2, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, + 0x73, 0x53, 0x25, 0x98, 0xc0, 0x22, 0x60, 0xb6, 0x90, 0x04, 0x17, 0x3b, 0xd0, 0x84, 0xb4, 0xcc, + 0x9c, 0x54, 0x09, 0x66, 0xb0, 0x30, 0x8c, 0x2b, 0xe4, 0xc6, 0xc5, 0x53, 0x94, 0x5a, 0x58, 0x9a, + 0x59, 0x94, 0x9a, 0x9b, 0x9a, 0x57, 0x52, 0x2c, 0xc1, 0xa2, 0xc0, 0xac, 0xc1, 0x6d, 0xa4, 0xa4, + 0x07, 0xb7, 0x49, 0x0f, 0x6c, 0x8b, 0x5e, 0x10, 0x92, 0x22, 0xd7, 0xbc, 0x92, 0xa2, 0xca, 0x20, + 0x14, 0x7d, 0x42, 0x56, 0x5c, 0x1c, 0xb9, 0xa9, 0x25, 0x89, 0x29, 0x89, 0x25, 0x89, 0x12, 0xac, + 0x60, 0x33, 0xe4, 0x30, 0xcc, 0xf0, 0x85, 0x2a, 0x80, 0xe8, 0x87, 0xab, 0x97, 0xb2, 0xe7, 0x12, + 0xc4, 0x30, 0x5e, 0x48, 0x80, 0x8b, 0x39, 0x3b, 0xb5, 0x12, 0xea, 0x2f, 0x10, 0x53, 0x48, 0x84, + 0x8b, 0xb5, 0x2c, 0x31, 0xa7, 0x14, 0xe6, 0x33, 0x08, 0xc7, 0x8a, 0xc9, 0x82, 0x51, 0xca, 0x9a, + 0x8b, 0x17, 0xc5, 0x6c, 0x52, 0x34, 0x27, 0xb1, 0x81, 0xc3, 0xd6, 0x18, 0x10, 0x00, 0x00, 0xff, + 0xff, 0xcc, 0x89, 0x49, 0x42, 0x6b, 0x01, 0x00, 0x00, +} diff --git a/bootcfg/storage/storagepb/groups.proto b/bootcfg/storage/storagepb/groups.proto new file mode 100644 index 00000000..502750b7 --- /dev/null +++ b/bootcfg/storage/storagepb/groups.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package storagepb; + +message Group { + // machine readable Id + string id = 1; + // human readable name + string name = 2; + // profile id + string profile = 3; + // tags required to match the group + map requirements = 4; + // custom metadata + map metadata = 5; +} \ No newline at end of file diff --git a/build b/build index 6331bc64..f600ea2c 100755 --- a/build +++ b/build @@ -2,3 +2,6 @@ LD_FLAGS="-w -X main.version=$(./git-version)" CGO_ENABLED=0 go build -o bin/bootcfg -ldflags "$LD_FLAGS" -a -tags netgo github.com/coreos/coreos-baremetal/cmd/bootcfg + +# gRPC server as a separate binary +CGO_ENABLED=0 go build -o bin/bootcfg-rpc -ldflags "$LD_FLAGS" -a -tags netgo github.com/coreos/coreos-baremetal/cmd/bootcfg-rpc diff --git a/cmd/bootcfg-rpc/main.go b/cmd/bootcfg-rpc/main.go new file mode 100644 index 00000000..678cf509 --- /dev/null +++ b/cmd/bootcfg-rpc/main.go @@ -0,0 +1,85 @@ +package main + +import ( + "flag" + "fmt" + "net" + "net/url" + "os" + + "github.com/coreos/pkg/capnslog" + "github.com/coreos/pkg/flagutil" + "google.golang.org/grpc" + + bootcfg "github.com/coreos/coreos-baremetal/bootcfg/server" + pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb" + "github.com/coreos/coreos-baremetal/bootcfg/storage" + "github.com/coreos/coreos-baremetal/config" +) + +var ( + // version provided by compile time flag: -ldflags "-X main.version $GIT_SHA" + version = "was not built properly" + log = capnslog.NewPackageLogger("github.com/coreos/coreos-baremetal/cmd/bootcfg-rpc", "main") +) + +func main() { + flags := struct { + address string + configPath string + version bool + help bool + }{} + flag.StringVar(&flags.address, "address", "127.0.0.1:8081", "gRPC listen address") + flag.StringVar(&flags.configPath, "config", "./data/config.yaml", "Path to config file") + // subcommands + flag.BoolVar(&flags.version, "version", false, "print version and exit") + flag.BoolVar(&flags.help, "help", false, "print usage and exit") + + // parse command-line and environment variable arguments + flag.Parse() + if err := flagutil.SetFlagsFromEnv(flag.CommandLine, "BOOTCFG"); err != nil { + log.Fatal(err.Error()) + } + + if flags.version { + fmt.Println(version) + return + } + + if flags.help { + flag.Usage() + return + } + + // validate arguments + if url, err := url.Parse(flags.address); err != nil || url.String() == "" { + log.Fatal("A valid HTTP listen address is required") + } + if finfo, err := os.Stat(flags.configPath); err != nil || finfo.IsDir() { + log.Fatal("A path to a config file is required") + } + + // load bootstrap config + cfg, err := config.LoadConfig(flags.configPath) + if err != nil { + log.Fatal(err) + } + + // storage + store := storage.NewMemStore(&storage.Config{ + Groups: cfg.PBGroups(), + }) + + // gRPC Server + log.Infof("starting bootcfg gRPC server on %s", flags.address) + lis, err := net.Listen("tcp", flags.address) + if err != nil { + log.Fatalf("failed to start listening: %v", err) + } + grpcServer := grpc.NewServer() + pb.RegisterGroupsServer(grpcServer, bootcfg.NewServer(&bootcfg.Config{ + Store: store, + })) + grpcServer.Serve(lis) +} diff --git a/cmd/bootcfg/main.go b/cmd/bootcfg/main.go index edf6a482..5df344cf 100644 --- a/cmd/bootcfg/main.go +++ b/cmd/bootcfg/main.go @@ -8,11 +8,12 @@ import ( "os" "strings" + "github.com/coreos/pkg/capnslog" + "github.com/coreos/pkg/flagutil" + "github.com/coreos/coreos-baremetal/api" "github.com/coreos/coreos-baremetal/config" "github.com/coreos/coreos-baremetal/sign" - "github.com/coreos/pkg/capnslog" - "github.com/coreos/pkg/flagutil" ) var ( @@ -104,7 +105,7 @@ func main() { } store.BootstrapGroups(cfg.Groups) - // API server + // HTTP server config := &api.Config{ Store: store, AssetsPath: flags.assetsPath, @@ -112,9 +113,9 @@ func main() { ArmoredSigner: armoredSigner, } server := api.NewServer(config) - log.Infof("starting config server on %s", flags.address) + log.Infof("starting bootcfg HTTP server on %s", flags.address) err = http.ListenAndServe(flags.address, server.HTTPHandler()) if err != nil { - log.Fatalf("failed to start listening: %s", err) + log.Fatalf("failed to start listening: %v", err) } } diff --git a/config/config.go b/config/config.go index d3b7d877..55490924 100644 --- a/config/config.go +++ b/config/config.go @@ -9,6 +9,8 @@ import ( "strings" "github.com/coreos/coreos-baremetal/api" + "github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb" + "github.com/satori/go.uuid" "gopkg.in/yaml.v2" ) @@ -71,3 +73,31 @@ func (c *Config) validate() error { } return nil } + +// PBGroups returns the parsed storagepb.Group slice. +func (c *Config) PBGroups() []*storagepb.Group { + groups := make([]*storagepb.Group, len(c.Groups)) + i := 0 + for _, g := range c.Groups { + group := &storagepb.Group{ + Id: uuid.NewV4().String(), + Name: g.Name, + Profile: g.Spec, + Metadata: make(map[string]string), + Requirements: g.Matcher, + } + // gRPC message fields must have concrete types. + // Limit YAML metadata nesting to a depth of 1 for now. + for key, unknown := range g.Metadata { + switch val := unknown.(type) { + case string: + group.Metadata[key] = val + default: + // skip subtree + } + } + groups[i] = group + i++ + } + return groups +} diff --git a/scripts/codegen.sh b/scripts/codegen.sh new file mode 100755 index 00000000..8574da78 --- /dev/null +++ b/scripts/codegen.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Generate Go protobuf code. + +set -e + +COREOS_ROOT="$GOPATH/src/" + +# protobuf subpackages end in "pb" +PBUFS=$(go list ./... | grep -v /vendor | grep 'pb$') + +# change into each protobuf directory +for pkg in $PBUFS ; do + abs_path=${GOPATH}/src/${pkg} + echo Generating $abs_path + pushd ${abs_path} > /dev/null + # generate protocol buffers, make other .proto files available to import + protoc --go_out=plugins=grpc:. -I=.:"${COREOS_ROOT}" *.proto + popd > /dev/null +done \ No newline at end of file diff --git a/test b/test index b48abac2..b97113fa 100755 --- a/test +++ b/test @@ -3,7 +3,10 @@ PKGS=$(go list ./... | grep -v /vendor) FORMATTABLE="./api ./config ./cmd ./sign" -go test $PKGS +LINT_EXCLUDE='(/vendor|pb$)' +LINTABLE=$(go list ./... | grep -v -E $LINT_EXCLUDE) + +go test $PKGS -cover go vet $PKGS echo "Checking gofmt..." @@ -14,7 +17,7 @@ if [ -n "${fmtRes}" ]; then fi echo "Checking golint..." -lintRes=$(echo $PKGS | xargs -n 1 golint) +lintRes=$(echo $LINTABLE | xargs -n 1 golint) if [ -n "${lintRes}" ]; then echo -e "golint checking failed:\n${lintRes}" exit 2