Skip to content
This repository has been archived by the owner on Jan 8, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4819 from hashicorp/WAYP-1457
Browse files Browse the repository at this point in the history
Automatically register apps when project has server-stored waypoint.hcl
  • Loading branch information
izaaklauer committed Jun 23, 2023
2 parents aca518e + 75faea0 commit 076667d
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .changelog/4819.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
server: Perform project init without requiring a runner if waypoint.hcl is stored serverside
```
64 changes: 63 additions & 1 deletion pkg/server/singleprocess/service_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ package singleprocess

import (
"context"
"fmt"

"github.com/hashicorp/go-hclog"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"

empty "google.golang.org/protobuf/types/known/emptypb"

Expand Down Expand Up @@ -34,7 +37,13 @@ func (s *Service) UpsertProject(
)
}

if projectNeedsRemoteInit(result) {
if hasUsableWaypointHCL(result) {
proj, err := s.serverSideProjectInit(ctx, result)
if err != nil {
return nil, err // already externalized
}
result = proj
} else if projectNeedsRemoteInit(result) {
// The project is connected to a data source but doesn’t use
// automatic polling, so let’s queue some remote init operations
// to ensure the application list is populated.
Expand Down Expand Up @@ -266,3 +275,56 @@ func projectNeedsRemoteInit(project *pb.Project) bool {

return true
}

// hasUsableWaypointHCL verifies that a project has waypoint.hcl contents
// of type HCL
func hasUsableWaypointHCL(project *pb.Project) bool {
return len(project.WaypointHcl) > 0 && project.WaypointHclFormat == pb.Hcl_HCL
}

// serverSideProjectInit initializes a project that directly contains a waypoint.hcl.
// "init" currently consists of getting the list of apps on a project, and upserting each one.
// If the waypoint.hcl is not on the project but is in VCS, you must enqueue an init job
// rather than attempting a serverside init.
// Returns externalized errors
func (s *Service) serverSideProjectInit(ctx context.Context, project *pb.Project) (*pb.Project, error) {
if !hasUsableWaypointHCL(project) {
return nil, fmt.Errorf("cannot init a project without a stored waypoint.hcl with hcl type contents serverside")
}
file, _ := hclsyntax.ParseConfig(project.WaypointHcl, "<waypoint-hcl>", hcl.Pos{})
content, _ := file.Body.Content(&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{Type: "app", LabelNames: []string{"name"}},
},
})
projRef := &pb.Ref_Project{Project: project.Name}

for _, b := range content.Blocks.ByType()["app"] {
name := b.Labels[0]
_, err := s.state(ctx).AppPut(ctx, &pb.Application{
Project: projRef,
Name: name,
})
if err != nil {
return nil, hcerr.Externalize(
hclog.FromContext(ctx),
err,
"failed to register app %q while creating project %q",
name, project.GetName(),
)
}
}

// Reload the project to populate the newly-added apps
result, err := s.state(ctx).ProjectGet(ctx, projRef)
if err != nil {
return nil, hcerr.Externalize(
hclog.FromContext(ctx),
err,
"failed to reload project %q",
project.GetName(),
)
}

return result, nil
}
51 changes: 51 additions & 0 deletions pkg/serverhandler/handlertest/test_service_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func init() {
TestServiceProject_GetApplication,
TestServiceProject_UpsertApplication,
TestServiceProject_InvalidName,
TestServiceProject_AutoPopulateApps,
}
}

Expand Down Expand Up @@ -275,3 +276,53 @@ func TestServiceProject_InvalidName(t *testing.T, factory Factory) {
})
require.Error(err)
}

func TestServiceProject_AutoPopulateApps(t *testing.T, factory Factory) {
ctx := context.Background()
require := require.New(t)
client, _ := factory(t)

project := ptypes.TestProject(t, &pb.Project{
WaypointHcl: []byte(`
project = "test"
variable "vartest" {
type = string
default = ""
}
app "website" {
build {
use "docker" {}
}
deploy {
use "kubernetes" {}
}
release {
use "kubernetes" {}
}
}
app "api" {
build {
use "docker" {}
}
deploy {
use "kubernetes" {}
}
release {
use "kubernetes" {}
}
}
`),
})

resp, err := client.UpsertProject(ctx, &pb.UpsertProjectRequest{
Project: project,
})
require.NoError(err)
require.NotNil(resp)
require.Len(resp.Project.Applications, 2)
require.Equal("website", resp.Project.Applications[0].Name)
require.Equal("api", resp.Project.Applications[1].Name)
}

0 comments on commit 076667d

Please sign in to comment.