Skip to content
This repository has been archived by the owner on Oct 9, 2023. It is now read-only.

Add UpdateProject functionality to Flyte control plane #114

Merged
merged 41 commits into from
Aug 19, 2020
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8f8f7e6
introduce pr to update project repo
kosigz Aug 13, 2020
0a3c9ef
wip
kosigz Aug 13, 2020
49d2159
wip
kosigz Aug 13, 2020
b82d9a4
wip + merge
kosigz Aug 13, 2020
6d72fc8
wip + compiles woot woot
kosigz Aug 13, 2020
206b18a
don't return types where unused; db write
kosigz Aug 14, 2020
7ebe9d6
check for writeTx error
kosigz Aug 14, 2020
6e1c679
interface
kosigz Aug 17, 2020
b992261
use get
kosigz Aug 17, 2020
a68c819
add first integration test
kosigz Aug 17, 2020
e3c1c72
cmts
kosigz Aug 17, 2020
c3667ad
fix
kosigz Aug 17, 2020
1c612b2
new integration test
kosigz Aug 17, 2020
bae3c4e
ci
kosigz Aug 17, 2020
85451db
apologies for all the commits; running CI off GitHub
kosigz Aug 17, 2020
5f34f8e
nit
kosigz Aug 17, 2020
34738e6
api update
kosigz Aug 17, 2020
7a8bf70
update mock interface
kosigz Aug 17, 2020
2953c95
wip
kosigz Aug 17, 2020
7fd36bd
mock update
kosigz Aug 17, 2020
1c56881
lol
kosigz Aug 17, 2020
20abeb6
bump back some dependencies
kosigz Aug 18, 2020
a319885
dep
kosigz Aug 18, 2020
a281fb6
unit test and migration
kosigz Aug 18, 2020
11daaaf
rebase
kosigz Aug 18, 2020
430a88f
protobuf
kosigz Aug 18, 2020
c2f6bc2
should be last dep change
kosigz Aug 18, 2020
301c7c3
revert go.mode and make compile
kosigz Aug 18, 2020
33e77d3
mock out more things, fix white space
kosigz Aug 18, 2020
a12d9fb
wip
kosigz Aug 18, 2020
5d7775a
wip
kosigz Aug 18, 2020
b8f87ab
gofmt
kosigz Aug 18, 2020
d466c6e
project
kosigz Aug 18, 2020
56a5355
Update pkg/repositories/gormimpl/project_repo.go
kosigz-lyft Aug 18, 2020
bc41a8f
Update pkg/repositories/gormimpl/project_repo.go
kosigz-lyft Aug 18, 2020
01fb1f7
cmnts
kosigz Aug 18, 2020
b2925ef
Merge branch 'update_project_repo' of github.com:lyft/flyteadmin into…
kosigz Aug 18, 2020
b1d7d96
truncate tables before tests
kosigz Aug 18, 2020
dd7e10d
update integration test
kosigz Aug 19, 2020
37b3805
integration test
kosigz Aug 19, 2020
2fcc285
:=
kosigz Aug 19, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .Makefile.swp
Binary file not shown.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ require (
github.com/jinzhu/gorm v1.9.12
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/lib/pq v1.3.0
github.com/lyft/flyteidl v0.18.2
github.com/lyft/flyteidl v0.18.3
github.com/lyft/flytepropeller v0.3.7
github.com/lyft/flytestdlib v0.3.9
github.com/magiconair/properties v1.8.1
Expand All @@ -33,6 +33,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.6.1
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/tools v0.0.0-20200818005847-188abfa75333 // indirect
google.golang.org/grpc v1.28.0
gopkg.in/gormigrate.v1 v1.6.0
k8s.io/api v0.17.3
Expand Down
274 changes: 274 additions & 0 deletions go.sum

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions pkg/manager/impl/project_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,27 @@ func (m *ProjectManager) ListProjects(ctx context.Context, request admin.Project
}, nil
}

func (m *ProjectManager) UpdateProject(ctx context.Context, projectUpdate admin.Project) (*admin.ProjectUpdateResponse, error) {
var response *admin.ProjectUpdateResponse
projectRepo := m.db.ProjectRepo()

// Fetch the existing project if exists. If not, return err and do not update.
_, err := projectRepo.Get(ctx, projectUpdate.Id)
if err != nil {
return nil, err
}

// Transform the provided project into a model and apply to the DB.
projectUpdateModel := transformers.CreateProjectModel(&projectUpdate)
err = projectRepo.UpdateProject(ctx, projectUpdateModel)

if err != nil {
return nil, err
}

return response, nil
}

