Tech QA Feedback & add support for organization level templates

This commit is contained in:
Eoin McAfee 2021-05-28 16:59:00 +01:00
parent a7ef135403
commit 88a5a8e796
34 changed files with 407 additions and 3009 deletions

View file

@ -87,6 +87,7 @@ tasks:
- cmd: go test -count=1 github.com/drone/drone/store/secret/global
- cmd: go test -count=1 github.com/drone/drone/store/stage
- cmd: go test -count=1 github.com/drone/drone/store/step
- cmd: go test -count=1 github.com/drone/drone/store/template
- cmd: go test -count=1 github.com/drone/drone/store/user
- cmd: docker kill mysql
@ -120,6 +121,7 @@ tasks:
- cmd: go test -count=1 github.com/drone/drone/store/secret/global
- cmd: go test -count=1 github.com/drone/drone/store/stage
- cmd: go test -count=1 github.com/drone/drone/store/step
- cmd: go test -count=1 github.com/drone/drone/store/template
- cmd: go test -count=1 github.com/drone/drone/store/user
- cmd: docker kill postgres
silent: true

View file

@ -32,23 +32,27 @@ type (
}
Template struct {
Id int64 `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Data []byte `json:"data,omitempty"`
Created int64 `json:"created,omitempty"`
Updated int64 `json:"updated,omitempty"`
Id int64 `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Namespace string `json:"namespace,omitempty"`
Data string `json:"data,omitempty"`
Created int64 `json:"created,omitempty"`
Updated int64 `json:"updated,omitempty"`
}
// TemplateStore manages repository templates.
TemplateStore interface {
// List returns template list at org level
List(ctx context.Context, namespace string) ([]*Template, error)
// ListAll returns templates list from the datastore.
ListAll(ctx context.Context) ([]*Template, error)
// Find returns a template from the datastore.
Find(ctx context.Context, id int64) (*Template, error)
// FindName returns a template from the datastore by name
FindName(ctx context.Context, name string) (*Template, error)
// FindName returns a template from the data store
FindName(ctx context.Context, name string, namespace string) (*Template, error)
// Create persists a new template to the datastore.
Create(ctx context.Context, template *Template) error

View file

@ -15,10 +15,11 @@
package api
import (
"github.com/drone/drone/handler/api/template"
"net/http"
"os"
"github.com/drone/drone/handler/api/template"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/acl"
"github.com/drone/drone/handler/api/auth"
@ -358,12 +359,13 @@ func (s Server) Handler() http.Handler {
})
r.Route("/templates", func(r chi.Router) {
r.With(acl.CheckMembership(s.Orgs, false)).Get("/", template.HandleList(s.Template))
r.With(acl.CheckMembership(s.Orgs, false)).Get("/", template.HandleListAll(s.Template))
r.With(acl.CheckMembership(s.Orgs, true)).Post("/", template.HandleCreate(s.Template))
r.With(acl.CheckMembership(s.Orgs, false)).Get("/{name}", template.HandleFind(s.Template))
r.With(acl.CheckMembership(s.Orgs, true)).Put("/{name}", template.HandleUpdate(s.Template))
r.With(acl.CheckMembership(s.Orgs, true)).Patch("/{name}", template.HandleUpdate(s.Template))
r.With(acl.CheckMembership(s.Orgs, true)).Delete("/{name}", template.HandleDelete(s.Template))
r.With(acl.CheckMembership(s.Orgs, false)).Get("/{namespace}", template.HandleList(s.Template))
r.With(acl.CheckMembership(s.Orgs, false)).Get("/{namespace}/{name}", template.HandleFind(s.Template))
r.With(acl.CheckMembership(s.Orgs, true)).Put("/{namespace}/{name}", template.HandleUpdate(s.Template))
r.With(acl.CheckMembership(s.Orgs, true)).Patch("/{namespace}/{name}", template.HandleUpdate(s.Template))
r.With(acl.CheckMembership(s.Orgs, true)).Delete("/{namespace}/{name}", template.HandleDelete(s.Template))
})
r.Route("/system", func(r chi.Router) {

View file

@ -0,0 +1,26 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/render"
"net/http"
)
// HandleListAll returns an http.HandlerFunc that writes a json-encoded
// list of templates to the response body.
func HandleListAll(templateStore core.TemplateStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
list, err := templateStore.ListAll(r.Context())
if err != nil {
render.NotFound(w, err)
return
}
render.JSON(w, list, 200)
}
}

View file

@ -0,0 +1,82 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"context"
"encoding/json"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/errors"
"github.com/drone/drone/mock"
"github.com/go-chi/chi"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"net/http"
"net/http/httptest"
"testing"
)
var (
dummyTemplate = &core.Template{
Name: "my_template",
Data: "my_data",
Created: 1,
Updated: 2,
Namespace: "my_org",
}
dummyTemplateList = []*core.Template{
dummyTemplate,
}
)
func TestHandleAll(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().ListAll(gomock.Any()).Return(dummyTemplateList, nil)
c := new(chi.Context)
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleListAll(templates).ServeHTTP(w, r)
if got, want := w.Code, http.StatusOK; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
}
func TestHandleAll_TemplateListErr(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().ListAll(gomock.Any()).Return(nil, errors.ErrNotFound)
c := new(chi.Context)
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleListAll(templates).ServeHTTP(w, r)
if got, want := w.Code, http.StatusNotFound; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := new(errors.Error), errors.ErrNotFound
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}

