From 8a846d27650d6652a7a414ce12cad47f9bbdf2f8 Mon Sep 17 00:00:00 2001 From: Dalton Hubble Date: Tue, 19 Apr 2016 14:59:13 -0700 Subject: [PATCH] bootcfg/rpc: Add IgnitionPut endpoint to gRPC API * Add `bootcmd ignition create -f file` CLI command --- bootcfg/cli/group_create.go | 5 +- bootcfg/cli/ignition.go | 16 ++++ bootcfg/cli/ignition_create.go | 48 +++++++++++ bootcfg/cli/profile_create.go | 5 +- bootcfg/cli/root.go | 3 +- bootcfg/client/client.go | 2 + bootcfg/rpc/grpc.go | 1 + bootcfg/rpc/ignition.go | 25 ++++++ bootcfg/rpc/rpcpb/rpc.pb.go | 100 +++++++++++++++++----- bootcfg/rpc/rpcpb/rpc.proto | 5 ++ bootcfg/rpc/select.go | 1 - bootcfg/server/server.go | 18 +++- bootcfg/server/server_test.go | 26 ++++++ bootcfg/server/serverpb/messages.pb.go | 77 +++++++++++------ bootcfg/server/serverpb/messages.proto | 7 ++ bootcfg/storage/filestore.go | 7 +- bootcfg/storage/filestore_test.go | 16 ++++ bootcfg/storage/storage.go | 5 +- bootcfg/storage/testfakes/broken_store.go | 9 +- bootcfg/storage/testfakes/empty_store.go | 17 ++-- bootcfg/storage/testfakes/fixed_store.go | 26 +++--- bootcfg/storage/testfakes/fixtures.go | 11 +++ 22 files changed, 355 insertions(+), 75 deletions(-) create mode 100644 bootcfg/cli/ignition.go create mode 100644 bootcfg/cli/ignition_create.go create mode 100644 bootcfg/rpc/ignition.go diff --git a/bootcfg/cli/group_create.go b/bootcfg/cli/group_create.go index d914651c..613866b7 100644 --- a/bootcfg/cli/group_create.go +++ b/bootcfg/cli/group_create.go @@ -3,10 +3,11 @@ package cli import ( "io/ioutil" - pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb" - "github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb" "github.com/spf13/cobra" "golang.org/x/net/context" + + pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb" + "github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb" ) // groupPutCmd creates and updates Groups. diff --git a/bootcfg/cli/ignition.go b/bootcfg/cli/ignition.go new file mode 100644 index 00000000..113f7fc6 --- /dev/null +++ b/bootcfg/cli/ignition.go @@ -0,0 +1,16 @@ +package cli + +import ( + "github.com/spf13/cobra" +) + +// ignitionCmd represents the ignition command +var ignitionCmd = &cobra.Command{ + Use: "ignition", + Short: "Manage Ignition templates", + Long: `Manage Ignition templates`, +} + +func init() { + RootCmd.AddCommand(ignitionCmd) +} diff --git a/bootcfg/cli/ignition_create.go b/bootcfg/cli/ignition_create.go new file mode 100644 index 00000000..04eb84cd --- /dev/null +++ b/bootcfg/cli/ignition_create.go @@ -0,0 +1,48 @@ +package cli + +import ( + "io/ioutil" + "path/filepath" + + "github.com/spf13/cobra" + "golang.org/x/net/context" + + pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb" +) + +// ignitionPutCmd creates and updates Ignition templates. +var ( + ignitionPutCmd = &cobra.Command{ + Use: "create --file FILENAME", + Short: "Create an Ignition template", + Long: `Create an Ignition template`, + Run: runIgnitionPutCmd, + } +) + +func init() { + ignitionCmd.AddCommand(ignitionPutCmd) + ignitionPutCmd.Flags().StringVarP(&flagFilename, "filename", "f", "", "filename to use to create an Ignition template") + ignitionPutCmd.MarkFlagRequired("filename") +} + +func runIgnitionPutCmd(cmd *cobra.Command, args []string) { + if len(flagFilename) == 0 { + cmd.Help() + return + } + if err := validateArgs(cmd, args); err != nil { + return + } + + client := mustClientFromCmd(cmd) + config, err := ioutil.ReadFile(flagFilename) + if err != nil { + exitWithError(ExitError, err) + } + req := &pb.IgnitionPutRequest{Name: filepath.Base(flagFilename), Config: config} + _, err = client.Ignition.IgnitionPut(context.TODO(), req) + if err != nil { + exitWithError(ExitError, err) + } +} diff --git a/bootcfg/cli/profile_create.go b/bootcfg/cli/profile_create.go index b1b9709f..0eda6c5c 100644 --- a/bootcfg/cli/profile_create.go +++ b/bootcfg/cli/profile_create.go @@ -3,10 +3,11 @@ package cli import ( "io/ioutil" - pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb" - "github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb" "github.com/spf13/cobra" "golang.org/x/net/context" + + pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb" + "github.com/coreos/coreos-baremetal/bootcfg/storage/storagepb" ) // profilePutCmd creates and updates Profiles. diff --git a/bootcfg/cli/root.go b/bootcfg/cli/root.go index f6158579..da4bd821 100644 --- a/bootcfg/cli/root.go +++ b/bootcfg/cli/root.go @@ -4,8 +4,9 @@ import ( "fmt" "os" - "github.com/coreos/coreos-baremetal/bootcfg/client" "github.com/spf13/cobra" + + "github.com/coreos/coreos-baremetal/bootcfg/client" ) var ( diff --git a/bootcfg/client/client.go b/bootcfg/client/client.go index 40560cdd..1fb27f52 100644 --- a/bootcfg/client/client.go +++ b/bootcfg/client/client.go @@ -16,6 +16,7 @@ type Config struct { type Client struct { Groups rpcpb.GroupsClient Profiles rpcpb.ProfilesClient + Ignition rpcpb.IgnitionClient conn *grpc.ClientConn } @@ -33,6 +34,7 @@ func newClient(config *Config) (*Client, error) { conn: conn, Groups: rpcpb.NewGroupsClient(conn), Profiles: rpcpb.NewProfilesClient(conn), + Ignition: rpcpb.NewIgnitionClient(conn), } return client, nil } diff --git a/bootcfg/rpc/grpc.go b/bootcfg/rpc/grpc.go index 8da38039..7f88bfdc 100644 --- a/bootcfg/rpc/grpc.go +++ b/bootcfg/rpc/grpc.go @@ -13,5 +13,6 @@ func NewServer(s server.Server, opts ...grpc.ServerOption) (*grpc.Server, error) rpcpb.RegisterGroupsServer(grpcServer, newGroupServer(s)) rpcpb.RegisterProfilesServer(grpcServer, newProfileServer(s)) rpcpb.RegisterSelectServer(grpcServer, newSelectServer(s)) + rpcpb.RegisterIgnitionServer(grpcServer, newIgnitionServer(s)) return grpcServer, nil } diff --git a/bootcfg/rpc/ignition.go b/bootcfg/rpc/ignition.go new file mode 100644 index 00000000..ca351f87 --- /dev/null +++ b/bootcfg/rpc/ignition.go @@ -0,0 +1,25 @@ +package rpc + +import ( + "golang.org/x/net/context" + + "github.com/coreos/coreos-baremetal/bootcfg/rpc/rpcpb" + "github.com/coreos/coreos-baremetal/bootcfg/server" + pb "github.com/coreos/coreos-baremetal/bootcfg/server/serverpb" +) + +// ignitionServer takes a bootcfg Server and implements a gRPC IgnitionServer. +type ignitionServer struct { + srv server.Server +} + +func newIgnitionServer(s server.Server) rpcpb.IgnitionServer { + return &ignitionServer{ + srv: s, + } +} + +func (s *ignitionServer) IgnitionPut(ctx context.Context, req *pb.IgnitionPutRequest) (*pb.IgnitionPutResponse, error) { + _, err := s.srv.IgnitionPut(ctx, req) + return &pb.IgnitionPutResponse{}, grpcError(err) +} diff --git a/bootcfg/rpc/rpcpb/rpc.pb.go b/bootcfg/rpc/rpcpb/rpc.pb.go index 8cbe4c71..1f801309 100644 --- a/bootcfg/rpc/rpcpb/rpc.pb.go +++ b/bootcfg/rpc/rpcpb/rpc.pb.go @@ -273,6 +273,65 @@ var _Profiles_serviceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, } +// Client API for Ignition service + +type IgnitionClient interface { + // Create or update an Ignition template. + IgnitionPut(ctx context.Context, in *messagepb.IgnitionPutRequest, opts ...grpc.CallOption) (*messagepb.IgnitionPutResponse, error) +} + +type ignitionClient struct { + cc *grpc.ClientConn +} + +func NewIgnitionClient(cc *grpc.ClientConn) IgnitionClient { + return &ignitionClient{cc} +} + +func (c *ignitionClient) IgnitionPut(ctx context.Context, in *messagepb.IgnitionPutRequest, opts ...grpc.CallOption) (*messagepb.IgnitionPutResponse, error) { + out := new(messagepb.IgnitionPutResponse) + err := grpc.Invoke(ctx, "/rpcpb.Ignition/IgnitionPut", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Ignition service + +type IgnitionServer interface { + // Create or update an Ignition template. + IgnitionPut(context.Context, *messagepb.IgnitionPutRequest) (*messagepb.IgnitionPutResponse, error) +} + +func RegisterIgnitionServer(s *grpc.Server, srv IgnitionServer) { + s.RegisterService(&_Ignition_serviceDesc, srv) +} + +func _Ignition_IgnitionPut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { + in := new(messagepb.IgnitionPutRequest) + if err := dec(in); err != nil { + return nil, err + } + out, err := srv.(IgnitionServer).IgnitionPut(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + +var _Ignition_serviceDesc = grpc.ServiceDesc{ + ServiceName: "rpcpb.Ignition", + HandlerType: (*IgnitionServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "IgnitionPut", + Handler: _Ignition_IgnitionPut_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, +} + // Client API for Select service type SelectClient interface { @@ -362,24 +421,25 @@ var _Select_serviceDesc = grpc.ServiceDesc{ } var fileDescriptor0 = []byte{ - // 290 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x92, 0x41, 0x4e, 0xf3, 0x30, - 0x10, 0x85, 0xff, 0x2e, 0xfe, 0xaa, 0x35, 0x62, 0xe3, 0x65, 0xa0, 0x80, 0xd8, 0x93, 0x48, 0xe5, - 0x0c, 0xa8, 0x20, 0x10, 0xaa, 0x80, 0x0b, 0xc4, 0xd6, 0x34, 0x44, 0x4a, 0x18, 0xe3, 0x71, 0x38, - 0x16, 0x67, 0xe2, 0x10, 0x1c, 0x80, 0xc4, 0xb1, 0xdd, 0x54, 0xb5, 0x57, 0x1e, 0xbd, 0xf7, 0xfc, - 0x65, 0x5e, 0x12, 0xb6, 0xd4, 0x4a, 0xe6, 0x4a, 0xa3, 0x41, 0xfe, 0xbf, 0x1f, 0x95, 0xc8, 0x1e, - 0xaa, 0xda, 0xbc, 0x77, 0x22, 0x97, 0xd8, 0x16, 0x12, 0x35, 0x20, 0xb9, 0xe3, 0x46, 0x94, 0x1a, - 0x5a, 0x30, 0x65, 0x53, 0x08, 0x44, 0x23, 0x77, 0x55, 0x41, 0xa0, 0xbf, 0x40, 0xbb, 0x43, 0x89, - 0xa2, 0x05, 0xa2, 0xb2, 0x02, 0x1a, 0x89, 0xeb, 0x9f, 0x19, 0x9b, 0x6f, 0x34, 0x76, 0x8a, 0xf8, - 0x1d, 0x5b, 0xd8, 0x69, 0xdb, 0x19, 0x9e, 0xe5, 0x2e, 0xa7, 0x44, 0xee, 0xc5, 0x17, 0xf8, 0xec, - 0x80, 0x4c, 0x76, 0x16, 0xf5, 0x48, 0xe1, 0x07, 0xc1, 0xf5, 0xbf, 0x80, 0xd9, 0x40, 0x04, 0xd3, - 0x8b, 0x49, 0x8c, 0xf5, 0x02, 0xe6, 0x9e, 0x2d, 0xad, 0xfa, 0x54, 0x93, 0xe1, 0x47, 0xd9, 0x41, - 0xf5, 0xa0, 0xf3, 0xb8, 0xe9, 0x49, 0xeb, 0xdf, 0x19, 0x5b, 0x6c, 0x35, 0xee, 0xea, 0x06, 0x88, - 0x3f, 0x32, 0xe6, 0xe6, 0xa1, 0xe6, 0xf4, 0xea, 0x5e, 0xf6, 0xe0, 0x55, 0xc2, 0x0d, 0x3b, 0xee, - 0x61, 0x43, 0xd9, 0x08, 0x6c, 0x52, 0x77, 0x95, 0x70, 0x03, 0xec, 0x99, 0x9d, 0x38, 0xdd, 0x56, - 0x8e, 0xe4, 0xa7, 0xa5, 0x2f, 0x52, 0x76, 0xa8, 0xfd, 0xdd, 0x7f, 0xd9, 0x57, 0x68, 0x40, 0x9a, - 0x01, 0x3d, 0x4e, 0xf6, 0xf5, 0x1c, 0xa0, 0x27, 0x7a, 0x0c, 0x7d, 0x60, 0x87, 0x55, 0xdf, 0xd8, - 0xe9, 0x68, 0xb8, 0x27, 0xf3, 0xcb, 0xa3, 0x2b, 0xce, 0xf1, 0xcc, 0xab, 0x74, 0xc0, 0x53, 0xc5, - 0xdc, 0xfe, 0x91, 0xb7, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x60, 0xb7, 0x21, 0x13, 0xf0, 0x02, - 0x00, 0x00, + // 310 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x92, 0x41, 0x4e, 0xc3, 0x30, + 0x10, 0x45, 0xe9, 0x82, 0xaa, 0x35, 0x62, 0xe3, 0x65, 0xa0, 0x80, 0xd8, 0x93, 0x48, 0xe5, 0x0c, + 0xa8, 0x54, 0x20, 0x54, 0x01, 0x2b, 0x76, 0x71, 0x34, 0x0d, 0x91, 0x92, 0xd8, 0x78, 0x1c, 0x8e, + 0xc5, 0x99, 0x38, 0x04, 0x07, 0x20, 0x76, 0x6c, 0xd7, 0x51, 0x9d, 0x55, 0x46, 0xff, 0x8d, 0x9f, + 0xf2, 0x2d, 0x93, 0xa5, 0x14, 0x45, 0x2a, 0x24, 0x57, 0x9c, 0x9e, 0xf6, 0xa3, 0x60, 0xc9, 0xb6, + 0xac, 0xd4, 0x67, 0xc7, 0xd2, 0x82, 0x37, 0x59, 0xc1, 0x25, 0x70, 0xb4, 0x9f, 0x3b, 0x96, 0x4b, + 0x68, 0x40, 0xe5, 0x75, 0xc6, 0x38, 0x57, 0xc5, 0xbe, 0xcc, 0x10, 0xe4, 0x37, 0x48, 0xfb, 0x11, + 0x2c, 0x6b, 0x00, 0x31, 0x2f, 0x01, 0x07, 0xe3, 0xfa, 0x77, 0x46, 0xe6, 0x1b, 0xc9, 0x3b, 0x81, + 0xf4, 0x81, 0x2c, 0xcc, 0xb4, 0xeb, 0x14, 0x4d, 0x52, 0xbb, 0x27, 0x58, 0xea, 0xc2, 0x57, 0xf8, + 0xea, 0x00, 0x55, 0x72, 0x11, 0x65, 0x28, 0x78, 0x8b, 0x70, 0x7b, 0xe2, 0x35, 0x1b, 0x88, 0x68, + 0xfa, 0x70, 0x52, 0x63, 0x98, 0xd7, 0x3c, 0x92, 0xa5, 0x49, 0x9f, 0x2b, 0x54, 0xf4, 0x68, 0x57, + 0xa7, 0x4e, 0x74, 0x19, 0x87, 0xce, 0xb4, 0xfe, 0x9b, 0x91, 0xc5, 0x4e, 0xf2, 0x7d, 0x55, 0x03, + 0xd2, 0x27, 0x42, 0xec, 0xac, 0x6b, 0x86, 0x47, 0x0f, 0xb1, 0x13, 0xaf, 0x26, 0xa8, 0xff, 0xc7, + 0x83, 0x4c, 0x97, 0x8d, 0xc8, 0x82, 0xba, 0xab, 0x09, 0xea, 0x65, 0x2f, 0xe4, 0xcc, 0xe6, 0xa6, + 0x72, 0x64, 0x3f, 0x2c, 0x7d, 0x35, 0x85, 0x7d, 0xed, 0x0f, 0xb2, 0xd8, 0x96, 0x6d, 0xa5, 0x2a, + 0xde, 0x6a, 0xb7, 0x9b, 0x75, 0xed, 0xd0, 0x1d, 0xe4, 0x31, 0xf7, 0x08, 0x7b, 0xf7, 0x4f, 0xff, + 0x6a, 0xde, 0xa0, 0x86, 0x42, 0x69, 0xf5, 0x30, 0x99, 0xab, 0x1f, 0xa9, 0x83, 0x3c, 0xa6, 0x1e, + 0x61, 0x7f, 0x0d, 0xef, 0xe4, 0x7c, 0x00, 0xb6, 0x15, 0xbd, 0x3e, 0x3a, 0x62, 0x89, 0x73, 0xde, + 0x4c, 0x2f, 0x38, 0x2b, 0x9b, 0x9b, 0xd7, 0x7e, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xa5, 0x3b, + 0x64, 0x50, 0x4c, 0x03, 0x00, 0x00, } diff --git a/bootcfg/rpc/rpcpb/rpc.proto b/bootcfg/rpc/rpcpb/rpc.proto index 4f53f87e..f76e7107 100644 --- a/bootcfg/rpc/rpcpb/rpc.proto +++ b/bootcfg/rpc/rpcpb/rpc.proto @@ -21,6 +21,11 @@ service Profiles { rpc ProfileList(messagepb.ProfileListRequest) returns (messagepb.ProfileListResponse) {}; } +service Ignition { + // Create or update an Ignition template. + rpc IgnitionPut(messagepb.IgnitionPutRequest) returns (messagepb.IgnitionPutResponse) {}; +} + service Select { // SelectGroup returns the Group matching the given labels. rpc SelectGroup(messagepb.SelectGroupRequest) returns (messagepb.SelectGroupResponse) {}; diff --git a/bootcfg/rpc/select.go b/bootcfg/rpc/select.go index fafc7e08..1c8c65b8 100644 --- a/bootcfg/rpc/select.go +++ b/bootcfg/rpc/select.go @@ -10,7 +10,6 @@ import ( // selectServer wraps a bootcfg Server to be suitable for gRPC registration. type selectServer struct { - // bootcfg Server srv server.Server } diff --git a/bootcfg/server/server.go b/bootcfg/server/server.go index d757f209..c4592cdd 100644 --- a/bootcfg/server/server.go +++ b/bootcfg/server/server.go @@ -38,9 +38,12 @@ type Server interface { // List all Profiles. ProfileList(context.Context, *pb.ProfileListRequest) ([]*storagepb.Profile, error) - // Returns an Ignition config tempate by name. + // Create or update an Ignition template. + IgnitionPut(context.Context, *pb.IgnitionPutRequest) (string, error) + // Get an Ignition template by name. IgnitionGet(ctx context.Context, name string) (string, error) - // Returns a Cloud config template by name. + + // Get a Cloud-Config template by name. CloudGet(ctx context.Context, name string) (string, error) } @@ -148,7 +151,16 @@ func (s *server) SelectProfile(ctx context.Context, req *pb.SelectProfileRequest return nil, ErrNoMatchingGroup } -// IgnitionGet gets an Ignition Config template by name. +// IgnitionPut creates or updates an Ignition template by name. +func (s *server) IgnitionPut(ctx context.Context, req *pb.IgnitionPutRequest) (string, error) { + err := s.store.IgnitionPut(req.Name, req.Config) + if err != nil { + return "", err + } + return string(req.Config), err +} + +// IgnitionGet gets an Ignition template by name. func (s *server) IgnitionGet(ctx context.Context, name string) (string, error) { return s.store.IgnitionGet(name) } diff --git a/bootcfg/server/server_test.go b/bootcfg/server/server_test.go index 17dff201..b36bf49d 100644 --- a/bootcfg/server/server_test.go +++ b/bootcfg/server/server_test.go @@ -175,3 +175,29 @@ func TestProfiles_BrokenStore(t *testing.T) { _, err = srv.ProfileList(context.Background(), &pb.ProfileListRequest{}) assert.Error(t, err) } + +func TestIgnitionPut(t *testing.T) { + srv := NewServer(&Config{Store: fake.NewFixedStore()}) + req := &pb.IgnitionPutRequest{ + Name: fake.IgnitionYAMLName, + Config: []byte(fake.IgnitionYAML), + } + _, err := srv.IgnitionPut(context.Background(), req) + // assert that: + // - Ignition template creation is successful + // - Ignition template can be retrieved by name + assert.Nil(t, err) + template, err := srv.IgnitionGet(context.Background(), fake.IgnitionYAMLName) + assert.Equal(t, fake.IgnitionYAML, template) + assert.Nil(t, err) +} + +func TestIgnition_BrokenStore(t *testing.T) { + srv := NewServer(&Config{&fake.BrokenStore{}}) + req := &pb.IgnitionPutRequest{ + Name: fake.IgnitionYAMLName, + Config: []byte(fake.IgnitionYAML), + } + _, err := srv.IgnitionPut(context.Background(), req) + assert.Error(t, err) +} diff --git a/bootcfg/server/serverpb/messages.pb.go b/bootcfg/server/serverpb/messages.pb.go index 40694122..e9bd064a 100644 --- a/bootcfg/server/serverpb/messages.pb.go +++ b/bootcfg/server/serverpb/messages.pb.go @@ -25,6 +25,8 @@ It has these top-level messages: ProfileGetResponse ProfileListRequest ProfileListResponse + IgnitionPutRequest + IgnitionPutResponse */ package messagepb @@ -252,6 +254,24 @@ func (m *ProfileListResponse) GetProfiles() []*storagepb.Profile { return nil } +type IgnitionPutRequest struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Config []byte `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` +} + +func (m *IgnitionPutRequest) Reset() { *m = IgnitionPutRequest{} } +func (m *IgnitionPutRequest) String() string { return proto.CompactTextString(m) } +func (*IgnitionPutRequest) ProtoMessage() {} +func (*IgnitionPutRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +type IgnitionPutResponse struct { +} + +func (m *IgnitionPutResponse) Reset() { *m = IgnitionPutResponse{} } +func (m *IgnitionPutResponse) String() string { return proto.CompactTextString(m) } +func (*IgnitionPutResponse) ProtoMessage() {} +func (*IgnitionPutResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } + func init() { proto.RegisterType((*SelectGroupRequest)(nil), "messagepb.SelectGroupRequest") proto.RegisterType((*SelectGroupResponse)(nil), "messagepb.SelectGroupResponse") @@ -269,33 +289,38 @@ func init() { proto.RegisterType((*ProfileGetResponse)(nil), "messagepb.ProfileGetResponse") proto.RegisterType((*ProfileListRequest)(nil), "messagepb.ProfileListRequest") proto.RegisterType((*ProfileListResponse)(nil), "messagepb.ProfileListResponse") + proto.RegisterType((*IgnitionPutRequest)(nil), "messagepb.IgnitionPutRequest") + proto.RegisterType((*IgnitionPutResponse)(nil), "messagepb.IgnitionPutResponse") } var fileDescriptor0 = []byte{ - // 395 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x53, 0xc1, 0x6a, 0xea, 0x40, - 0x14, 0x25, 0x8a, 0xbe, 0xe7, 0x15, 0x7c, 0x3a, 0xfa, 0x40, 0x5c, 0xb5, 0x29, 0x14, 0x4b, 0xdb, - 0x11, 0xec, 0xa6, 0x0a, 0x2e, 0x6c, 0x11, 0xa1, 0xb8, 0x10, 0xfb, 0x05, 0x49, 0x7a, 0x4d, 0x43, - 0xa3, 0x93, 0x66, 0x26, 0x05, 0x7f, 0xa3, 0x8b, 0x7e, 0x6f, 0x35, 0xb9, 0x93, 0x46, 0x2d, 0xa5, - 0x96, 0xae, 0x32, 0x73, 0xee, 0x39, 0xe7, 0x72, 0xce, 0x10, 0xa8, 0x2c, 0x50, 0x4a, 0xcb, 0x45, - 0xc9, 0x83, 0x50, 0x28, 0xc1, 0x4a, 0x74, 0x0f, 0xec, 0xd6, 0x9d, 0xeb, 0xa9, 0xc7, 0xc8, 0xe6, - 0x8e, 0x58, 0x74, 0x1c, 0x11, 0xa2, 0x90, 0xf4, 0xb9, 0xb4, 0xad, 0x10, 0x17, 0xa8, 0x2c, 0xbf, - 0x63, 0x0b, 0xa1, 0x9c, 0xb9, 0xdb, 0x91, 0x4a, 0x84, 0x6b, 0x95, 0xfe, 0x06, 0xb6, 0x3e, 0x25, - 0xb6, 0xe6, 0xab, 0x01, 0xec, 0x1e, 0x7d, 0x74, 0xd4, 0x38, 0x14, 0x51, 0x30, 0xc3, 0xe7, 0x08, - 0xa5, 0x62, 0x43, 0x28, 0xfa, 0x96, 0x8d, 0xbe, 0x6c, 0x1a, 0x47, 0xf9, 0x76, 0xb9, 0x7b, 0xc6, - 0xd3, 0xf5, 0x7c, 0x9f, 0xce, 0x27, 0x31, 0x77, 0xb4, 0x54, 0xe1, 0x6a, 0x46, 0xc2, 0x56, 0x0f, - 0xca, 0x19, 0x98, 0x55, 0x21, 0xff, 0x84, 0xab, 0xb5, 0x9d, 0xd1, 0x2e, 0xcd, 0x36, 0x47, 0xd6, - 0x80, 0xc2, 0x8b, 0xe5, 0x47, 0xd8, 0xcc, 0xc5, 0x58, 0x72, 0xe9, 0xe7, 0xae, 0x0d, 0x73, 0x00, - 0xf5, 0xad, 0x25, 0x32, 0x10, 0x4b, 0x89, 0xec, 0x14, 0x0a, 0xee, 0x06, 0x88, 0x4d, 0xca, 0xdd, - 0x2a, 0x4f, 0x43, 0xf1, 0x84, 0x98, 0x8c, 0xcd, 0x37, 0x03, 0x1a, 0x89, 0x7e, 0x1a, 0x8a, 0xb9, - 0xe7, 0xa3, 0x4e, 0x75, 0xbb, 0x93, 0xea, 0x7c, 0x2f, 0xd5, 0xb6, 0xe0, 0xb7, 0x73, 0x8d, 0xe0, - 0xff, 0xce, 0x1a, 0x4a, 0x76, 0x01, 0x7f, 0x82, 0x04, 0xa2, 0x6c, 0x2c, 0x93, 0x4d, 0x93, 0x35, - 0xc5, 0xec, 0xc1, 0xbf, 0x38, 0xef, 0x34, 0x52, 0x3a, 0xd9, 0x77, 0xab, 0x61, 0x50, 0xfd, 0x90, - 0x26, 0xcb, 0xcd, 0x63, 0xb2, 0x1b, 0x63, 0x6a, 0x57, 0x81, 0x9c, 0xf7, 0x40, 0x99, 0xd6, 0xa7, - 0x54, 0x36, 0xf1, 0xa4, 0xe6, 0x98, 0x7d, 0xc2, 0x62, 0xd9, 0x81, 0x2f, 0x34, 0x80, 0x5a, 0xc6, - 0x8f, 0xc4, 0x6d, 0x28, 0xc6, 0x53, 0xfd, 0x3a, 0xfb, 0x6a, 0x9a, 0x9b, 0x43, 0xa8, 0x51, 0x29, - 0x99, 0x0a, 0x0e, 0xeb, 0xb0, 0x01, 0x2c, 0x6b, 0x41, 0x55, 0x9c, 0xa4, 0xc6, 0x5f, 0x94, 0x71, - 0x93, 0x4a, 0xb3, 0xd1, 0x7f, 0xba, 0x3e, 0x5b, 0xe9, 0x08, 0xea, 0x5b, 0x28, 0x59, 0x73, 0xf8, - 0x4b, 0x3a, 0x5d, 0xcd, 0x67, 0xde, 0x29, 0xc7, 0x2e, 0xc6, 0xbf, 0xf6, 0xd5, 0x7b, 0x00, 0x00, - 0x00, 0xff, 0xff, 0xba, 0xcc, 0xec, 0xd6, 0x43, 0x04, 0x00, 0x00, + // 439 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x54, 0xd1, 0x6e, 0xd3, 0x30, + 0x14, 0x55, 0x3a, 0x16, 0xd8, 0x2d, 0x1a, 0x9d, 0xdb, 0xa1, 0x69, 0x4f, 0x60, 0x24, 0x54, 0x04, + 0xb8, 0xd2, 0x78, 0x61, 0x93, 0x26, 0x31, 0x50, 0x35, 0x81, 0xf6, 0x30, 0x85, 0x2f, 0x48, 0xc2, + 0x6d, 0x88, 0x48, 0xe2, 0x10, 0x3b, 0x48, 0xfd, 0x0d, 0x1e, 0xf8, 0x5e, 0x5a, 0xfb, 0x3a, 0x38, + 0x2d, 0x42, 0x14, 0xf1, 0xe4, 0xeb, 0x7b, 0xcf, 0x39, 0xb7, 0xe7, 0xb8, 0x0a, 0x1c, 0x96, 0xa8, + 0x54, 0x9c, 0xa1, 0x12, 0x75, 0x23, 0xb5, 0x64, 0x07, 0x74, 0xaf, 0x93, 0xd3, 0x0f, 0x59, 0xae, + 0x3f, 0xb7, 0x89, 0x48, 0x65, 0x39, 0x4b, 0x65, 0x83, 0x52, 0xd1, 0xf1, 0x32, 0x89, 0x1b, 0x2c, + 0x51, 0xc7, 0xc5, 0x2c, 0x91, 0x52, 0xa7, 0x8b, 0x6c, 0xa6, 0xb4, 0x6c, 0x56, 0x2c, 0x77, 0xd6, + 0x89, 0xab, 0xac, 0x2c, 0xff, 0x1e, 0x00, 0xfb, 0x88, 0x05, 0xa6, 0xfa, 0xba, 0x91, 0x6d, 0x1d, + 0xe1, 0xd7, 0x16, 0x95, 0x66, 0x57, 0x10, 0x16, 0x71, 0x82, 0x85, 0x3a, 0x09, 0x1e, 0xed, 0x4d, + 0x87, 0x67, 0xcf, 0x44, 0xb7, 0x5e, 0x6c, 0xc3, 0xc5, 0x8d, 0xc1, 0xce, 0x2b, 0xdd, 0x2c, 0x23, + 0x22, 0x9e, 0x9e, 0xc3, 0xd0, 0x6b, 0xb3, 0x11, 0xec, 0x7d, 0xc1, 0xe5, 0x4a, 0x2e, 0x98, 0x1e, + 0x44, 0xeb, 0x92, 0x4d, 0x60, 0xff, 0x5b, 0x5c, 0xb4, 0x78, 0x32, 0x30, 0x3d, 0x7b, 0xb9, 0x18, + 0xbc, 0x0e, 0xf8, 0x25, 0x8c, 0x7b, 0x4b, 0x54, 0x2d, 0x2b, 0x85, 0xec, 0x29, 0xec, 0x67, 0xeb, + 0x86, 0x11, 0x19, 0x9e, 0x8d, 0x44, 0x67, 0x4a, 0x58, 0xa0, 0x1d, 0xf3, 0x1f, 0x01, 0x4c, 0x2c, + 0xff, 0xb6, 0x91, 0x8b, 0xbc, 0x40, 0xe7, 0xea, 0xdd, 0x86, 0xab, 0xe7, 0x5b, 0xae, 0xfa, 0x84, + 0xff, 0xed, 0x6b, 0x0e, 0xc7, 0x1b, 0x6b, 0xc8, 0xd9, 0x0b, 0xb8, 0x5b, 0xdb, 0x16, 0x79, 0x63, + 0x9e, 0x37, 0x07, 0x76, 0x10, 0x7e, 0x0e, 0x0f, 0x8c, 0xdf, 0xdb, 0x56, 0x3b, 0x67, 0x7f, 0x1b, + 0x0d, 0x83, 0xd1, 0x2f, 0xaa, 0x5d, 0xce, 0x1f, 0x93, 0xdc, 0x35, 0x76, 0x72, 0x87, 0x30, 0xc8, + 0x3f, 0x91, 0xa7, 0x55, 0xd5, 0xd1, 0x6e, 0x72, 0xe5, 0x30, 0xfc, 0x82, 0x7a, 0x86, 0xb6, 0xe3, + 0x0b, 0x5d, 0xc2, 0x91, 0xa7, 0x47, 0xe4, 0x29, 0x84, 0x66, 0xea, 0x5e, 0x67, 0x9b, 0x4d, 0x73, + 0x7e, 0x05, 0x47, 0x14, 0x8a, 0x17, 0xc1, 0x6e, 0x19, 0x4e, 0x80, 0xf9, 0x12, 0x14, 0xc5, 0x93, + 0x4e, 0xf8, 0x0f, 0x61, 0xbc, 0xed, 0xa8, 0xbe, 0xf5, 0x7f, 0x5d, 0xef, 0x47, 0x3a, 0x87, 0x71, + 0xaf, 0x4b, 0xd2, 0x02, 0xee, 0x11, 0xcf, 0x45, 0xf3, 0x3b, 0xed, 0x0e, 0xc3, 0xdf, 0x00, 0x7b, + 0x9f, 0x55, 0xb9, 0xce, 0x65, 0xe5, 0xe5, 0xc3, 0xe0, 0x4e, 0x15, 0x97, 0x48, 0x46, 0x4c, 0xcd, + 0x1e, 0x42, 0x98, 0xca, 0x6a, 0x91, 0x67, 0xe6, 0xbf, 0x7a, 0x3f, 0xa2, 0x1b, 0x3f, 0x86, 0x71, + 0x4f, 0xc1, 0xfe, 0x90, 0x24, 0x34, 0xdf, 0x8c, 0x57, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xfb, + 0x98, 0x11, 0xcc, 0x9c, 0x04, 0x00, 0x00, } diff --git a/bootcfg/server/serverpb/messages.proto b/bootcfg/server/serverpb/messages.proto index 22b55004..4ce3b581 100644 --- a/bootcfg/server/serverpb/messages.proto +++ b/bootcfg/server/serverpb/messages.proto @@ -58,3 +58,10 @@ message ProfileListRequest {} message ProfileListResponse { repeated storagepb.Profile profiles = 1; } + +message IgnitionPutRequest { + string name = 1; + bytes config = 2; +} + +message IgnitionPutResponse {} diff --git a/bootcfg/storage/filestore.go b/bootcfg/storage/filestore.go index 635f8474..07e3d913 100644 --- a/bootcfg/storage/filestore.go +++ b/bootcfg/storage/filestore.go @@ -112,7 +112,12 @@ func (s *fileStore) ProfileList() ([]*storagepb.Profile, error) { return profiles, nil } -// IgnitionGet gets an Ignition Config template by name. +// IgnitionPut creates or updates an Ignition template. +func (s *fileStore) IgnitionPut(name string, config []byte) error { + return Dir(s.root).writeFile(filepath.Join("ignition", name), config) +} + +// IgnitionGet gets an Ignition template by name. func (s *fileStore) IgnitionGet(name string) (string, error) { data, err := Dir(s.root).readFile(filepath.Join("ignition", name)) return string(data), err diff --git a/bootcfg/storage/filestore_test.go b/bootcfg/storage/filestore_test.go index 918ec8e8..69095351 100644 --- a/bootcfg/storage/filestore_test.go +++ b/bootcfg/storage/filestore_test.go @@ -130,6 +130,22 @@ func TestProfileList(t *testing.T) { } } +func TestIgnitionPut(t *testing.T) { + dir, err := setup(&fake.FixedStore{}) + assert.Nil(t, err) + defer os.RemoveAll(dir) + + store := NewFileStore(&Config{Root: dir}) + // assert that: + // - Ignition template creation was successful + // - Ignition template can be retrieved by name + err = store.IgnitionPut(fake.IgnitionYAMLName, []byte(fake.IgnitionYAML)) + assert.Nil(t, err) + template, err := store.IgnitionGet(fake.IgnitionYAMLName) + assert.Nil(t, err) + assert.Equal(t, fake.IgnitionYAML, template) +} + func TestIgnitionGet(t *testing.T) { contents := `{"ignitionVersion":1,"storage":{},"systemd":{"units":[{"name":"etcd2.service","enable":true}]},"networkd":{},"passwd":{}}` dir, err := setup(&fake.FixedStore{ diff --git a/bootcfg/storage/storage.go b/bootcfg/storage/storage.go index 3bf293ce..73dc6ea8 100644 --- a/bootcfg/storage/storage.go +++ b/bootcfg/storage/storage.go @@ -28,8 +28,11 @@ type Store interface { // ProfileList lists all profiles. ProfileList() ([]*storagepb.Profile, error) - // IgnitionGet gets an Ignition Config template by name. + // IgnitionPut creates or updates an Ignition template. + IgnitionPut(name string, config []byte) error + // IgnitionGet gets an Ignition template by name. IgnitionGet(name string) (string, error) + // CloudGet gets a Cloud-Config template by name. CloudGet(name string) (string, error) } diff --git a/bootcfg/storage/testfakes/broken_store.go b/bootcfg/storage/testfakes/broken_store.go index 1274a6b5..d844aee2 100644 --- a/bootcfg/storage/testfakes/broken_store.go +++ b/bootcfg/storage/testfakes/broken_store.go @@ -43,12 +43,17 @@ func (s *BrokenStore) ProfileList() (profiles []*storagepb.Profile, err error) { return profiles, errIntentional } +// IgnitionPut returns an error. +func (s *BrokenStore) IgnitionPut(name string, config []byte) error { + return errIntentional +} + // IgnitionGet returns an error. -func (s *BrokenStore) IgnitionGet(id string) (string, error) { +func (s *BrokenStore) IgnitionGet(name string) (string, error) { return "", errIntentional } // CloudGet returns an error. -func (s *BrokenStore) CloudGet(id string) (string, error) { +func (s *BrokenStore) CloudGet(name string) (string, error) { return "", errIntentional } diff --git a/bootcfg/storage/testfakes/empty_store.go b/bootcfg/storage/testfakes/empty_store.go index 6837df82..315e69e6 100644 --- a/bootcfg/storage/testfakes/empty_store.go +++ b/bootcfg/storage/testfakes/empty_store.go @@ -39,12 +39,17 @@ func (s *EmptyStore) ProfileList() (profiles []*storagepb.Profile, err error) { return profiles, nil } -// IgnitionGet get returns an Ignition config not found error. -func (s *EmptyStore) IgnitionGet(id string) (string, error) { - return "", fmt.Errorf("no Ignition Config %s", id) +// IgnitionPut returns an error writing any Ignition template. +func (s *EmptyStore) IgnitionPut(name string, config []byte) error { + return fmt.Errorf("emptyStore does not accept Ignition templates") } -// CloudGet returns a Cloud config not found error. -func (s *EmptyStore) CloudGet(id string) (string, error) { - return "", fmt.Errorf("no Cloud Config %s", id) +// IgnitionGet get returns an Ignition template not found error. +func (s *EmptyStore) IgnitionGet(name string) (string, error) { + return "", fmt.Errorf("no Ignition template %s", name) +} + +// CloudGet returns a Cloud-config template not found error. +func (s *EmptyStore) CloudGet(name string) (string, error) { + return "", fmt.Errorf("no Cloud-Config template %s", name) } diff --git a/bootcfg/storage/testfakes/fixed_store.go b/bootcfg/storage/testfakes/fixed_store.go index 6b4a8644..acbe3ab4 100644 --- a/bootcfg/storage/testfakes/fixed_store.go +++ b/bootcfg/storage/testfakes/fixed_store.go @@ -74,18 +74,24 @@ func (s *FixedStore) ProfileList() ([]*storagepb.Profile, error) { return profiles, nil } -// IgnitionGet returns the Ignition config with the given id. -func (s *FixedStore) IgnitionGet(id string) (string, error) { - if config, present := s.IgnitionConfigs[id]; present { - return config, nil - } - return "", fmt.Errorf("no Ignition Config %s", id) +// IgnitionPut create or updates an Ignition template. +func (s *FixedStore) IgnitionPut(name string, config []byte) error { + s.IgnitionConfigs[name] = string(config) + return nil } -// CloudGet returns the Cloud config with the given id. -func (s *FixedStore) CloudGet(id string) (string, error) { - if config, present := s.CloudConfigs[id]; present { +// IgnitionGet returns an Ignition template by name. +func (s *FixedStore) IgnitionGet(name string) (string, error) { + if config, present := s.IgnitionConfigs[name]; present { return config, nil } - return "", fmt.Errorf("no Cloud Config %s", id) + return "", fmt.Errorf("no Ignition template %s", name) +} + +// CloudGet returns a Cloud-config template by name. +func (s *FixedStore) CloudGet(name string) (string, error) { + if config, present := s.CloudConfigs[name]; present { + return config, nil + } + return "", fmt.Errorf("no Cloud-Config template %s", name) } diff --git a/bootcfg/storage/testfakes/fixtures.go b/bootcfg/storage/testfakes/fixtures.go index 52043bae..92a5d931 100644 --- a/bootcfg/storage/testfakes/fixtures.go +++ b/bootcfg/storage/testfakes/fixtures.go @@ -35,4 +35,15 @@ var ( CloudId: "cloud-config.yml", IgnitionId: "ignition.json", } + + // IgnitionYAMLName is an Ignition template name for testing. + IgnitionYAMLName = "ignition.yaml" + + // IgnitionYAML is an Ignition template for testing. + IgnitionYAML = `ignition_version: 1 +systemd: + units: + - name: etcd2.service + enable: true +` )