Skip to content

Commit

Permalink
feat: create and update template from file
Browse files Browse the repository at this point in the history
  • Loading branch information
Yuanlin Lin committed Jun 5, 2024
1 parent 2dfb579 commit e7c6288
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 1 deletion.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,6 @@ require (
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gopkg.in/yaml.v3 v3.0.1
nhooyr.io/websocket v1.8.7 // indirect
)
102 changes: 102 additions & 0 deletions internal/cmd/template/create/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package create

import (
"context"
"fmt"
"io"
"net/http"
"os"
"strings"

"github.com/briandowns/spinner"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"

"github.com/zeabur/cli/internal/cmdutil"
)

type Options struct {
file string
}

func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
opts := Options{}

cmd := &cobra.Command{
Use: "create",
Short: "Create template from file",
RunE: func(cmd *cobra.Command, args []string) error {
return runCreate(f, opts)
},
}

cmd.Flags().StringVarP(&opts.file, "file", "f", "", "Template file")

return cmd
}

func runCreate(f *cmdutil.Factory, opts Options) error {
if opts.file == "" {
return fmt.Errorf("file is required, use -f or --file to specify the file path")
}

var file []byte
var err error

if strings.HasPrefix(opts.file, "https://") || strings.HasPrefix(opts.file, "http://") {
s := spinner.New(cmdutil.SpinnerCharSet, cmdutil.SpinnerInterval,
spinner.WithColor(cmdutil.SpinnerColor),
spinner.WithSuffix(" Fetching remote template file ..."),
)

s.Start()
get, err := http.Get(opts.file)
if err != nil {
f.Log.Errorf("fetch file failed: %v", err)
return err
}

file, err = io.ReadAll(get.Body)
if err != nil {
f.Log.Errorf("read file failed: %v", err)
return err
}

s.Stop()
} else {
file, err = os.ReadFile(opts.file)
if err != nil {
return fmt.Errorf("read file failed: %w", err)
}
}

type RawTemplate struct {
APIVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
Metadata struct {
Name string `yaml:"name"`
} `yaml:"metadata"`
Spec struct {
Variables []struct {
Key string `yaml:"key"`
Type string `yaml:"type"`
Name string `yaml:"name"`
Description string `yaml:"description"`
} `yaml:"variables"`
}
}

var raw RawTemplate
err = yaml.Unmarshal(file, &raw)
if err != nil {
return fmt.Errorf("parse yaml failed: %w", err)
}

t, err := f.ApiClient.CreateTemplateFromFile(context.Background(), string(file))
if err != nil {
return err
}

f.Log.Infof("Template %q (https://zeabur.com/templates/%s) created", t.Name, t.Code)
return nil
}
4 changes: 4 additions & 0 deletions internal/cmd/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import (

"github.com/zeabur/cli/internal/cmdutil"

templateCreateCmd "github.com/zeabur/cli/internal/cmd/template/create"
templateDeleteCmd "github.com/zeabur/cli/internal/cmd/template/delete"
templateDeployCmd "github.com/zeabur/cli/internal/cmd/template/deploy"
templateGetCmd "github.com/zeabur/cli/internal/cmd/template/get"
templateListCmd "github.com/zeabur/cli/internal/cmd/template/list"
templateUpdateCmd "github.com/zeabur/cli/internal/cmd/template/update"
)

func NewCmdTemplate(f *cmdutil.Factory) *cobra.Command {
Expand All @@ -21,6 +23,8 @@ func NewCmdTemplate(f *cmdutil.Factory) *cobra.Command {
cmd.AddCommand(templateDeployCmd.NewCmdDeploy(f))
cmd.AddCommand(templateGetCmd.NewCmdGet(f))
cmd.AddCommand(templateDeleteCmd.NewCmdDelete(f))
cmd.AddCommand(templateCreateCmd.NewCmdCreate(f))
cmd.AddCommand(templateUpdateCmd.NewCmdUpdate(f))

return cmd
}
108 changes: 108 additions & 0 deletions internal/cmd/template/update/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package update

import (
"context"
"fmt"
"io"
"net/http"
"os"
"strings"

"github.com/briandowns/spinner"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"

"github.com/zeabur/cli/internal/cmdutil"
)

type Options struct {
code string
file string
}

func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
opts := Options{}

cmd := &cobra.Command{
Use: "update",
Short: "Update template from file",
RunE: func(cmd *cobra.Command, args []string) error {
return runUpdate(f, opts)
},
}

cmd.Flags().StringVarP(&opts.code, "code", "c", "", "Template code")
cmd.Flags().StringVarP(&opts.file, "file", "f", "", "Template file")

return cmd
}

