Skip to content

Commit

Permalink
feat(deploy): Add .rillcloud/project.yaml to store deployed info (#5146)
Browse files Browse the repository at this point in the history
* Add dotrillcloud

* Only add project_id

* Swap around proto order

* PR comments

* Remove rillcloud info on delete

* Update InferProjectName to look at rillcloud file

* json=>yaml

* Add a prefix if env is not prod

* Fix lint

* Safe delete rillcloud file

* Move deployedProjectID before github access check

* PR comments
  • Loading branch information
AdityaHegde authored Jul 2, 2024
1 parent 4f4c492 commit fe464ef
Show file tree
Hide file tree
Showing 22 changed files with 4,746 additions and 3,831 deletions.
30 changes: 29 additions & 1 deletion admin/server/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,32 @@ func (s *Server) GetProject(ctx context.Context, req *adminv1.GetProjectRequest)
}, nil
}

func (s *Server) GetProjectByID(ctx context.Context, req *adminv1.GetProjectByIDRequest) (*adminv1.GetProjectByIDResponse, error) {
observability.AddRequestAttributes(ctx,
attribute.String("args.project_id", req.Id),
)

proj, err := s.admin.DB.FindProject(ctx, req.Id)
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

org, err := s.admin.DB.FindOrganization(ctx, proj.OrganizationID)
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

claims := auth.GetClaims(ctx)
permissions := claims.ProjectPermissions(ctx, proj.OrganizationID, proj.ID)
if !permissions.ReadProject && !proj.Public && !claims.Superuser(ctx) {
return nil, status.Error(codes.PermissionDenied, "does not have permission to read project")
}

return &adminv1.GetProjectByIDResponse{
Project: s.projToDTO(proj, org.Name),
}, nil
}

