Skip to content

Commit

Permalink
feat(GROW-2487): v1 component commands (#1428)
Browse files Browse the repository at this point in the history
  • Loading branch information
jon-stewart authored Nov 1, 2023
1 parent 32ce177 commit f44bf6f
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 58 deletions.
2 changes: 2 additions & 0 deletions api/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type LatestComponentVersion struct {
Version string `json:"version"`
Size int64 `json:"size"`
ComponentType string `json:"type"`
Deprecated bool `json:"deprecated"`
}

func (svc *ComponentsService) ListComponents(os string, arch string) (response ListComponentsResponse, err error) {
Expand All @@ -41,6 +42,7 @@ type ComponentVersions struct {
Name string `json:"name"`
Description string `json:"description"`
Component_type string `json:"type"`
Deprecated bool `json:"deprecated"`
Versions []string `json:"versions"`
}

Expand Down
101 changes: 92 additions & 9 deletions cli/cmd/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ func init() {
componentsCmd.AddCommand(componentsDevModeCmd)

// load components dynamically
cli.PrototypeLoadComponents()

// v1 components
cli.LoadComponents()
}

Expand Down Expand Up @@ -158,9 +161,96 @@ func (c *cliState) IsComponentInstalled(name string) bool {
return false
}

// Load v1 components
func (c *cliState) LoadComponents() {
components, err := lwcomponent.LocalComponents()
if err != nil {
c.Log.Debugw("unable to load components", "error", err)
return
}

// @jon-stewart: TODO: load from cached API info

for _, component := range components {
exists := false

for _, cmd := range rootCmd.Commands() {
if cmd.Use == component.Name {
exists = true
break
}
}

// Skip components that were added by the prototype code
if exists {
continue
}

version := component.InstalledVersion()
if version != nil && component.Exec.Executable() {
componentCmd := &cobra.Command{
Use: component.Name,
Short: component.Description,
Annotations: map[string]string{"type": componentTypeAnnotation},
Version: version.String(),
SilenceUsage: true,
DisableFlagParsing: true,
DisableFlagsInUseLine: true,
RunE: func(cmd *cobra.Command, args []string) error {
return v1ComponentCommand(c, cmd, args)
},
}

rootCmd.AddCommand(componentCmd)
}
}
}

// Grpc server used for components to communicate back to the CLI
func startGrpcServer(c *cliState) {
if err := c.Serve(); err != nil {
c.Log.Errorw("couldn't serve gRPC server", "error", err)
}
}

func v1ComponentCommand(c *cliState, cmd *cobra.Command, args []string) error {
// Parse component -v/--version flag
versionVal, _ := cmd.Flags().GetBool("version")
if versionVal {
cmd.Printf("%s version %s\n", cmd.Use, cmd.Version)
return nil
}

go startGrpcServer(c)

c.Log.Debugw("running component", "component", cmd.Use,
"args", c.componentParser.componentArgs,
"cli_flags", c.componentParser.cliArgs)

catalog, err := lwcomponent.NewCatalog(cli.LwApi, lwcomponent.NewStageTarGz)
if err != nil {
return errors.Wrap(err, "unable to load component Catalog")
}

component, err := catalog.GetComponent(cmd.Use)
if err != nil {
return err
}

// @jon-stewart: TODO: v1 dailyComponentUpdateAvailable

envs := []string{
fmt.Sprintf("LW_COMPONENT_NAME=%s", cmd.Use),
}

envs = append(envs, c.envs()...)

return component.Exec.ExecuteInline(c.componentParser.componentArgs, envs...)
}

// LoadComponents reads the local components state and loads all installed components
// of type `CLI_COMMAND` dynamically into the root command of the CLI (`rootCmd`)
func (c *cliState) LoadComponents() {
func (c *cliState) PrototypeLoadComponents() {
c.Log.Debugw("loading local components")
state, err := lwcomponent.LocalState()
if err != nil || state == nil {
Expand Down Expand Up @@ -421,14 +511,7 @@ func showComponent(args []string) error {

printComponent(component.PrintSummary())

version := component.InstalledVersion()

availableVersions, err := catalog.ListComponentVersions(component)
if err != nil {
return err
}

printAvailableVersions(version, availableVersions)
printAvailableVersions(component.InstalledVersion(), catalog.ListComponentVersions(component))

return nil
}
Expand Down
16 changes: 14 additions & 2 deletions lwcomponent/api_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ type ApiInfo interface {
LatestVersion() *semver.Version

AllVersions() []*semver.Version

Deprecated() bool
}

type apiInfo struct {
Expand All @@ -19,6 +21,7 @@ type apiInfo struct {
allVersions []*semver.Version
desc string
sizeKB int64
deprecated bool
}

func NewAPIInfo(
Expand All @@ -28,6 +31,7 @@ func NewAPIInfo(
allVersions []*semver.Version,
desc string,
size int64,
deprecated bool,
) ApiInfo {
return &apiInfo{
id: id,
Expand All @@ -36,17 +40,25 @@ func NewAPIInfo(
allVersions: allVersions,
desc: desc,
sizeKB: size,
deprecated: deprecated,
}
}

func (a *apiInfo) Id() int32 {
return a.id
}

// AllVersions implements ApiInfo.
func (a *apiInfo) AllVersions() []*semver.Version {
return a.allVersions
}

// LatestVersion implements ApiInfo.
func (a *apiInfo) LatestVersion() *semver.Version {
return &a.version
}

func (a *apiInfo) AllVersions() []*semver.Version {
return a.allVersions
// Deprecated implements ApiInfo.
func (a *apiInfo) Deprecated() bool {
return a.deprecated
}
4 changes: 2 additions & 2 deletions lwcomponent/api_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestApiInfoId(t *testing.T) {

var id int32 = 23

info := lwcomponent.NewAPIInfo(id, "test", version, allVersions, "", 0)
info := lwcomponent.NewAPIInfo(id, "test", version, allVersions, "", 0, false)

result := info.Id()
assert.Equal(t, id, result)
Expand All @@ -32,7 +32,7 @@ func TestApiInfoLatestVersion(t *testing.T) {
panic(err)
}

info := lwcomponent.NewAPIInfo(1, "test", version, allVersions, "", 0)
info := lwcomponent.NewAPIInfo(1, "test", version, allVersions, "", 0, false)

result := info.LatestVersion()
assert.Equal(t, expectedVer, result.String())
Expand Down
33 changes: 25 additions & 8 deletions lwcomponent/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,14 @@ func (c *Catalog) GetComponent(name string) (*CDKComponent, error) {
return &component, nil
}

func (c *Catalog) ListComponentVersions(component *CDKComponent) (versions []*semver.Version, err error) {
func (c *Catalog) ListComponentVersions(component *CDKComponent) (versions []*semver.Version) {
if component.apiInfo == nil {
return
}

return listComponentVersions(c.client, component.apiInfo.Id())
versions = component.apiInfo.AllVersions()

return
}

func (c *Catalog) PrintComponents() [][]string {
Expand Down Expand Up @@ -253,7 +255,7 @@ func NewCatalog(client *api.Client, stageConstructor StageConstructor) (*Catalog
return nil, errors.Wrap(err, fmt.Sprintf("unable to fetch component '%s' versions", c.Name))
}

api := NewAPIInfo(c.Id, c.Name, ver, allVersions, c.Description, c.Size)
api := NewAPIInfo(c.Id, c.Name, ver, allVersions, c.Description, c.Size, c.Deprecated)

host, found := localComponents[c.Name]
if found {
Expand All @@ -273,6 +275,20 @@ func NewCatalog(client *api.Client, stageConstructor StageConstructor) (*Catalog
return &Catalog{client, cdkComponents, stageConstructor}, nil
}

func LocalComponents() (components []CDKComponent, err error) {

localHostInfo, err := loadLocalComponents()
if err != nil {
return
}

for _, l := range localHostInfo {
components = append(components, NewCDKComponent(l.Name(), BinaryType, nil, l))
}

return
}

func loadLocalComponents() (local map[string]HostInfo, err error) {
cacheDir, err := CatalogCacheDir()
if err != nil {
Expand Down Expand Up @@ -303,9 +319,14 @@ func listComponentVersions(client *api.Client, componentId int32) (versions []*s
return nil, err
}

rawVersions := response.Data[0].Versions
var rawVersions []string

if len(response.Data) > 0 {
rawVersions = response.Data[0].Versions
}

versions = make([]*semver.Version, len(rawVersions))

for idx, v := range rawVersions {
ver, err := semver.NewVersion(v)
if err != nil {
Expand All @@ -318,10 +339,6 @@ func listComponentVersions(client *api.Client, componentId int32) (versions []*s
return versions, nil
}

// func isDevelopmentComponent(path string, name string) bool {
// return file.FileExists(filepath.Join(path, name, DevelopmentFile))
// }

// Returns the directory that the component executable and configuration is stored in.
func componentDirectory(componentName string) (string, error) {
dir, err := CatalogCacheDir()
Expand Down
25 changes: 13 additions & 12 deletions lwcomponent/catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,9 +348,9 @@ func TestCatalogGetComponent(t *testing.T) {
assert.Equal(t, version, ver.String())
})

t.Run("deprecated", func(t *testing.T) {
t.Run("installed deprecated", func(t *testing.T) {
var (
name = "deprecated"
name = "installed deprecated"
version = "1.1.0"
)

Expand All @@ -365,7 +365,7 @@ func TestCatalogGetComponent(t *testing.T) {
component, err := catalog.GetComponent(name)
assert.NotNil(t, component)
assert.Nil(t, err)
assert.Equal(t, lwcomponent.Deprecated, component.Status)
assert.Equal(t, lwcomponent.InstalledDeprecated, component.Status)

ver := component.InstalledVersion()
assert.Equal(t, version, ver.String())
Expand Down Expand Up @@ -408,8 +408,7 @@ func TestCatalogListComponentVersions(t *testing.T) {
assert.NotNil(t, component)
assert.Nil(t, err)

vers, err := catalog.ListComponentVersions(component)
assert.Nil(t, err)
vers := catalog.ListComponentVersions(component)

for idx, v := range versions {
assert.Equal(t, v, vers[idx].String())
Expand Down Expand Up @@ -810,10 +809,11 @@ func generateComponentsResponse(prefix string, count int) string {

for idx = 0; idx < int32(count); idx++ {
component := api.LatestComponentVersion{
Id: idx,
Name: fmt.Sprintf("%s-%d", prefix, idx),
Version: fmt.Sprintf("%d.0.0", idx),
Size: 512,
Id: idx,
Name: fmt.Sprintf("%s-%d", prefix, idx),
Version: fmt.Sprintf("%d.0.0", idx),
Size: 512,
Deprecated: false,
}

components = append(components, component)
Expand Down Expand Up @@ -847,9 +847,10 @@ func generateInvalidComponentsResponse() string {
func generateComponentVersionsResponse(name string, versions []string) string {
response := api.ListComponentVersionsResponse{
Data: []api.ComponentVersions{{
Id: 1,
Name: name,
Versions: versions,
Id: 1,
Name: name,
Deprecated: false,
Versions: versions,
}},
}

Expand Down
14 changes: 11 additions & 3 deletions lwcomponent/cdk_component.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func NewCDKComponent(name string, componentType Type, apiInfo ApiInfo, hostInfo
status := status(apiInfo, hostInfo)

switch status {
case Installed, UpdateAvailable, Deprecated, Development:
case Installed, UpdateAvailable, InstalledDeprecated, Development:
{
dir := hostInfo.Dir()

Expand Down Expand Up @@ -84,7 +84,7 @@ func (c *CDKComponent) PrintSummary() []string {
)

switch c.Status {
case Installed, Deprecated, Development, UpdateAvailable, Tainted:
case Installed, InstalledDeprecated, NotInstalledDeprecated, Development, UpdateAvailable, Tainted:
version, err = c.hostInfo.Version()
if err != nil {
panic(err)
Expand Down Expand Up @@ -123,6 +123,10 @@ func status(apiInfo ApiInfo, hostInfo HostInfo) Status {
return Tainted
}

if apiInfo.Deprecated() {
return InstalledDeprecated
}

latestVer := apiInfo.LatestVersion()
if latestVer.GreaterThan(installedVer) {
return UpdateAvailable
Expand All @@ -133,12 +137,16 @@ func status(apiInfo ApiInfo, hostInfo HostInfo) Status {
if hostInfo.Development() {
return Development
} else {
return Deprecated
return InstalledDeprecated
}
}
}

if apiInfo != nil && hostInfo == nil {
if apiInfo.Deprecated() {
return NotInstalledDeprecated
}

return NotInstalled
}

Expand Down
Loading

0 comments on commit f44bf6f

Please sign in to comment.