Skip to content

Commit

Permalink
feat(cdk): component dev mode (#911)
Browse files Browse the repository at this point in the history
This feature is adding the ability to enter development mode of one CDK
component at a time. To do so, developers need to export the environment
variable `LW_CDK_DEV_COMPONENT`

Example:
```
$ export LW_CDK_DEV_COMPONENT=new-component
$ lacework component list
```

That should show you the following output:
```
     STATUS           NAME        VERSION                                       DESCRIPTION
----------------+---------------+---------+---------------------------------------------------------------------------------------
  Not Installed   new-component   0.0.0     create dev specs file '/Users/afiune/.config/lacework/components/new-component/.dev'

Components version: 0.2.0
```

Then you just need to create the dev specs file that contains all the
specifications of your dev component.

Example:
```
cat ~/.config/lacework/components/new-component/.dev
{
  "version": "0.0.1",
  "name": "new-component",
  "description": "A component being developed. Have fun!",
  "type": "CLI_COMMAND",
  "artifacts": []
}
```

Finally, if you try to install the component, you will get an error
message like this one:
```
$ lacework component install my-component

ERROR unable to install component: components under development can't be installed.

Deploy the component manually at '/Users/afiune/.config/lacework/components/my-component/my-component'
```

As the message suggests, deploy the component manually and you should be
good to go.

Signed-off-by: Salim Afiune Maya <[email protected]>

Signed-off-by: Salim Afiune Maya <[email protected]>
  • Loading branch information
afiune authored Oct 3, 2022
1 parent 0167eab commit d477e68
Showing 1 changed file with 86 additions and 0 deletions.
86 changes: 86 additions & 0 deletions lwcomponent/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,36 @@ func LoadState(client *api.Client) (*State, error) {
return s, err
}

s.loadDevComponent()

return s, s.WriteState()
}
return nil, errors.New("invalid api client")
}

// loadDevComponent will load a component that is under development,
// developers need to export the environment variable 'LW_CDK_DEV_COMPONENT'
func (s *State) loadDevComponent() {
if devComponent := os.Getenv("LW_CDK_DEV_COMPONENT"); devComponent != "" {
for i := range s.Components {
if s.Components[i].Name == devComponent {
// existing component being developed
if err := s.Components[i].loadDevSpecs(); err != nil {
s.Components[i].Description = err.Error()
}
return
}
}

// component is not yet defined, add it to the state
dev := Component{Name: devComponent}
if err := dev.loadDevSpecs(); err != nil {
dev.Description = err.Error()
}
s.Components = append(s.Components, dev)
}
}

// LocalState loads the state from the local storage ("Dir()/state")
func LocalState() (*State, error) {
state := new(State)
Expand Down Expand Up @@ -150,6 +175,14 @@ func (s State) Install(name string) error {
return err
}

// verify development mode
if component.underDevelopment() {
p, _ := component.Path() // @afiune we don't care if the component exists or not
msg := "components under development can't be installed.\n\n" +
"Deploy the component manually at '" + p + "'"
return errors.New(msg)
}

// @afiune verify if component is in latest

// @afiune install
Expand Down Expand Up @@ -262,6 +295,12 @@ func (c Component) Path() (string, error) {

// CurrentVersion returns the current installed version of the component
func (c Component) CurrentVersion() (*semver.Version, error) {
// development mode, avoid loading the current version,
// return latest which is what's inside the '.dev' specs
if c.underDevelopment() {
return &c.LatestVersion, nil
}

dir, err := c.RootPath()
if err != nil {
return nil, err
Expand Down Expand Up @@ -381,7 +420,54 @@ func (c Component) ArtifactForRunningHost() (*Artifact, bool) {
return nil, false
}

// loadDevSpecs will lookup for the '.dev' specs file under the
// component root path to load it into the component itself
func (c *Component) loadDevSpecs() error {
dir, err := c.RootPath()
if err != nil {
return errors.New("unable to detect RootPath")
}

devSpecs := filepath.Join(dir, ".dev")
if file.FileExists(devSpecs) {
devSpecsBytes, err := ioutil.ReadFile(devSpecs)
if err != nil {
return errors.Errorf("unable to read %s file", devSpecs)
}
err = json.Unmarshal(devSpecsBytes, c)
if err != nil {
return errors.Errorf("unable to unmarshal %s file", devSpecs)
}
} else {
return errors.Errorf("create dev specs file '%s'", devSpecs)
}

return nil
}

// underDevelopment returns true if the component is under development
// that is, if the component root path has the '.dev' specs file or, if
// the environment variable 'LW_CDK_DEV_COMPONENT' matches the component name
func (c Component) underDevelopment() bool {
if os.Getenv("LW_CDK_DEV_COMPONENT") == c.Name {
return true
}

dir, err := c.RootPath()
if err != nil {
return false
}

return file.FileExists(filepath.Join(dir, ".dev"))
}

// isVerified checks if the component has a valid signature
func (c Component) isVerified() error {
// development mode, avoid verifying
if c.underDevelopment() {
return nil
}

// get component signature
sig, err := c.SignatureFromDisk()
if err != nil {
Expand Down

0 comments on commit d477e68

Please sign in to comment.