Merge pull request #55 from spotify/ben/listing-components

Create Component in Scaffolder
This commit is contained in:
Ben Lambert
2020-02-06 14:50:41 +01:00
committed by GitHub
11 changed files with 484 additions and 67 deletions
+29
View File
@@ -0,0 +1,29 @@
name: Scaffolder CI
on:
push:
paths:
- 'backend/scaffolder/**'
- '.github/workflows/scaffolder.yml'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.12
- name: checkout code
uses: actions/checkout@v1
- name: setup env
run: |
echo "::set-env name=GOPATH::$(go env GOPATH)"
echo "::add-path::$(go env GOPATH)/bin"
shell: bash
- name: build
run: go build -v ./...
working-directory: ./backend/scaffolder
- name: test
run: go test ./... -short
working-directory: ./backend/scaffolder
+211 -50
View File
@@ -7,6 +7,7 @@ import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
_struct "github.com/golang/protobuf/ptypes/struct"
v1 "github.com/spotify/backstage/backend/proto/identity/v1"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
@@ -56,45 +57,158 @@ func (m *Empty) XXX_DiscardUnknown() {
var xxx_messageInfo_Empty proto.InternalMessageInfo
type GetAllTemplatesReply struct {
type ListTemplatesReply struct {
Templates []*Template `protobuf:"bytes,1,rep,name=templates,proto3" json:"templates,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetAllTemplatesReply) Reset() { *m = GetAllTemplatesReply{} }
func (m *GetAllTemplatesReply) String() string { return proto.CompactTextString(m) }
func (*GetAllTemplatesReply) ProtoMessage() {}
func (*GetAllTemplatesReply) Descriptor() ([]byte, []int) {
func (m *ListTemplatesReply) Reset() { *m = ListTemplatesReply{} }
func (m *ListTemplatesReply) String() string { return proto.CompactTextString(m) }
func (*ListTemplatesReply) ProtoMessage() {}
func (*ListTemplatesReply) Descriptor() ([]byte, []int) {
return fileDescriptor_6737326b433dc57d, []int{1}
}
func (m *GetAllTemplatesReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetAllTemplatesReply.Unmarshal(m, b)
func (m *ListTemplatesReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListTemplatesReply.Unmarshal(m, b)
}
func (m *GetAllTemplatesReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetAllTemplatesReply.Marshal(b, m, deterministic)
func (m *ListTemplatesReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListTemplatesReply.Marshal(b, m, deterministic)
}
func (m *GetAllTemplatesReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetAllTemplatesReply.Merge(m, src)
func (m *ListTemplatesReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListTemplatesReply.Merge(m, src)
}
func (m *GetAllTemplatesReply) XXX_Size() int {
return xxx_messageInfo_GetAllTemplatesReply.Size(m)
func (m *ListTemplatesReply) XXX_Size() int {
return xxx_messageInfo_ListTemplatesReply.Size(m)
}
func (m *GetAllTemplatesReply) XXX_DiscardUnknown() {
xxx_messageInfo_GetAllTemplatesReply.DiscardUnknown(m)
func (m *ListTemplatesReply) XXX_DiscardUnknown() {
xxx_messageInfo_ListTemplatesReply.DiscardUnknown(m)
}
var xxx_messageInfo_GetAllTemplatesReply proto.InternalMessageInfo
var xxx_messageInfo_ListTemplatesReply proto.InternalMessageInfo
func (m *GetAllTemplatesReply) GetTemplates() []*Template {
func (m *ListTemplatesReply) GetTemplates() []*Template {
if m != nil {
return m.Templates
}
return nil
}
type CreateReply struct {
ComponentId string `protobuf:"bytes,1,opt,name=component_id,json=componentId,proto3" json:"component_id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CreateReply) Reset() { *m = CreateReply{} }
func (m *CreateReply) String() string { return proto.CompactTextString(m) }
func (*CreateReply) ProtoMessage() {}
func (*CreateReply) Descriptor() ([]byte, []int) {
return fileDescriptor_6737326b433dc57d, []int{2}
}
func (m *CreateReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CreateReply.Unmarshal(m, b)
}
func (m *CreateReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CreateReply.Marshal(b, m, deterministic)
}
func (m *CreateReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_CreateReply.Merge(m, src)
}
func (m *CreateReply) XXX_Size() int {
return xxx_messageInfo_CreateReply.Size(m)
}
func (m *CreateReply) XXX_DiscardUnknown() {
xxx_messageInfo_CreateReply.DiscardUnknown(m)
}
var xxx_messageInfo_CreateReply proto.InternalMessageInfo
func (m *CreateReply) GetComponentId() string {
if m != nil {
return m.ComponentId
}
return ""
}
type CreateRequest struct {
TemplateId string `protobuf:"bytes,1,opt,name=template_id,json=templateId,proto3" json:"template_id,omitempty"`
Org string `protobuf:"bytes,2,opt,name=org,proto3" json:"org,omitempty"`
ComponentId string `protobuf:"bytes,3,opt,name=component_id,json=componentId,proto3" json:"component_id,omitempty"`
Private bool `protobuf:"varint,4,opt,name=private,proto3" json:"private,omitempty"`
// here's the cookiecutter.json that is used for the request.
// make as a struct so that we can pass through the data in a nice way
// withouth having to mess around with stuff and special types.
Metadata *_struct.Struct `protobuf:"bytes,5,opt,name=metadata,proto3" json:"metadata,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CreateRequest) Reset() { *m = CreateRequest{} }
func (m *CreateRequest) String() string { return proto.CompactTextString(m) }
func (*CreateRequest) ProtoMessage() {}
func (*CreateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_6737326b433dc57d, []int{3}
}
func (m *CreateRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CreateRequest.Unmarshal(m, b)
}
func (m *CreateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CreateRequest.Marshal(b, m, deterministic)
}
func (m *CreateRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CreateRequest.Merge(m, src)
}
func (m *CreateRequest) XXX_Size() int {
return xxx_messageInfo_CreateRequest.Size(m)
}
func (m *CreateRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CreateRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CreateRequest proto.InternalMessageInfo
func (m *CreateRequest) GetTemplateId() string {
if m != nil {
return m.TemplateId
}
return ""
}
func (m *CreateRequest) GetOrg() string {
if m != nil {
return m.Org
}
return ""
}
func (m *CreateRequest) GetComponentId() string {
if m != nil {
return m.ComponentId
}
return ""
}
func (m *CreateRequest) GetPrivate() bool {
if m != nil {
return m.Private
}
return false
}
func (m *CreateRequest) GetMetadata() *_struct.Struct {
if m != nil {
return m.Metadata
}
return nil
}
type Template struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
@@ -109,7 +223,7 @@ func (m *Template) Reset() { *m = Template{} }
func (m *Template) String() string { return proto.CompactTextString(m) }
func (*Template) ProtoMessage() {}
func (*Template) Descriptor() ([]byte, []int) {
return fileDescriptor_6737326b433dc57d, []int{2}
return fileDescriptor_6737326b433dc57d, []int{4}
}
func (m *Template) XXX_Unmarshal(b []byte) error {
@@ -160,31 +274,42 @@ func (m *Template) GetUser() *v1.User {
func init() {
proto.RegisterType((*Empty)(nil), "spotify.backstage.scaffolder.v1.Empty")
proto.RegisterType((*GetAllTemplatesReply)(nil), "spotify.backstage.scaffolder.v1.GetAllTemplatesReply")
proto.RegisterType((*ListTemplatesReply)(nil), "spotify.backstage.scaffolder.v1.ListTemplatesReply")
proto.RegisterType((*CreateReply)(nil), "spotify.backstage.scaffolder.v1.CreateReply")
proto.RegisterType((*CreateRequest)(nil), "spotify.backstage.scaffolder.v1.CreateRequest")
proto.RegisterType((*Template)(nil), "spotify.backstage.scaffolder.v1.Template")
}
func init() { proto.RegisterFile("scaffolder/v1/scaffolder.proto", fileDescriptor_6737326b433dc57d) }
var fileDescriptor_6737326b433dc57d = []byte{
// 270 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0x4f, 0x4b, 0xc4, 0x30,
0x10, 0xc5, 0xe9, 0x6e, 0xfd, 0xb3, 0x53, 0x59, 0x21, 0x78, 0x28, 0x3d, 0x68, 0xa9, 0x20, 0xf5,
0x92, 0xa5, 0x15, 0xf1, 0xac, 0x20, 0x7b, 0xaf, 0x7a, 0xf1, 0x22, 0xdd, 0x76, 0x2a, 0xc1, 0xb6,
0x09, 0xc9, 0x58, 0xe8, 0xc5, 0xbb, 0xdf, 0x5a, 0x8c, 0x74, 0xbb, 0xc8, 0x42, 0x6f, 0x93, 0x97,
0xf7, 0x4b, 0xe6, 0xcd, 0xc0, 0xb9, 0x29, 0xf2, 0xaa, 0x92, 0x75, 0x89, 0x7a, 0xd5, 0x25, 0xab,
0xf1, 0xc4, 0x95, 0x96, 0x24, 0xd9, 0x85, 0x51, 0x92, 0x44, 0xd5, 0xf3, 0x4d, 0x5e, 0x7c, 0x18,
0xca, 0xdf, 0x91, 0xef, 0x78, 0xba, 0x24, 0x08, 0x44, 0x89, 0x2d, 0x09, 0xea, 0x7f, 0xf1, 0xa1,
0xfe, 0x83, 0xa3, 0x23, 0x38, 0x78, 0x6c, 0x14, 0xf5, 0xd1, 0x1b, 0x9c, 0xad, 0x91, 0xee, 0xeb,
0xfa, 0x19, 0x1b, 0x55, 0xe7, 0x84, 0x26, 0x43, 0x55, 0xf7, 0x6c, 0x0d, 0x0b, 0x1a, 0x14, 0xdf,
0x09, 0xe7, 0xb1, 0x97, 0x5e, 0xf3, 0x89, 0x1f, 0xf9, 0xf0, 0x46, 0x36, 0xb2, 0xd1, 0xb7, 0x03,
0xc7, 0x83, 0xce, 0x96, 0x30, 0x13, 0xa5, 0xef, 0x84, 0x4e, 0xbc, 0xc8, 0x66, 0xa2, 0x64, 0x0c,
0xdc, 0x36, 0x6f, 0xd0, 0x9f, 0x59, 0xc5, 0xd6, 0x2c, 0x04, 0xaf, 0x44, 0x53, 0x68, 0xa1, 0x48,
0xc8, 0xd6, 0x9f, 0xdb, 0xab, 0x5d, 0x89, 0xdd, 0x81, 0xfb, 0x69, 0x50, 0xfb, 0x6e, 0xe8, 0xc4,
0x5e, 0x7a, 0xb9, 0xa7, 0xad, 0x6d, 0xda, 0x2e, 0xe1, 0x2f, 0x06, 0x75, 0x66, 0x81, 0xf4, 0x0b,
0xe0, 0x69, 0xdb, 0x30, 0x53, 0x70, 0xfa, 0x2f, 0x3a, 0xbb, 0x9a, 0x8c, 0x68, 0xa7, 0x16, 0xdc,
0x4e, 0xfa, 0xf6, 0x0d, 0xf5, 0x61, 0xf9, 0x7a, 0x32, 0xba, 0xba, 0x64, 0x73, 0x68, 0x97, 0x71,
0xf3, 0x13, 0x00, 0x00, 0xff, 0xff, 0x5b, 0x4d, 0x79, 0x5e, 0xeb, 0x01, 0x00, 0x00,
// 411 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0xd1, 0x8a, 0xd3, 0x40,
0x14, 0x25, 0x6d, 0x77, 0xb7, 0x7b, 0xb3, 0xbb, 0xc8, 0xbc, 0x18, 0x82, 0xb8, 0x31, 0x82, 0x54,
0x90, 0xa9, 0x6d, 0x1f, 0x7c, 0x57, 0x44, 0x16, 0x7c, 0xca, 0xea, 0x8b, 0x20, 0x32, 0x4d, 0x6e,
0xca, 0x60, 0x92, 0x19, 0x67, 0x6e, 0x03, 0xf9, 0x04, 0xff, 0xc7, 0x2f, 0xf2, 0x4b, 0xa4, 0x53,
0x27, 0xdd, 0x65, 0x0b, 0xd9, 0xb7, 0x7b, 0xcf, 0xbd, 0xe7, 0xce, 0xe1, 0x9c, 0x81, 0xe7, 0x36,
0x17, 0x65, 0xa9, 0xaa, 0x02, 0xcd, 0xbc, 0x5d, 0xcc, 0x0f, 0x1d, 0xd7, 0x46, 0x91, 0x62, 0xd7,
0x56, 0x2b, 0x92, 0x65, 0xc7, 0xd7, 0x22, 0xff, 0x69, 0x49, 0x6c, 0x90, 0xdf, 0xd9, 0x69, 0x17,
0x71, 0x2c, 0x0b, 0x6c, 0x48, 0x52, 0xb7, 0xa3, 0xfb, 0x7a, 0x4f, 0x8e, 0x9f, 0x6d, 0x94, 0xda,
0x54, 0x38, 0x77, 0xdd, 0x7a, 0x5b, 0xce, 0x2d, 0x99, 0x6d, 0x4e, 0xfb, 0x69, 0x7a, 0x06, 0x27,
0x1f, 0x6b, 0x4d, 0x5d, 0xfa, 0x1d, 0xd8, 0x67, 0x69, 0xe9, 0x0b, 0xd6, 0xba, 0x12, 0x84, 0x36,
0x43, 0x5d, 0x75, 0xec, 0x13, 0x9c, 0x93, 0x47, 0xa2, 0x20, 0x19, 0xcf, 0xc2, 0xe5, 0x6b, 0x3e,
0xa0, 0x86, 0xfb, 0x1b, 0xd9, 0x81, 0x9b, 0xbe, 0x85, 0xf0, 0x83, 0xc1, 0x1d, 0xe8, 0xee, 0xbe,
0x80, 0x8b, 0x5c, 0xd5, 0x5a, 0x35, 0xd8, 0xd0, 0x0f, 0x59, 0x44, 0x41, 0x12, 0xcc, 0xce, 0xb3,
0xb0, 0xc7, 0x6e, 0x8a, 0xf4, 0x4f, 0x00, 0x97, 0x9e, 0xf2, 0x6b, 0x8b, 0x96, 0xd8, 0x35, 0x84,
0xfe, 0xe0, 0x81, 0x03, 0x1e, 0xba, 0x29, 0xd8, 0x13, 0x18, 0x2b, 0xb3, 0x89, 0x46, 0x6e, 0xb0,
0x2b, 0x1f, 0xbc, 0x33, 0x7e, 0xf0, 0x0e, 0x8b, 0xe0, 0x4c, 0x1b, 0xd9, 0x0a, 0xc2, 0x68, 0x92,
0x04, 0xb3, 0x69, 0xe6, 0x5b, 0xb6, 0x82, 0x69, 0x8d, 0x24, 0x0a, 0x41, 0x22, 0x3a, 0x49, 0x82,
0x59, 0xb8, 0x7c, 0xca, 0xf7, 0x66, 0x72, 0x6f, 0x26, 0xbf, 0x75, 0x66, 0x66, 0xfd, 0x62, 0xfa,
0x3b, 0x80, 0xa9, 0x37, 0x80, 0x5d, 0xc1, 0xa8, 0x17, 0x3a, 0x92, 0x05, 0x63, 0x30, 0x69, 0x44,
0x8d, 0xff, 0x15, 0xba, 0x9a, 0x25, 0x10, 0x16, 0x68, 0x73, 0x23, 0x35, 0x49, 0xd5, 0x78, 0x85,
0x77, 0x20, 0xf6, 0x0e, 0x26, 0x5b, 0x8b, 0xc6, 0xc9, 0x0b, 0x97, 0x2f, 0x8f, 0xf8, 0xdf, 0x47,
0xde, 0x2e, 0xf8, 0x57, 0x8b, 0x26, 0x73, 0x84, 0xe5, 0xdf, 0x00, 0xe0, 0xb6, 0x8f, 0x86, 0x55,
0x70, 0x79, 0x2f, 0x62, 0xf6, 0x6a, 0x30, 0x4a, 0xf7, 0x37, 0xe2, 0xd5, 0xe0, 0xde, 0x91, 0xaf,
0x53, 0xc2, 0xe9, 0x3e, 0x3e, 0xc6, 0x07, 0xe9, 0xf7, 0x72, 0x8e, 0xdf, 0x3c, 0x7a, 0x5f, 0x57,
0xdd, 0xfb, 0xab, 0x6f, 0x17, 0x87, 0x61, 0xbb, 0x58, 0x9f, 0xba, 0x6c, 0x56, 0xff, 0x02, 0x00,
0x00, 0xff, 0xff, 0xfc, 0x1f, 0xfc, 0xf0, 0x55, 0x03, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -199,7 +324,8 @@ const _ = grpc.SupportPackageIsVersion6
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type ScaffolderClient interface {
GetAllTemplates(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetAllTemplatesReply, error)
ListTemplates(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ListTemplatesReply, error)
Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateReply, error)
}
type scaffolderClient struct {
@@ -210,9 +336,18 @@ func NewScaffolderClient(cc grpc.ClientConnInterface) ScaffolderClient {
return &scaffolderClient{cc}
}
func (c *scaffolderClient) GetAllTemplates(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetAllTemplatesReply, error) {
out := new(GetAllTemplatesReply)
err := c.cc.Invoke(ctx, "/spotify.backstage.scaffolder.v1.Scaffolder/GetAllTemplates", in, out, opts...)
func (c *scaffolderClient) ListTemplates(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ListTemplatesReply, error) {
out := new(ListTemplatesReply)
err := c.cc.Invoke(ctx, "/spotify.backstage.scaffolder.v1.Scaffolder/ListTemplates", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *scaffolderClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateReply, error) {
out := new(CreateReply)
err := c.cc.Invoke(ctx, "/spotify.backstage.scaffolder.v1.Scaffolder/Create", in, out, opts...)
if err != nil {
return nil, err
}
@@ -221,35 +356,57 @@ func (c *scaffolderClient) GetAllTemplates(ctx context.Context, in *Empty, opts
// ScaffolderServer is the server API for Scaffolder service.
type ScaffolderServer interface {
GetAllTemplates(context.Context, *Empty) (*GetAllTemplatesReply, error)
ListTemplates(context.Context, *Empty) (*ListTemplatesReply, error)
Create(context.Context, *CreateRequest) (*CreateReply, error)
}
// UnimplementedScaffolderServer can be embedded to have forward compatible implementations.
type UnimplementedScaffolderServer struct {
}
func (*UnimplementedScaffolderServer) GetAllTemplates(ctx context.Context, req *Empty) (*GetAllTemplatesReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetAllTemplates not implemented")
func (*UnimplementedScaffolderServer) ListTemplates(ctx context.Context, req *Empty) (*ListTemplatesReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListTemplates not implemented")
}
func (*UnimplementedScaffolderServer) Create(ctx context.Context, req *CreateRequest) (*CreateReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
}
func RegisterScaffolderServer(s *grpc.Server, srv ScaffolderServer) {
s.RegisterService(&_Scaffolder_serviceDesc, srv)
}
func _Scaffolder_GetAllTemplates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
func _Scaffolder_ListTemplates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ScaffolderServer).GetAllTemplates(ctx, in)
return srv.(ScaffolderServer).ListTemplates(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/spotify.backstage.scaffolder.v1.Scaffolder/GetAllTemplates",
FullMethod: "/spotify.backstage.scaffolder.v1.Scaffolder/ListTemplates",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ScaffolderServer).GetAllTemplates(ctx, req.(*Empty))
return srv.(ScaffolderServer).ListTemplates(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Scaffolder_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ScaffolderServer).Create(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/spotify.backstage.scaffolder.v1.Scaffolder/Create",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ScaffolderServer).Create(ctx, req.(*CreateRequest))
}
return interceptor(ctx, in, info, handler)
}
@@ -259,8 +416,12 @@ var _Scaffolder_serviceDesc = grpc.ServiceDesc{
HandlerType: (*ScaffolderServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetAllTemplates",
Handler: _Scaffolder_GetAllTemplates_Handler,
MethodName: "ListTemplates",
Handler: _Scaffolder_ListTemplates_Handler,
},
{
MethodName: "Create",
Handler: _Scaffolder_Create_Handler,
},
},
Streams: []grpc.StreamDesc{},
+40 -5
View File
@@ -2,21 +2,56 @@ package app
import (
"context"
"fmt"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
identity "github.com/spotify/backstage/backend/proto/identity/v1"
pb "github.com/spotify/backstage/backend/proto/scaffolder/v1"
"github.com/spotify/backstage/scaffolder/fs"
"github.com/spotify/backstage/scaffolder/remote"
"github.com/spotify/backstage/scaffolder/repository"
)
// Server is the inventory Grpc server
type Server struct {
Repository *repository.Repository
repository *repository.Repository
github *remote.Github
fs *fs.Filesystem
}
// GetAllTemplates returns the local templatess
func (s *Server) GetAllTemplates(ctx context.Context, req *pb.Empty) (*pb.GetAllTemplatesReply, error) {
// NewServer creates a new server for with all the things
func NewServer() *Server {
return &Server{
github: remote.NewGithubClient(),
}
}
// Create scaffolds the repo in github and then will create push to the repository
func (s *Server) Create(ctx context.Context, req *pb.CreateRequest) (*pb.CreateReply, error) {
// first create the repository with github
fmt.Sprintf("Creating repository for Component %s", req.ComponentId)
repo := remote.Repository{
Name: req.ComponentId,
Org: req.Org,
Private: req.Private,
}
if _, err := s.github.CreateRepository(repo); err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("Could not create repository %s/%s", req.Org, req.ComponentId))
}
// move the template into a temporary directory
tempFolder, _ := s.fs.PrepareTemplate(fs.Template{req.TemplateId})
fmt.Sprintf("Created temporary folder %s", tempFolder)
// use git bindings to add the remote with access token and push to the directory
return nil, nil
}
// ListTemplates returns the local templatess
func (s *Server) ListTemplates(ctx context.Context, req *pb.Empty) (*pb.ListTemplatesReply, error) {
// todo (blam): yes we currently read the disk on every load. but it's fine for now 🤷‍♂️
definitions, err := s.Repository.Load()
definitions, err := s.repository.Load()
var templates []*pb.Template
for _, definition := range definitions {
@@ -35,7 +70,7 @@ func (s *Server) GetAllTemplates(ctx context.Context, req *pb.Empty) (*pb.GetAll
templates = append(templates, template)
}
return &pb.GetAllTemplatesReply{
return &pb.ListTemplatesReply{
Templates: templates,
}, err
}
+8
View File
@@ -0,0 +1,8 @@
package app
import (
"testing"
)
func TestSample(t *testing.T) {
}
+87
View File
@@ -0,0 +1,87 @@
package fs
import (
"fmt"
"io"
"io/ioutil"
"os"
"path"
)
// Filesystem Repository
type Filesystem struct{}
func file(src, dst string) error {
var err error
var srcfd *os.File
var dstfd *os.File
var srcinfo os.FileInfo
if srcfd, err = os.Open(src); err != nil {
return err
}
defer srcfd.Close()
if dstfd, err = os.Create(dst); err != nil {
return err
}
defer dstfd.Close()
if _, err = io.Copy(dstfd, srcfd); err != nil {
return err
}
if srcinfo, err = os.Stat(src); err != nil {
return err
}
return os.Chmod(dst, srcinfo.Mode())
}
func dir(src string, dst string) error {
var err error
var fds []os.FileInfo
var srcinfo os.FileInfo
if srcinfo, err = os.Stat(src); err != nil {
return err
}
if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil {
return err
}
if fds, err = ioutil.ReadDir(src); err != nil {
return err
}
for _, fd := range fds {
srcfp := path.Join(src, fd.Name())
dstfp := path.Join(dst, fd.Name())
if fd.IsDir() {
if err = dir(srcfp, dstfp); err != nil {
fmt.Println(err)
}
} else {
if err = file(srcfp, dstfp); err != nil {
fmt.Println(err)
}
}
}
return nil
}
// Template holds the template ID for copying
type Template struct {
ID string
}
// PrepareTemplate will copy the local template to temp folder
// and return the temp location
func (f *Filesystem) PrepareTemplate(template Template) (string, error) {
tempDirectory, _ := ioutil.TempDir(os.TempDir(), template.ID)
fmt.Println(tempDirectory)
if err := dir("./templates/"+template.ID, tempDirectory); err != nil {
return "", err
}
return tempDirectory, nil
}
+8 -1
View File
@@ -2,9 +2,16 @@ module github.com/spotify/backstage/scaffolder
go 1.13
replace github.com/spotify/backstage/backend/proto => ../proto
replace (
github.com/spotify/backstage/backend/proto => ./../proto
github.com/spotify/backstage/proto => ../proto
)
require (
github.com/golang/protobuf v1.3.3
github.com/google/go-github/v29 v29.0.2
github.com/spotify/backstage/backend/proto v0.0.0-00010101000000-000000000000
github.com/spotify/backstage/proto v0.0.0-00010101000000-000000000000
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
google.golang.org/grpc v1.27.0
)
+7
View File
@@ -14,7 +14,13 @@ github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts=
github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -25,6 +31,7 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+3 -2
View File
@@ -6,7 +6,6 @@ import (
pb "github.com/spotify/backstage/backend/proto/scaffolder/v1"
"github.com/spotify/backstage/scaffolder/app"
"google.golang.org/grpc"
)
@@ -21,7 +20,9 @@ func main() {
}
grpcServer := grpc.NewServer()
pb.RegisterScaffolderServer(grpcServer, &app.Server{})
serverHandler := app.NewServer()
pb.RegisterScaffolderServer(grpcServer, serverHandler)
log.Println("Serving Scaffolder Service")
grpcServer.Serve(lis)
}
+62
View File
@@ -0,0 +1,62 @@
package remote
import (
"context"
"fmt"
gh "github.com/google/go-github/v29/github"
"golang.org/x/oauth2"
"os"
)
// Github is the exported struct
type Github struct {
client *gh.Client
ctx *context.Context
}
// NewGithubClient returns a new client with the correct access token enabled
func NewGithubClient() *Github {
accessToken := os.Getenv("BOSS_GH_ACCESS_TOKEN")
if accessToken == "" {
fmt.Println("No BOSS_GH_ACCESS_TOKEN set. Cannot continue")
os.Exit(1)
}
ctx := context.Background()
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: accessToken},
)
tc := oauth2.NewClient(ctx, ts)
client := gh.NewClient(tc)
return &Github{
client: client,
ctx: &ctx,
}
}
// Repository holds the information of the created repo
type Repository struct {
Org string
Name string
Private bool
}
// CreateRepository will create the repository in Github ready for use by the scaffolder
func (g *Github) CreateRepository(repo Repository) (*gh.Repository, error) {
ghRepo := &gh.Repository{
Name: &repo.Name,
Private: &repo.Private,
}
var org string
if repo.Org != "" {
org = repo.Org
}
created, _, err := g.client.Repositories.Create(*g.ctx, org, ghRepo)
return created, err
}
+3 -2
View File
@@ -20,9 +20,10 @@ type TemplateDefinition struct {
// Load will return all the Repository templates
func (s *Repository) Load() ([]*TemplateDefinition, error) {
templateInfoFilePaths, err := filepath.Glob("templates/**/template-info.json")
var templateDefinitions []*TemplateDefinition
templateInfoFilePaths, err := filepath.Glob("templates/**/template-info.json")
if err != nil {
fmt.Errorf("failed to load template-info files")
return nil, err
@@ -45,5 +46,5 @@ func (s *Repository) Load() ([]*TemplateDefinition, error) {
templateDefinitions = append(templateDefinitions, &definition)
}
return templateDefinitions, err
return templateDefinitions, nil
}
+26 -7
View File
@@ -3,22 +3,41 @@ syntax = "proto3";
package spotify.backstage.scaffolder.v1;
import "identity/v1/identity.proto";
import "google/protobuf/struct.proto";
option go_package = "scaffolderv1";
service Scaffolder {
rpc GetAllTemplates(Empty) returns (GetAllTemplatesReply);
rpc ListTemplates(Empty) returns (ListTemplatesReply);
rpc Create(CreateRequest) returns (CreateReply);
}
message Empty {}
message GetAllTemplatesReply {
message ListTemplatesReply {
repeated Template templates = 1;
}
message Template {
string id = 1;
string name = 2;
string description = 3;
spotify.backstage.identity.v1.User user = 4;
message CreateReply {
string component_id = 1;
}
message CreateRequest {
string template_id = 1;
string org = 2;
string component_id = 3;
bool private = 4;
// here's the cookiecutter.json that is used for the request.
// make as a struct so that we can pass through the data in a nice way
// withouth having to mess around with stuff and special types.
google.protobuf.Struct metadata = 5;
}
message Template {
string id = 1;
string name = 2;
string description = 3;
spotify.backstage.identity.v1.User user = 4;
}