func runUpdate(f *cmdutil.Factory, opts Options) error {
if opts.code == "" {
return fmt.Errorf("code is required, use -c or --code to specify the template code")
}

if opts.file == "" {
return fmt.Errorf("file is required, use -f or --file to specify the file path")
}

var file []byte
var err error

if strings.HasPrefix(opts.file, "https://") || strings.HasPrefix(opts.file, "http://") {
s := spinner.New(cmdutil.SpinnerCharSet, cmdutil.SpinnerInterval,
spinner.WithColor(cmdutil.SpinnerColor),
spinner.WithSuffix(" Fetching remote template file ..."),
)

s.Start()
get, err := http.Get(opts.file)
if err != nil {
f.Log.Errorf("fetch file failed: %v", err)
return err
}

file, err = io.ReadAll(get.Body)
if err != nil {
f.Log.Errorf("read file failed: %v", err)
return err
}

s.Stop()
} else {
file, err = os.ReadFile(opts.file)
if err != nil {
return fmt.Errorf("read file failed: %w", err)
}
}

type RawTemplate struct {
APIVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
Metadata struct {
Name string `yaml:"name"`
} `yaml:"metadata"`
Spec struct {
Variables []struct {
Key string `yaml:"key"`
Type string `yaml:"type"`
Name string `yaml:"name"`
Description string `yaml:"description"`
} `yaml:"variables"`
}
}

var raw RawTemplate
err = yaml.Unmarshal(file, &raw)
if err != nil {
return fmt.Errorf("parse yaml failed: %w", err)
}

_, err = f.ApiClient.UpdateTemplateFromFile(context.Background(), opts.code, string(file))
if err != nil {
return err
}

f.Log.Infof("Template updated.")
return nil
}
3 changes: 3 additions & 0 deletions pkg/api/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,8 @@ type (
projectID string,
) (*model.Project, error)
DeleteTemplate(ctx context.Context, code string) error

CreateTemplateFromFile(ctx context.Context, rawSpecYaml string) (*model.Template, error)
UpdateTemplateFromFile(ctx context.Context, code, rawSpecYaml string) (bool, error)
}
)
26 changes: 26 additions & 0 deletions pkg/api/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,32 @@ func (c *client) DeployTemplate(
return &mutation.DeployTemplate, nil
}

func (c *client) CreateTemplateFromFile(ctx context.Context, rawSpecYaml string) (*model.Template, error) {
var mutation struct {
CreateTemplateFromFile model.Template `graphql:"createTemplateFromFile(rawSpecYaml: $rawSpecYaml)"`
}

err := c.Mutate(ctx, &mutation, V{"rawSpecYaml": rawSpecYaml})
if err != nil {
return nil, err
}

return &mutation.CreateTemplateFromFile, nil
}

func (c *client) UpdateTemplateFromFile(ctx context.Context, code, rawSpecYaml string) (bool, error) {
var mutation struct {
UpdateTemplateFromFile bool `graphql:"updateTemplateFromFile(code: $code, rawSpecYaml: $rawSpecYaml)"`
}

err := c.Mutate(ctx, &mutation, V{"rawSpecYaml": rawSpecYaml, "code": code})
if err != nil {
return false, err
}

return mutation.UpdateTemplateFromFile, nil
}

func (c *client) GetTemplate(ctx context.Context, code string) (*model.Template, error) {
var query struct {
Template model.Template `graphql:"template(code: $code)"`
Expand Down

0 comments on commit e7c6288

Please sign in to comment.