func NewProjectManager(db repositories.RepositoryInterface, config runtimeInterfaces.Configuration) interfaces.ProjectInterface {
return &ProjectManager{
db: db,
Expand Down
1 change: 1 addition & 0 deletions pkg/manager/interfaces/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ import (
type ProjectInterface interface {
CreateProject(ctx context.Context, request admin.ProjectRegisterRequest) (*admin.ProjectRegisterResponse, error)
ListProjects(ctx context.Context, request admin.ProjectListRequest) (*admin.Projects, error)
UpdateProject(ctx context.Context, request admin.Project) (*admin.ProjectUpdateResponse, error)
}
12 changes: 10 additions & 2 deletions pkg/manager/mocks/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,32 @@ import (

type CreateProjectFunc func(ctx context.Context, request admin.ProjectRegisterRequest) (*admin.ProjectRegisterResponse, error)
type ListProjectFunc func(ctx context.Context, request admin.ProjectListRequest) (*admin.Projects, error)
type UpdateProjectFunc func(ctx context.Context, request admin.Project) (*admin.ProjectUpdateResponse, error)

type MockProjectManager struct {
listProjectFunc ListProjectFunc
createProjectFunc CreateProjectFunc
updateProjectFunc UpdateProjectFunc
}

func (m *MockProjectManager) SetCreateProject(createProjectFunc CreateProjectFunc) {
m.createProjectFunc = createProjectFunc
}

func (m *MockProjectManager) CreateProject(ctx context.Context, request admin.ProjectRegisterRequest) (
*admin.ProjectRegisterResponse, error) {
func (m *MockProjectManager) CreateProject(ctx context.Context, request admin.ProjectRegisterRequest) (*admin.ProjectRegisterResponse, error) {
if m.createProjectFunc != nil {
return m.createProjectFunc(ctx, request)
}
return nil, nil
}

func (m *MockProjectManager) UpdateProject(ctx context.Context, request admin.Project) (*admin.ProjectUpdateResponse, error) {
if m.updateProjectFunc != nil {
return m.updateProjectFunc(ctx, request)
}
return nil, nil
}

func (m *MockProjectManager) SetListCallback(listProjectFunc ListProjectFunc) {
m.listProjectFunc = listProjectFunc
}
Expand Down
9 changes: 9 additions & 0 deletions pkg/repositories/config/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,13 @@ var Migrations = []*gormigrate.Migration{
return tx.Model(&models.NodeExecution{}).DropColumn("parent_id").DropColumn("node_execution_metadata").Error
},
},
{
ID: "2020-08-17-labels-addition",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&models.NodeExecution{}).Error
kosigz-lyft marked this conversation as resolved.
Show resolved Hide resolved
},
Rollback: func(tx *gorm.DB) error {
return tx.Model(&models.NodeExecution{}).DropColumn("labels").Error
kosigz-lyft marked this conversation as resolved.
Show resolved Hide resolved
},
},
}
12 changes: 12 additions & 0 deletions pkg/repositories/gormimpl/project_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,15 @@ func NewProjectRepo(db *gorm.DB, errorTransformer errors.ErrorTransformer,
metrics: metrics,
}
}

func (r *ProjectRepo) UpdateProject(ctx context.Context, projectUpdate models.Project) error {
// Use gorm client to update the two fields that are changed.
writeTx := r.db.Model(&projectUpdate).Update("Description", "Labels")
kosigz-lyft marked this conversation as resolved.
Show resolved Hide resolved

// Return error if applies.
if writeTx != nil {
kosigz-lyft marked this conversation as resolved.
Show resolved Hide resolved
_ = r.errorTransformer.ToFlyteAdminError(writeTx.Error)
kosigz-lyft marked this conversation as resolved.
Show resolved Hide resolved
}

return nil
}
2 changes: 1 addition & 1 deletion pkg/repositories/gormimpl/project_repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestCreateProject(t *testing.T) {

query := GlobalMock.NewMock()
query.WithQuery(
`INSERT INTO "projects" ("created_at","updated_at","deleted_at","identifier","name","description") VALUES (?,?,?,?,?,?)`)
`INSERT INTO "projects" ("created_at","updated_at","deleted_at","identifier","name","description","labels") VALUES (?,?,?,?,?,?,?)`)

