Skip to content

Commit

Permalink
Merge pull request #22 from klothoplatform/state
Browse files Browse the repository at this point in the history
State
  • Loading branch information
atorres-klo authored Jun 10, 2024
2 parents 7c72c75 + 43395ae commit bfffb69
Show file tree
Hide file tree
Showing 27 changed files with 833 additions and 253 deletions.
7 changes: 4 additions & 3 deletions cmd/k2/dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"context"
"errors"
"os/exec"
"runtime"

errors2 "github.com/klothoplatform/klotho/pkg/errors"
Expand Down Expand Up @@ -70,10 +71,10 @@ func installPulumi(config CliDependencyConfig) error {
}

func isDockerInstalled() bool {
//TODO: Implement this

// Check if docker is installed
return true
cmd := exec.Command("docker", "--version")
err := cmd.Run()
return err == nil
}

func isPulumiInstalled() bool {
Expand Down
6 changes: 3 additions & 3 deletions cmd/k2/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package main

import (
"fmt"
"github.com/klothoplatform/klotho/pkg/k2/deployment"
"github.com/klothoplatform/klotho/pkg/k2/model"
"github.com/klothoplatform/klotho/pkg/k2/orchestrator"
"github.com/klothoplatform/klotho/pkg/k2/pulumi"
"github.com/spf13/cobra"
"go.uber.org/zap"
"log"
Expand Down Expand Up @@ -54,10 +54,10 @@ func downCmd(outputPath string) string {
return "failure"
}

var stackReferences []deployment.StackReference
var stackReferences []pulumi.StackReference
for _, entry := range entries {
if entry.IsDir() {
stackReference := deployment.StackReference{
stackReference := pulumi.StackReference{
ConstructURN: model.URN{},
Name: entry.Name(),
IacDirectory: filepath.Join(outputPath, entry.Name()),
Expand Down
3 changes: 1 addition & 2 deletions cmd/k2/language_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@ func waitForServer(client pb.KlothoServiceClient, retries int, delay time.Durati
}

func startPythonClient() *exec.Cmd {
cmd := exec.Command("python3", "python_language_host.py")
cmd := exec.Command("pipenv", "run", "python", "python_language_host.py")
cmd.Dir = "pkg/k2/language_host/python"

cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

Expand Down
39 changes: 31 additions & 8 deletions cmd/k2/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ package main
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"time"

"github.com/klothoplatform/klotho/pkg/engine/constraints"
"github.com/klothoplatform/klotho/pkg/k2/constructs"
"github.com/klothoplatform/klotho/pkg/k2/deployment"
pb "github.com/klothoplatform/klotho/pkg/k2/language_host/go"
"github.com/klothoplatform/klotho/pkg/k2/model"
"github.com/klothoplatform/klotho/pkg/k2/orchestrator"
"github.com/klothoplatform/klotho/pkg/k2/pulumi"
"github.com/spf13/cobra"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
"os"
"path/filepath"
"time"
)

var upConfig struct {
Expand Down Expand Up @@ -109,7 +110,29 @@ func updCmd(args struct {
return fmt.Sprintf("Error reading IR file: %s", err)
}

var o orchestrator.Orchestrator
// Take the IR -- generate and save a state file and stored in the
// output directory, the path should include the environment name and
// the project URN
statefile := filepath.Join(args.outputPath, fmt.Sprintf("%s-%s-state.yaml", ir.ProjectURN, ir.Environment))

// Create a new state manager
sm := model.NewStateManager(statefile)

// Initialize the state if it doesn't exist
if !sm.CheckStateFileExists() {
sm.InitState(ir)
// Save the state
if err = sm.SaveState(); err != nil {
return fmt.Sprintf("Error saving state: %s", err)
}
} else {
// Load the state
if err = sm.LoadState(); err != nil {
return fmt.Sprintf("Error loading state: %s", err)
}
}

o := orchestrator.NewOrchestrator(sm)

// Apply constraints
for _, c := range ir.Constructs {
Expand Down Expand Up @@ -178,15 +201,15 @@ func updCmd(args struct {
}
}

var refs []deployment.StackReference
var refs []pulumi.StackReference
for _, c := range ir.Constructs {
var id constructs.ConstructId
err = id.FromURN(c.URN)
if err != nil {
return fmt.Sprintf("Error parsing URN: %s", err)
}
constructOutDir := filepath.Join(args.outputPath, id.InstanceId)
refs = append(refs, deployment.StackReference{
refs = append(refs, pulumi.StackReference{
ConstructURN: c.URN,
Name: id.InstanceId,
IacDirectory: constructOutDir,
Expand Down
77 changes: 76 additions & 1 deletion pkg/construct/graph_io.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package construct
import (
"errors"
"fmt"
"reflect"
"sort"
"strings"

Expand All @@ -11,7 +12,8 @@ import (
)

type YamlGraph struct {
Graph Graph
Graph Graph
Outputs map[string]Output
}

// nullNode is used to render as nothing in the YAML output
Expand Down Expand Up @@ -89,6 +91,50 @@ func (g YamlGraph) MarshalYAML() (interface{}, error) {
edges = nullNode
}

outputs := &yaml.Node{
Kind: yaml.MappingNode,
}
for name, output := range g.Outputs {
outputs.Content = append(outputs.Content,
&yaml.Node{
Kind: yaml.ScalarNode,
Value: name,
})

outputMap := &yaml.Node{
Kind: yaml.MappingNode,
}

if !output.Ref.IsZero() {
outputMap.Content = append(outputMap.Content,
&yaml.Node{
Kind: yaml.ScalarNode,
Value: "ref",
},
&yaml.Node{
Kind: yaml.ScalarNode,
Value: output.Ref.String(),
},
)
} else {
value := &yaml.Node{}
err = value.Encode(output.Value)
if err != nil {
errs = errors.Join(errs, err)
continue
}

outputMap.Content = append(outputMap.Content,
&yaml.Node{
Kind: yaml.ScalarNode,
Value: "value",
},
value,
)
}
outputs.Content = append(outputs.Content, outputMap)
}

return &yaml.Node{
Kind: yaml.MappingNode,
Content: []*yaml.Node{
Expand All @@ -102,14 +148,35 @@ func (g YamlGraph) MarshalYAML() (interface{}, error) {
Value: "edges",
},
edges,
{
Kind: yaml.ScalarNode,
Value: "outputs",
},
outputs,
},
}, nil
}

func resolveNodeType(value any) yaml.Kind {
v := reflect.ValueOf(value)
if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
v = v.Elem()
}
switch v.Kind() {
case reflect.Map:
return yaml.MappingNode
case reflect.Slice, reflect.Array:
return yaml.SequenceNode
default:
return yaml.ScalarNode
}
}

func (g *YamlGraph) UnmarshalYAML(n *yaml.Node) error {
type graph struct {
Resources map[ResourceId]Properties `yaml:"resources"`
Edges map[SimpleEdge]struct{} `yaml:"edges"`
Outputs map[string]Output `yaml:"outputs"`
}
var y graph
if err := n.Decode(&y); err != nil {
Expand Down Expand Up @@ -143,6 +210,14 @@ func (g *YamlGraph) UnmarshalYAML(n *yaml.Node) error {
err := g.Graph.AddEdge(e.Source, e.Target)
errs = errors.Join(errs, err)
}

if g.Outputs == nil {
g.Outputs = make(map[string]Output)
}
for name, output := range y.Outputs {
g.Outputs[name] = Output{Ref: output.Ref, Value: output.Value}
}

return errs
}

Expand Down
6 changes: 6 additions & 0 deletions pkg/construct/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package construct

type Output struct {
Ref PropertyRef `json:"ref,omitempty" yaml:"ref,omitempty"`
Value any `json:"value,omitempty" yaml:"value,omitempty"`
}
15 changes: 15 additions & 0 deletions pkg/construct/property_ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ type PropertyRef struct {
}

func (v PropertyRef) String() string {
if v.IsZero() {
return ""
}
return v.Resource.String() + "#" + v.Property
}

Expand All @@ -37,3 +40,15 @@ func (v *PropertyRef) UnmarshalText(b []byte) error {
}
return v.Validate()
}

func (v *PropertyRef) Equals(ref interface{}) bool {
other, ok := ref.(PropertyRef)
if !ok {
return false
}
return v.Resource == other.Resource && v.Property == other.Property
}

func (v *PropertyRef) IsZero() bool {
return v.Resource.IsZero() && v.Property == ""
}
10 changes: 10 additions & 0 deletions pkg/engine/constraints.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ func ApplyConstraints(ctx solution_context.SolutionContext) error {
return nil
}

func applyOutputConstraint(ctx solution_context.SolutionContext, constraint constraints.OutputConstraint) error {
currentOutputs := ctx.Outputs()
if current, ok := currentOutputs[constraint.Name]; ok && !current.Ref.Equals(constraint.Ref) {
return fmt.Errorf("output %s already exists with a different value", constraint.Name)
}

ctx.Outputs()[constraint.Name] = construct.Output{Ref: constraint.Ref}
return nil
}

// applyApplicationConstraint returns a resource to be made operational, if needed. Otherwise, it returns nil.
func applyApplicationConstraint(ctx solution_context.SolutionContext, constraint constraints.ApplicationConstraint) error {
ctx = ctx.With("constraint", constraint)
Expand Down
14 changes: 14 additions & 0 deletions pkg/engine/constraints/constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type (
GetResource(construct.ResourceId) (*construct.Resource, error)
AllPaths(src, dst construct.ResourceId) ([][]*construct.Resource, error)
GetClassification(construct.ResourceId) knowledgebase.Classification
GetOutput(string) construct.Output
}

// BaseConstraint is the base struct for all constraints
Expand All @@ -58,6 +59,7 @@ type (
Construct []ConstructConstraint
Resources []ResourceConstraint
Edges []EdgeConstraint
Outputs []OutputConstraint
}
)

Expand All @@ -66,6 +68,7 @@ const (
ConstructConstraintScope ConstraintScope = "construct"
EdgeConstraintScope ConstraintScope = "edge"
ResourceConstraintScope ConstraintScope = "resource"
OutputConstraintScope ConstraintScope = "output"

MustExistConstraintOperator ConstraintOperator = "must_exist"
MustNotExistConstraintOperator ConstraintOperator = "must_not_exist"
Expand Down Expand Up @@ -158,6 +161,11 @@ func (cs *ConstraintList) UnmarshalYAML(node *yaml.Node) error {
err = raw.Decode(&constraint)
c = &constraint

case OutputConstraintScope:
var constraint OutputConstraint
err = raw.Decode(&constraint)
c = &constraint

default:
err = fmt.Errorf("invalid scope %q", base.Scope)
}
Expand Down Expand Up @@ -186,6 +194,8 @@ func (list ConstraintList) ToConstraints() (Constraints, error) {
constraints.Resources = append(constraints.Resources, *c)
case *EdgeConstraint:
constraints.Edges = append(constraints.Edges, *c)
case *OutputConstraint:
constraints.Outputs = append(constraints.Outputs, *c)
default:
return Constraints{}, fmt.Errorf("invalid constraint type %T", constraint)
}
Expand Down Expand Up @@ -239,6 +249,9 @@ func (c Constraints) ToList() ConstraintList {
for i := range c.Edges {
list = append(list, &c.Edges[i])
}
for i := range c.Outputs {
list = append(list, &c.Outputs[i])
}
return list
}

Expand All @@ -261,4 +274,5 @@ func (c *Constraints) Append(other Constraints) {
c.Construct = append(c.Construct, other.Construct...)
c.Resources = append(c.Resources, other.Resources...)
c.Edges = append(c.Edges, other.Edges...)
c.Outputs = append(c.Outputs, other.Outputs...)
}
Loading

0 comments on commit bfffb69

Please sign in to comment.