View file

@ -14,8 +14,9 @@ import (
)
type templateInput struct {
Name string `json:"name"`
Data []byte `json:"data"`
Name string `json:"name"`
Data string `json:"data"`
Namespace string `json:"namespace"`
}
// HandleCreate returns an http.HandlerFunc that processes http
@ -30,8 +31,9 @@ func HandleCreate(templateStore core.TemplateStore) http.HandlerFunc {
}
t := &core.Template{
Name: in.Name,
Data: in.Data,
Name: in.Name,
Data: in.Data,
Namespace: in.Namespace,
}
err = t.Validate()

View file

@ -51,7 +51,7 @@ func TestHandleCreate_ValidationErrorName(t *testing.T) {
c := new(chi.Context)
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(&core.Template{Name: "", Data: []byte("my_data")})
json.NewEncoder(in).Encode(&core.Template{Name: "", Data: "my_data"})
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", in)
@ -77,7 +77,7 @@ func TestHandleCreate_ValidationErrorData(t *testing.T) {
c := new(chi.Context)
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(&core.Template{Name: "my_template", Data: nil})
json.NewEncoder(in).Encode(&core.Template{Name: "my_template", Data: ""})
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", in)

View file

@ -18,9 +18,10 @@ import (
func HandleDelete(template core.TemplateStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var (
name = chi.URLParam(r, "name")
name = chi.URLParam(r, "name")
namespace = chi.URLParam(r, "namespace")
)
s, err := template.FindName(r.Context(), name)
s, err := template.FindName(r.Context(), name, namespace)
if err != nil {
render.NotFound(w, err)
return

View file

@ -24,11 +24,12 @@ func TestHandleDelete(t *testing.T) {
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name).Return(dummyTemplate, nil)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(dummyTemplate, nil)
template.EXPECT().Delete(gomock.Any(), dummyTemplate).Return(nil)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
@ -47,10 +48,11 @@ func TestHandleDelete_TemplateNotFound(t *testing.T) {
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name).Return(nil, errors.ErrNotFound)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(nil, errors.ErrNotFound)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
@ -75,11 +77,12 @@ func TestHandleDelete_DeleteError(t *testing.T) {
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name).Return(dummyTemplate, nil)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(dummyTemplate, nil)
template.EXPECT().Delete(gomock.Any(), dummyTemplate).Return(errors.ErrNotFound)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)

View file

@ -18,9 +18,10 @@ import (
func HandleFind(templateStore core.TemplateStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var (
name = chi.URLParam(r, "name")
name = chi.URLParam(r, "name")
namespace = chi.URLParam(r, "namespace")
)
template, err := templateStore.FindName(r.Context(), name)
template, err := templateStore.FindName(r.Context(), name, namespace)
if err != nil {
render.NotFound(w, err)
return

View file

@ -24,10 +24,11 @@ func TestHandleFind(t *testing.T) {
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name).Return(dummyTemplate, nil)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(dummyTemplate, nil)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
@ -46,10 +47,11 @@ func TestHandleFind_TemplateNotFound(t *testing.T) {
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name).Return(nil, errors.ErrNotFound)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(nil, errors.ErrNotFound)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)

View file

@ -9,14 +9,16 @@ package template
import (
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/render"
"github.com/go-chi/chi"
"net/http"
)
// HandleList returns an http.HandlerFunc that writes a json-encoded
// list of templates to the response body.
// list of templates to the response body by namespace
func HandleList(templateStore core.TemplateStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
list, err := templateStore.ListAll(r.Context())
namespace := chi.URLParam(r, "namespace")
list, err := templateStore.List(r.Context(), namespace)
if err != nil {
render.NotFound(w, err)
return

View file

@ -1,15 +1,12 @@
package template
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"context"
"encoding/json"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/errors"
"github.com/drone/drone/mock"
"github.com/go-chi/chi"
@ -20,26 +17,15 @@ import (
"testing"
)
var (
dummyTemplate = &core.Template{
Name: "my_template",
Data: []byte("my_data"),
Created: 1,
Updated: 2,
}
dummyTemplateList = []*core.Template{
dummyTemplate,
}
)
func TestHandleList(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().ListAll(gomock.Any()).Return(dummyTemplateList, nil)
templates.EXPECT().List(gomock.Any(), dummyTemplate.Namespace).Return(dummyTemplateList, nil)
c := new(chi.Context)
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
@ -58,9 +44,10 @@ func TestHandleList_TemplateListErr(t *testing.T) {
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().ListAll(gomock.Any()).Return(nil, errors.ErrNotFound)
templates.EXPECT().List(gomock.Any(), dummyTemplate.Namespace).Return(nil, errors.ErrNotFound)
c := new(chi.Context)
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)

View file

@ -13,8 +13,8 @@ import (
)
type templateUpdate struct {
Data *[]byte `json:"data"`
Updated *int64 `json:"Updated"`
Data *string `json:"data"`
Namespace *string `json:"namespace"`
}
// HandleUpdate returns an http.HandlerFunc that processes http
@ -22,7 +22,8 @@ type templateUpdate struct {
func HandleUpdate(templateStore core.TemplateStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var (
name = chi.URLParam(r, "name")
name = chi.URLParam(r, "name")
namespace = chi.URLParam(r, "namespace")
)
in := new(templateUpdate)
@ -32,7 +33,7 @@ func HandleUpdate(templateStore core.TemplateStore) http.HandlerFunc {
return
}
s, err := templateStore.FindName(r.Context(), name)
s, err := templateStore.FindName(r.Context(), name, namespace)
if err != nil {
render.NotFound(w, err)
return
@ -41,8 +42,8 @@ func HandleUpdate(templateStore core.TemplateStore) http.HandlerFunc {
if in.Data != nil {
s.Data = *in.Data
}
if in.Updated != nil {
s.Updated = *in.Updated
if in.Namespace != nil {
s.Namespace = *in.Namespace
}
err = s.Validate()

View file

@ -26,11 +26,12 @@ func TestHandleUpdate(t *testing.T) {
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name).Return(dummyTemplate, nil)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(dummyTemplate, nil)
template.EXPECT().Update(gomock.Any(), gomock.Any()).Return(nil)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(dummyTemplate)
@ -52,10 +53,11 @@ func TestHandleUpdate_ValidationErrorData(t *testing.T) {
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name).Return(&core.Template{Name: "my_template"}, nil)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(&core.Template{Name: "my_template"}, nil)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(&core.Secret{Data: ""})
@ -83,10 +85,11 @@ func TestHandleUpdate_TemplateNotFound(t *testing.T) {
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name).Return(nil, errors.ErrNotFound)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(nil, errors.ErrNotFound)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(&core.Secret{})
@ -114,14 +117,15 @@ func TestHandleUpdate_UpdateError(t *testing.T) {
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name).Return(&core.Template{Name: "my_template"}, nil)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(&core.Template{Name: "my_template"}, nil)
template.EXPECT().Update(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(&core.Template{Data: []byte("my_data")})
json.NewEncoder(in).Encode(&core.Template{Data: "my_data"})
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", in)

File diff suppressed because it is too large Load diff

View file

@ -17,7 +17,7 @@ package converter
import (
"context"
"github.com/drone/drone/core"
"github.com/drone/drone/plugin/converter/parser"
"github.com/drone/drone/plugin/converter/starlark"
"strings"
)
@ -48,7 +48,7 @@ func (p *starlarkPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
return nil, nil
}
file, _ := parser.ParseStarlark(req, nil, nil)
file, _ := starlark.Parse(req, nil, nil)
return &core.Config{
Data: *file,
}, nil

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
package starlark
import (
"github.com/drone/drone/core"

View file

@ -1,4 +1,18 @@
package parser
// Copyright 2019 Drone IO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package starlark
import (
"bytes"
@ -38,7 +52,7 @@ var (
ErrCannotLoad = errors.New("starlark: cannot load external scripts")
)
func ParseStarlark(req *core.ConvertArgs, template *core.Template, templateData map[string]interface{}) (file *string, err error) {
func Parse(req *core.ConvertArgs, template *core.Template, templateData map[string]interface{}) (file *string, err error) {
thread := &starlark.Thread{
Name: "drone",
Load: noLoad,
@ -49,13 +63,13 @@ func ParseStarlark(req *core.ConvertArgs, template *core.Template, templateData
}).Traceln(msg)
},
}
var starlarkFile []byte
var starlarkFile string
var starlarkFileName string
if template != nil {
starlarkFile = template.Data
starlarkFileName = template.Name
} else {
starlarkFile = []byte(req.Config.Data)
starlarkFile = req.Config.Data
starlarkFileName = req.Repo.Config
}

View file

@ -1,4 +1,18 @@
package parser
// Copyright 2019 Drone IO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package starlark
import (
"github.com/drone/drone/core"
@ -31,7 +45,7 @@ func TestParseStarlark(t *testing.T) {
}
template := &core.Template{
Name: "my_template.star",
Data: before,
Data: string(before),
}
templateData := map[string]interface{}{
@ -42,7 +56,7 @@ func TestParseStarlark(t *testing.T) {
req.Config.Data = string(before)
parsedFile, err := ParseStarlark(req, template, templateData)
parsedFile, err := Parse(req, template, templateData)
if err != nil {
t.Error(err)
return
@ -80,7 +94,7 @@ func TestParseStarlarkNotTemplateFile(t *testing.T) {
req.Repo.Config = "plugin.starlark.star"
req.Config.Data = string(before)
parsedFile, err := ParseStarlark(req, nil, nil)
parsedFile, err := Parse(req, nil, nil)
if err != nil {
t.Error(err)
return

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
package starlark
import (
"encoding/json"

View file

@ -1,15 +1,31 @@
// Copyright 2019 Drone IO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package converter
import (
"context"
"errors"
"github.com/drone/drone/core"
"github.com/drone/drone/plugin/converter/parser"
"github.com/drone/drone/plugin/converter/starlark"
"gopkg.in/yaml.v2"
"regexp"
"strings"
"gopkg.in/yaml.v2"
)
var (
@ -45,7 +61,7 @@ func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
return nil, ErrTemplateSyntaxErrors
}
// get template from db
template, err := p.templateStore.FindName(ctx, templateArgs.Load)
template, err := p.templateStore.FindName(ctx, templateArgs.Load, req.Repo.Namespace)
if err != nil {
return nil, nil
}
@ -57,7 +73,7 @@ func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
strings.HasSuffix(templateArgs.Load, ".star") ||
strings.HasSuffix(templateArgs.Load, ".starlark") {
file, err := parser.ParseStarlark(req, template, templateArgs.Data)
file, err := starlark.Parse(req, template, templateArgs.Data)
if err != nil {
return nil, err
}

View file

@ -1,3 +1,17 @@
// Copyright 2019 Drone IO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package converter
import (
@ -9,6 +23,26 @@ import (
)
func TestTemplatePluginConvert(t *testing.T) {
templateArgs, err := ioutil.ReadFile("testdata/starlark.template.yml")
if err != nil {
t.Error(err)
return
}
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.yml",
Namespace: "octocat",
},
Config: &core.Config{
Data: string(templateArgs),
},
}
beforeInput, err := ioutil.ReadFile("testdata/starlark.input.star")
if err != nil {
t.Error(err)
@ -21,37 +55,18 @@ func TestTemplatePluginConvert(t *testing.T) {
return
}
templateArgs, err := ioutil.ReadFile("testdata/starlark.template.yml")
if err != nil {
t.Error(err)
return
}
template := &core.Template{
Name: "plugin.starlark",
Data: beforeInput,
Data: string(beforeInput),
}
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().FindName(gomock.Any(), template.Name).Return(template, nil)
templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(template, nil)
plugin := Template(templates)
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.yml",
},
Config: &core.Config{
Data: string(templateArgs),
},
}
config, err := plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
@ -121,35 +136,37 @@ func TestTemplatePluginConvertDroneFileTypePipeline(t *testing.T) {
}
func TestTemplatePluginConvertTemplateNotFound(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
template := &core.Template{
Name: "plugin.starlark",
Data: nil,
}
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().FindName(gomock.Any(), template.Name).Return(nil, nil)
templateArgs, err := ioutil.ReadFile("testdata/starlark.template.yml")
if err != nil {
t.Error(err)
return
}
plugin := Template(templates)
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.yml",
Slug: "octocat/hello-world",
Config: ".drone.yml",
Namespace: "octocat",
},
Config: &core.Config{Data: string(templateArgs)},
}
controller := gomock.NewController(t)
defer controller.Finish()
template := &core.Template{
Name: "plugin.starlark",
Data: "",
}
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(nil, nil)
plugin := Template(templates)
config, err := plugin.Convert(noContext, req)
if config != nil {
t.Errorf("template converter: template name given not found")

18
plugin/converter/testdata/input.jsonnet vendored Normal file
View file

@ -0,0 +1,18 @@
local stepName = std.extVar("input.my_step");
local image = std.extVar("input.my_image");
local commands = std.extVar("input.my_command");
{
"kind": "pipeline",
"type": "docker",
"name": "default",
"steps": [
{
"name": stepName,
"image": image,
"commands": [
commands
]
}
]
}

View file

@ -0,0 +1,6 @@
kind: template
load: plugin.jsonnet
data:
stepName: my_step
image: my_image
commands: my_command

View file

@ -664,13 +664,14 @@ CREATE INDEX ix_latest_repo ON latest (latest_repo_id);
`
//
// 015_create_table_template.sql
// 015_create_table_templates.sql
//
var createTableTemplate = `
CREATE TABLE IF NOT EXISTS template (
CREATE TABLE IF NOT EXISTS templates (
template_id INTEGER PRIMARY KEY AUTO_INCREMENT
,template_name VARCHAR(500)
,template_namespace VARCHAR(50)
,template_data BLOB
,template_created INTEGER
,template_updated INTEGER

View file

@ -1,8 +1,9 @@
-- name: create-table-template
CREATE TABLE IF NOT EXISTS template (
CREATE TABLE IF NOT EXISTS templates (
template_id INTEGER PRIMARY KEY AUTO_INCREMENT
,template_name VARCHAR(500)
,template_namespace VARCHAR(50)
,template_data BLOB
,template_created INTEGER
,template_updated INTEGER

View file

@ -642,13 +642,14 @@ CREATE INDEX IF NOT EXISTS ix_latest_repo ON latest (latest_repo_id);
`
//
// 016_create_template_table.sql
// 016_create_template_tables.sql
//
var createTableTemplate = `
CREATE TABLE IF NOT EXISTS template (
CREATE TABLE IF NOT EXISTS templates (
template_id SERIAL PRIMARY KEY
,template_name TEXT UNIQUE
,template_namespace VARCHAR(50)
,template_data BYTEA
,template_created INTEGER
,template_updated INTEGER

View file

@ -1,8 +1,9 @@
-- name: create-table-template
CREATE TABLE IF NOT EXISTS template (
CREATE TABLE IF NOT EXISTS templates (
template_id SERIAL PRIMARY KEY
,template_name TEXT UNIQUE
,template_namespace VARCHAR(50)
,template_data BYTEA
,template_created INTEGER
,template_updated INTEGER

View file

@ -644,13 +644,14 @@ CREATE INDEX IF NOT EXISTS ix_latest_repo ON latest (latest_repo_id);
`
//
// 015_create_template_table.sql
// 015_create_template_tables.sql
//
var createTableTemplate = `
CREATE TABLE IF NOT EXISTS template (
template_id INTEGER PRIMARY KEY AUTOINCREMENT
CREATE TABLE IF NOT EXISTS templates (
template_id INTEGER PRIMARY KEY AUTOINCREMENT
,template_name TEXT UNIQUE
,template_namespace TEXT COLLATE NOCASE
,template_data BLOB
,template_created INTEGER
,template_updated INTEGER

View file

@ -1,8 +1,9 @@
-- name: create-table-template
CREATE TABLE IF NOT EXISTS template (
CREATE TABLE IF NOT EXISTS templates (
template_id INTEGER PRIMARY KEY AUTOINCREMENT
,template_name TEXT UNIQUE
,template_namespace TEXT COLLATE NOCASE
,template_data BLOB
,template_created INTEGER
,template_updated INTEGER

View file

@ -8,6 +8,7 @@ package template
import (
"database/sql"
"github.com/drone/drone/core"
"github.com/drone/drone/store/shared/db"
)
@ -16,11 +17,12 @@ import (
// of named query parameters.
func toParams(template *core.Template) (map[string]interface{}, error) {
return map[string]interface{}{
"template_id": template.Id,
"template_name": template.Name,
"template_data": template.Data,
"template_created": template.Created,
"template_updated": template.Updated,
"template_id": template.Id,
"template_name": template.Name,
"template_namespace": template.Namespace,
"template_data": template.Data,
"template_created": template.Created,
"template_updated": template.Updated,
}, nil
}
@ -30,6 +32,7 @@ func scanRow(scanner db.Scanner, dst *core.Template) error {
err := scanner.Scan(
&dst.Id,
&dst.Name,
&dst.Namespace,
&dst.Data,
&dst.Created,
&dst.Updated,

View file

@ -23,6 +23,24 @@ type templateStore struct {
db *db.DB
}
func (s *templateStore) List(ctx context.Context, namespace string) ([]*core.Template, error) {
var out []*core.Template
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
params := map[string]interface{}{"template_namespace": namespace}
stmt, args, err := binder.BindNamed(queryNamespace, params)
if err != nil {
return err
}
rows, err := queryer.Query(stmt, args...)
if err != nil {
return err
}
out, err = scanRows(rows)
return err
})
return out, err
}
func (s *templateStore) ListAll(ctx context.Context) ([]*core.Template, error) {
var out []*core.Template
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
@ -58,14 +76,16 @@ func (s *templateStore) Find(ctx context.Context, id int64) (*core.Template, err
return out, err
}
func (s *templateStore) FindName(ctx context.Context, name string) (*core.Template, error) {
out := &core.Template{Name: name}
func (s *templateStore) FindName(ctx context.Context, name string, namespace string) (*core.Template, error) {
out := &core.Template{Name: name, Namespace: namespace}
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
params, err := toParams(out)
if err != nil {
return err
}
query, args, err := binder.BindNamed(queryName, params)
if err != nil {
return err
}
@ -146,7 +166,7 @@ func (s *templateStore) Delete(ctx context.Context, template *core.Template) err
}
const queryKey = queryBase + `
FROM template
FROM templates
WHERE template_id = :template_id
LIMIT 1
`
@ -155,24 +175,33 @@ const queryBase = `
SELECT
template_id
,template_name
,template_namespace
,template_data
,template_created
,template_updated
`
const queryAll = queryBase + `
FROM template
FROM templates
ORDER BY template_name
`
const queryNamespace = queryBase + `
FROM templates
WHERE template_namespace = :template_namespace
ORDER BY template_name
`
const stmtInsert = `
INSERT INTO template (
INSERT INTO templates (
template_name
,template_namespace
,template_data
,template_created
,template_updated
) VALUES (
:template_name
,:template_namespace
,:template_data
,:template_created
,:template_updated
@ -180,20 +209,22 @@ INSERT INTO template (
`
const stmtUpdate = `
UPDATE template SET
UPDATE templates SET
template_name = :template_name
,template_namespace = :template_namespace
,template_data = :template_data
,template_updated = :template_updated
WHERE template_id = :template_id
`
const stmtDelete = `
DELETE FROM template
DELETE FROM templates
WHERE template_id = :template_id
`
const queryName = queryBase + `
FROM template
FROM templates
WHERE template_name = :template_name
AND template_namespace = :template_namespace
LIMIT 1
`

View file

@ -7,7 +7,6 @@
package template
import (
"bytes"
"context"
"database/sql"
"github.com/drone/drone/core"
@ -35,11 +34,12 @@ func TestTemplate(t *testing.T) {
func testTemplateCreate(store *templateStore) func(t *testing.T) {
return func(t *testing.T) {
item := &core.Template{
Id: 1,
Name: "my_template",
Data: []byte("some_template_data"),
Created: 1,
Updated: 2,
Id: 1,
Name: "my_template",
Namespace: "my_org",
Data: "some_template_data",
Created: 1,
Updated: 2,
}
err := store.Create(noContext, item)
if err != nil {
@ -52,6 +52,7 @@ func testTemplateCreate(store *templateStore) func(t *testing.T) {
t.Run("Find", testTemplateFind(store, item))
t.Run("FindName", testTemplateFindName(store))
t.Run("ListAll", testTemplateListAll(store))
t.Run("List", testTemplateList(store))
t.Run("Update", testTemplateUpdate(store))
t.Run("Delete", testTemplateDelete(store))
}
@ -70,7 +71,7 @@ func testTemplateFind(store *templateStore, template *core.Template) func(t *tes
func testTemplateFindName(store *templateStore) func(t *testing.T) {
return func(t *testing.T) {
item, err := store.FindName(noContext, "my_template")
item, err := store.FindName(noContext, "my_template", "my_org")
if err != nil {
t.Error(err)
} else {
@ -84,9 +85,12 @@ func testTemplate(item *core.Template) func(t *testing.T) {
if got, want := item.Name, "my_template"; got != want {
t.Errorf("Want template name %q, got %q", want, got)
}
if got, want := item.Data, []byte("some_template_data"); bytes.Compare(got, want) != 0 {
if got, want := item.Data, "some_template_data"; got != want {
t.Errorf("Want template data %q, got %q", want, got)
}
if got, want := item.Namespace, "my_org"; got != want {
t.Errorf("Want template org %q, got %q", want, got)
}
}
}
@ -105,9 +109,24 @@ func testTemplateListAll(store *templateStore) func(t *testing.T) {
}
}
func testTemplateList(store *templateStore) func(t *testing.T) {
return func(t *testing.T) {
list, err := store.List(noContext, "my_org")
if err != nil {
t.Error(err)
return
}
if got, want := len(list), 1; got != want {
t.Errorf("Want count %d, got %d", want, got)
} else {
t.Run("Fields", testTemplate(list[0]))
}
}
}
func testTemplateUpdate(store *templateStore) func(t *testing.T) {
return func(t *testing.T) {
before, err := store.FindName(noContext, "my_template")
before, err := store.FindName(noContext, "my_template", "my_org")
if err != nil {
t.Error(err)
return
@ -130,7 +149,7 @@ func testTemplateUpdate(store *templateStore) func(t *testing.T) {
func testTemplateDelete(store *templateStore) func(t *testing.T) {
return func(t *testing.T) {
secret, err := store.FindName(noContext, "my_template")
secret, err := store.FindName(noContext, "my_template", "my_org")
if err != nil {
t.Error(err)
return