func (s *Server) SearchProjectNames(ctx context.Context, req *adminv1.SearchProjectNamesRequest) (*adminv1.SearchProjectNamesResponse, error) {
observability.AddRequestAttributes(ctx,
attribute.String("args.pattern", req.NamePattern),
Expand Down Expand Up @@ -401,7 +427,9 @@ func (s *Server) DeleteProject(ctx context.Context, req *adminv1.DeleteProjectRe
return nil, status.Error(codes.InvalidArgument, err.Error())
}

return &adminv1.DeleteProjectResponse{}, nil
return &adminv1.DeleteProjectResponse{
Id: proj.ID,
}, nil
}

func (s *Server) UpdateProject(ctx context.Context, req *adminv1.UpdateProjectRequest) (*adminv1.UpdateProjectResponse, error) {
Expand Down
17 changes: 17 additions & 0 deletions cli/cmd/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/rilldata/rill/cli/pkg/cmdutil"
"github.com/rilldata/rill/cli/pkg/deviceauth"
"github.com/rilldata/rill/cli/pkg/dotrill"
"github.com/rilldata/rill/cli/pkg/dotrillcloud"
"github.com/rilldata/rill/cli/pkg/gitutil"
"github.com/rilldata/rill/cli/pkg/printer"
adminv1 "github.com/rilldata/rill/proto/gen/rill/admin/v1"
Expand Down Expand Up @@ -293,6 +294,15 @@ func DeployFlow(ctx context.Context, ch *cmdutil.Helper, opts *Options) error {
return fmt.Errorf("create project failed with error %w", err)
}

if localProjectPath != "" {
err = dotrillcloud.SetAll(localProjectPath, ch.AdminURL, &dotrillcloud.Config{
ProjectID: res.Project.Id,
})
if err != nil {
return err
}
}

// Success!
ch.PrintfSuccess("Created project \"%s/%s\". Use `rill project rename` to change name if required.\n\n", ch.Org, res.Project.Name)
ch.PrintfSuccess("Rill projects deploy continuously when you push changes to Github.\n")
Expand Down Expand Up @@ -426,6 +436,13 @@ func deployWithUploadFlow(ctx context.Context, ch *cmdutil.Helper, opts *Options
return fmt.Errorf("create project failed with error %w", err)
}

err = dotrillcloud.SetAll(localProjectPath, ch.AdminURL, &dotrillcloud.Config{
ProjectID: res.Project.Id,
})
if err != nil {
return err
}

// Success!
ch.PrintfSuccess("Created project \"%s/%s\". Use `rill project rename` to change name if required.\n\n", ch.Org, res.Project.Name)

Expand Down
35 changes: 6 additions & 29 deletions cli/cmd/devtool/switch-env.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

"github.com/rilldata/rill/cli/cmd/auth"
"github.com/rilldata/rill/cli/pkg/adminenv"
"github.com/rilldata/rill/cli/pkg/cmdutil"
"github.com/rilldata/rill/cli/pkg/dotrill"
adminv1 "github.com/rilldata/rill/proto/gen/rill/admin/v1"
Expand All @@ -28,7 +29,7 @@ func SwitchEnvCmd(ch *cmdutil.Helper) *cobra.Command {
return fmt.Errorf("can't switch environment when assuming another user (run `rill sudo user unassume` and try again)")
}

fromEnv, err := inferEnv(ch)
fromEnv, err := adminenv.Infer(ch.AdminURL)
if err != nil {
return err
}
Expand All @@ -37,7 +38,7 @@ func SwitchEnvCmd(ch *cmdutil.Helper) *cobra.Command {
if len(args) > 0 {
toEnv = args[0]
} else {
toEnv, err = cmdutil.SelectPrompt("Select environment", maps.Keys(envURLs), fromEnv)
toEnv, err = cmdutil.SelectPrompt("Select environment", maps.Keys(adminenv.EnvURLs), fromEnv)
if err != nil {
return err
}
Expand All @@ -48,7 +49,7 @@ func SwitchEnvCmd(ch *cmdutil.Helper) *cobra.Command {
return err
}

ch.PrintfSuccess("Set default env to %q (%q)\n", toEnv, adminURLForEnv(toEnv))
ch.PrintfSuccess("Set default env to %q (%q)\n", toEnv, adminenv.AdminURL(toEnv))

return auth.SelectOrgFlow(cmd.Context(), ch, true)
},
Expand All @@ -57,30 +58,6 @@ func SwitchEnvCmd(ch *cmdutil.Helper) *cobra.Command {
return cmd
}

var envURLs = map[string]string{
"prod": "https://admin.rilldata.com",
"stage": "https://admin.rilldata.io",
"test": "https://admin.rilldata.in",
"dev": "http://localhost:9090",
}

func inferEnv(ch *cmdutil.Helper) (string, error) {
for env, url := range envURLs {
if url == ch.AdminURL {
return env, nil
}
}
return "", fmt.Errorf("could not infer env from admin URL %q", ch.AdminURL)
}

func adminURLForEnv(env string) string {
u, ok := envURLs[env]
if !ok {
panic(fmt.Errorf("invalid environment %q", env))
}
return u
}

func switchEnv(ch *cmdutil.Helper, fromEnv, toEnv string) error {
token, err := dotrill.GetAccessToken()
if err != nil {
Expand All @@ -103,7 +80,7 @@ func switchEnv(ch *cmdutil.Helper, fromEnv, toEnv string) error {
}
ch.AdminTokenDefault = toToken // Also set the cfg's token to the one we just got

toURL := adminURLForEnv(toEnv)
toURL := adminenv.AdminURL(toEnv)
err = dotrill.SetDefaultAdminURL(toURL)
if err != nil {
return err
Expand All @@ -116,7 +93,7 @@ func switchEnv(ch *cmdutil.Helper, fromEnv, toEnv string) error {
// switchEnvToDevTemporarily switches the CLI to the "dev" environment (if not already there),
// and then switches it back and returns when the context is cancelled.
func switchEnvToDevTemporarily(ctx context.Context, ch *cmdutil.Helper) {
env, err := inferEnv(ch)
env, err := adminenv.Infer(ch.AdminURL)
if err != nil {
logWarn.Printf("Did not switch CLI to dev environment: failed to infer environment (error: %v)\n", err)
return
Expand Down
19 changes: 18 additions & 1 deletion cli/cmd/project/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

"github.com/rilldata/rill/cli/pkg/cmdutil"
"github.com/rilldata/rill/cli/pkg/dotrillcloud"
adminv1 "github.com/rilldata/rill/proto/gen/rill/admin/v1"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -51,14 +52,30 @@ func DeleteCmd(ch *cmdutil.Helper) *cobra.Command {
}
}

_, err = client.DeleteProject(cmd.Context(), &adminv1.DeleteProjectRequest{
delResp, err := client.DeleteProject(cmd.Context(), &adminv1.DeleteProjectRequest{
OrganizationName: ch.Org,
Name: name,
})
if err != nil {
return err
}

var deployedID string
rc, err := dotrillcloud.GetAll(path, ch.AdminURL)
if err != nil {
return err
}
if rc != nil {
deployedID = rc.ProjectID
}

if delResp.Id == deployedID {
err = dotrillcloud.Delete(path, ch.AdminURL)
if err != nil {
return err
}
}

ch.PrintfSuccess("Deleted project: %v\n", name)
return nil
},
Expand Down
29 changes: 29 additions & 0 deletions cli/pkg/adminenv/admin_env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package adminenv

import (
"fmt"
)

var EnvURLs = map[string]string{
"prod": "https://admin.rilldata.com",
"stage": "https://admin.rilldata.io",
"test": "https://admin.rilldata.in",
"dev": "http://localhost:9090",
}

func Infer(adminURL string) (string, error) {
for env, url := range EnvURLs {
if url == adminURL {
return env, nil
}
}
return "", fmt.Errorf("could not infer env from admin URL %q", adminURL)
}

func AdminURL(env string) string {
u, ok := EnvURLs[env]
if !ok {
panic(fmt.Errorf("invalid environment %q", env))
}
return u
}
20 changes: 20 additions & 0 deletions cli/pkg/cmdutil/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/rilldata/rill/admin/client"
"github.com/rilldata/rill/cli/pkg/dotrill"
"github.com/rilldata/rill/cli/pkg/dotrillcloud"
"github.com/rilldata/rill/cli/pkg/gitutil"
"github.com/rilldata/rill/cli/pkg/printer"
adminv1 "github.com/rilldata/rill/proto/gen/rill/admin/v1"
Expand Down Expand Up @@ -259,6 +260,25 @@ func (h *Helper) ProjectNamesByGithubURL(ctx context.Context, org, githubURL, su
}

func (h *Helper) InferProjectName(ctx context.Context, org, path string) (string, error) {
rc, err := dotrillcloud.GetAll(path, h.AdminURL)
if err != nil {
return "", err
}
if rc != nil {
c, err := h.Client()
if err != nil {
return "", err
}

proj, err := c.GetProjectByID(ctx, &adminv1.GetProjectByIDRequest{
Id: rc.ProjectID,
})
if err != nil {
return "", err
}
return proj.Project.Name, nil
}

// Verify projectPath is a Git repo with remote on Github
_, githubURL, err := gitutil.ExtractGitRemote(path, "", true)
if err != nil {
Expand Down
83 changes: 83 additions & 0 deletions cli/pkg/dotrillcloud/dotrillcloud.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package dotrillcloud

import (
"fmt"
"os"
"path/filepath"

"github.com/rilldata/rill/cli/pkg/adminenv"
"gopkg.in/yaml.v3"
)

type Config struct {
ProjectID string `yaml:"project_id"`
}

func GetAll(localProjectPath, adminURL string) (*Config, error) {
confPath, err := getConfPath(localProjectPath, adminURL)
if err != nil {
return nil, err
}

data, err := os.ReadFile(confPath)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}

conf := &Config{}
err = yaml.Unmarshal(data, conf)
if err != nil {
return nil, err
}
return conf, nil
}

func SetAll(localProjectPath, adminURL string, conf *Config) error {
err := os.MkdirAll(filepath.Join(localProjectPath, ".rillcloud"), os.ModePerm)
if err != nil {
return err
}

data, err := yaml.Marshal(conf)
if err != nil {
return err
}

confPath, err := getConfPath(localProjectPath, adminURL)
if err != nil {
return err
}

return os.WriteFile(confPath, data, 0o644)
}

func Delete(localProjectPath, adminURL string) error {
confPath, err := getConfPath(localProjectPath, adminURL)
if err != nil {
return err
}

err = os.Remove(confPath)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
return nil
}

func getConfPath(localProjectPath, adminURL string) (string, error) {
env, err := adminenv.Infer(adminURL)
if err != nil {
return "", err
}

if env != "prod" {
return filepath.Join(localProjectPath, ".rillcloud", fmt.Sprintf("project_%s.yaml", env)), nil
}
return filepath.Join(localProjectPath, ".rillcloud", "project.yaml"), nil
}
Loading

1 comment on commit fe464ef

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

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

Please sign in to comment.