err := projectRepo.Create(context.Background(), models.Project{
Identifier: "proj",
Expand Down
4 changes: 4 additions & 0 deletions pkg/repositories/interfaces/project_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ type ProjectRepoInterface interface {
Get(ctx context.Context, projectID string) (models.Project, error)
// Lists unique projects registered as namespaces
ListAll(ctx context.Context, sortParameter common.SortParameter) ([]models.Project, error)
// Given a project that exists in the DB and a partial set of fields to update
// as a second project (projectUpdate), updates the original project which already
// exists in the DB.
UpdateProject(ctx context.Context, projectUpdate models.Project) error
}
15 changes: 12 additions & 3 deletions pkg/repositories/mocks/project_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import (
type CreateProjectFunction func(ctx context.Context, project models.Project) error
type GetProjectFunction func(ctx context.Context, projectID string) (models.Project, error)
type ListProjectsFunction func(ctx context.Context, sortParameter common.SortParameter) ([]models.Project, error)
type UpdateProjectFunction func(ctx context.Context, projectUpdate models.Project) error

type MockProjectRepo struct {
CreateFunction CreateProjectFunction
GetFunction GetProjectFunction
ListProjectsFunction ListProjectsFunction
CreateFunction CreateProjectFunction
GetFunction GetProjectFunction
ListProjectsFunction ListProjectsFunction
UpdateProjectFunction UpdateProjectFunction
}

func (r *MockProjectRepo) Create(ctx context.Context, project models.Project) error {
Expand All @@ -39,6 +41,13 @@ func (r *MockProjectRepo) ListAll(ctx context.Context, sortParameter common.Sort
return make([]models.Project, 0), nil
}

func (r *MockProjectRepo) UpdateProject(ctx context.Context, projectUpdate models.Project) error {
if r.UpdateProjectFunction != nil {
return r.UpdateProjectFunction(ctx, projectUpdate)
}
return nil
}

func NewMockProjectRepo() interfaces.ProjectRepoInterface {
return &MockProjectRepo{}
}
1 change: 1 addition & 0 deletions pkg/repositories/models/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ type Project struct {
Identifier string `gorm:"primary_key"`
Name string // Human-readable name, not a unique identifier.
Description string `gorm:"type:varchar(300)"`
Labels []byte
}
12 changes: 12 additions & 0 deletions pkg/repositories/transformers/project.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package transformers

import (
"github.com/golang/protobuf/proto"
"github.com/lyft/flyteadmin/pkg/repositories/models"
"github.com/lyft/flyteidl/gen/pb-go/flyteidl/admin"
)
Expand All @@ -12,18 +13,29 @@ type CreateProjectModelInput struct {
}

func CreateProjectModel(project *admin.Project) models.Project {
projectBytes, err := proto.Marshal(project)
if err != nil {
return models.Project{}
}
return models.Project{
Identifier: project.Id,
Name: project.Name,
Description: project.Description,
Labels: projectBytes,
}
}

func FromProjectModel(projectModel models.Project, domains []*admin.Domain) admin.Project {
projectDeserialized := &admin.Project{}
err := proto.Unmarshal(projectModel.Labels, projectDeserialized)
if err != nil {
return admin.Project{}
}
project := admin.Project{
Id: projectModel.Identifier,
Name: projectModel.Name,
Description: projectModel.Description,
Labels: projectDeserialized.Labels,
}
project.Domains = domains
return project
Expand Down
5 changes: 5 additions & 0 deletions pkg/repositories/transformers/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ func TestCreateProjectModel(t *testing.T) {
Identifier: "project_id",
Name: "project_name",
Description: "project_description",
Labels: []uint8{
0xa, 0xa, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x12, 0xc, 0x70, 0x72, 0x6f,
0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x13, 0x70, 0x72, 0x6f, 0x6a, 0x65,
0x63, 0x74, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
},
}, projectModel)
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/rpc/adminservice/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type projectEndpointMetrics struct {

register util.RequestMetrics
list util.RequestMetrics
update util.RequestMetrics
}

type attributeEndpointMetrics struct {
Expand Down Expand Up @@ -154,6 +155,7 @@ func InitMetrics(adminScope promutils.Scope) AdminMetrics {
scope: adminScope,
register: util.NewRequestMetrics(adminScope, "register_project"),
list: util.NewRequestMetrics(adminScope, "list_projects"),
update: util.NewRequestMetrics(adminScope, "update_project"),
},
projectAttributesEndpointMetrics: attributeEndpointMetrics{
scope: adminScope,
Expand Down
27 changes: 27 additions & 0 deletions pkg/rpc/adminservice/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,30 @@ func (m *AdminService) ListProjects(ctx context.Context, request *admin.ProjectL
m.Metrics.projectEndpointMetrics.list.Success()
return response, nil
}

func (m *AdminService) UpdateProject(ctx context.Context, request *admin.Project) (
*admin.ProjectUpdateResponse, error) {
defer m.interceptPanic(ctx, request)
requestedAt := time.Now()
if request == nil {
return nil, status.Errorf(codes.InvalidArgument, "Incorrect request, nil requests not allowed")
}
var response *admin.ProjectUpdateResponse
var err error
m.Metrics.projectEndpointMetrics.register.Time(func() {
response, err = m.ProjectManager.UpdateProject(ctx, *request)
})
audit.NewLogBuilder().WithAuthenticatedCtx(ctx).WithRequest(
"UpdateProject",
map[string]string{
audit.Project: request.Id,
},
audit.ReadWrite,
requestedAt,
).WithResponse(time.Now(), err).Log(ctx)
if err != nil {
return nil, util.TransformAndRecordError(err, &m.Metrics.projectEndpointMetrics.update)
}

return response, nil
}
105 changes: 105 additions & 0 deletions tests/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,108 @@ func TestCreateProject(t *testing.T) {
}
assert.True(t, sawNewProject)
}

func TestUpdateProjectDescription(t *testing.T) {
Copy link
Contributor

@katrogan katrogan Aug 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for adding both integration tests! 😄

kosigz-lyft marked this conversation as resolved.
Show resolved Hide resolved
ctx := context.Background()
client, conn := GetTestAdminServiceClient()
defer conn.Close()

// Create a new project.
req := admin.ProjectRegisterRequest{
Project: &admin.Project{
Id: "potato",
Name: "spud",
kosigz-lyft marked this conversation as resolved.
Show resolved Hide resolved
Labels: &admin.Labels{
Values: map[string]string{
"foo": "bar",
"bar": "baz",
},
},
},
}
_, err := client.RegisterProject(ctx, &req)
assert.Nil(t, err)

// Verify the project has been registered.
projects, err := client.ListProjects(ctx, &admin.ProjectListRequest{})
assert.Nil(t, err)
assert.NotEmpty(t, projects.Projects)

// Attempt to modify the name of the Project. Modifying the Name should be a
// no-op, while the Description is modified. Labels should be a no-op.
_, err = client.UpdateProject(ctx, &admin.Project{
Id: "potato",
Name: "foobar",
Description: "a-new-description",
Labels: &admin.Labels{},
kosigz-lyft marked this conversation as resolved.
Show resolved Hide resolved
})

// Fetch updated projects.
projectsUpdated, err := client.ListProjects(ctx, &admin.ProjectListRequest{})
assert.Nil(t, err)
assert.NotEmpty(t, projectsUpdated.Projects)

// Verify that the project's Name has not been modified but the Description has.
updatedProject := projectsUpdated.Projects[0]
assert.Equal(t, updatedProject.Id, "potato")
assert.Equal(t, updatedProject.Name, "spud") // unchanged
assert.Equal(t, updatedProject.Description, "a-new-description") // changed

// Verify that project labels are not removed.
labelsMap := updatedProject.Labels
fooVal, fooExists := labelsMap.Values["foo"]
barVal, barExists := labelsMap.Values["bar"]
assert.Equal(t, fooExists, true)
assert.Equal(t, fooVal, "bar")
assert.Equal(t, barExists, true)
assert.Equal(t, barVal, "baz")
}

func TestUpdateProjectLabels(t *testing.T) {
ctx := context.Background()
client, conn := GetTestAdminServiceClient()
defer conn.Close()

// Create a new project.
req := admin.ProjectRegisterRequest{
Project: &admin.Project{
Id: "potato",
Name: "spud",
},
}
_, err := client.RegisterProject(ctx, &req)
assert.Nil(t, err)

// Verify the project has been registered.
projects, err := client.ListProjects(ctx, &admin.ProjectListRequest{})
assert.Nil(t, err)
assert.NotEmpty(t, projects.Projects)

// Attempt to modify the name of the Project. Modifying the Name should be a
// no-op, while the Labels are modified.
_, err = client.UpdateProject(ctx, &admin.Project{
Id: "potato",
Name: "foobar",
Labels: &admin.Labels{
Values: map[string]string{
"foo": "bar",
"bar": "baz",
},
},
})

// Fetch updated projects.
projectsUpdated, err := client.ListProjects(ctx, &admin.ProjectListRequest{})
assert.Nil(t, err)
assert.NotEmpty(t, projectsUpdated.Projects)

// Verify that the expected labels have been added to the project.
updatedProject := projectsUpdated.Projects[0]
labelsMap := updatedProject.Labels
fooVal, fooExists := labelsMap.Values["foo"]
barVal, barExists := labelsMap.Values["bar"]
assert.Equal(t, fooExists, true)
assert.Equal(t, fooVal, "bar")
assert.Equal(t, barExists, true)
assert.Equal(t, barVal, "baz")
}