Skip to content

Commit

Permalink
refactor: expose bound and is bound to spec
Browse files Browse the repository at this point in the history
  • Loading branch information
siyul-park committed Oct 8, 2024
1 parent 5989e23 commit 69fb695
Show file tree
Hide file tree
Showing 12 changed files with 178 additions and 190 deletions.
4 changes: 2 additions & 2 deletions pkg/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,12 @@ func (r *Runtime) Reconcile(ctx context.Context) error {
bounded := make(map[uuid.UUID]spec.Spec)
for _, id := range r.symbolTable.Keys() {
sb := r.symbolTable.Lookup(id)
if sb != nil && r.scheme.IsBound(sb.Spec, secrets...) {
if sb != nil && spec.IsBound(sb.Spec, secrets...) {
bounded[sb.Spec.GetID()] = sb.Spec
}
}
for _, sp := range unloaded {
if r.scheme.IsBound(sp, secrets...) {
if spec.IsBound(sp, secrets...) {
bounded[sp.GetID()] = sp
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func TestRuntime_Reconcile(t *testing.T) {
ID: uuid.Must(uuid.NewV7()),
Kind: kind,
Namespace: resource.DefaultNamespace,
Env: map[string][]spec.Secret{
Env: map[string][]spec.Value{
"key": {
{
ID: sec.GetID(),
Expand Down Expand Up @@ -178,7 +178,7 @@ func TestRuntime_Reconcile(t *testing.T) {
ID: uuid.Must(uuid.NewV7()),
Kind: kind,
Namespace: resource.DefaultNamespace,
Env: map[string][]spec.Secret{
Env: map[string][]spec.Value{
"key": {
{
ID: sec.GetID(),
Expand Down
90 changes: 0 additions & 90 deletions pkg/scheme/scheme.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@ import (
"reflect"
"sync"

"github.com/gofrs/uuid"
"github.com/pkg/errors"
"github.com/siyul-park/uniflow/pkg/encoding"
"github.com/siyul-park/uniflow/pkg/node"
"github.com/siyul-park/uniflow/pkg/resource"
"github.com/siyul-park/uniflow/pkg/secret"
"github.com/siyul-park/uniflow/pkg/spec"
"github.com/siyul-park/uniflow/pkg/template"
"github.com/siyul-park/uniflow/pkg/types"
)

Expand Down Expand Up @@ -105,92 +101,6 @@ func (s *Scheme) Compile(sp spec.Spec) (node.Node, error) {
return codec.Compile(sp)
}

// IsBound checks if the spec is bound to any of the provided secrets.
func (s *Scheme) IsBound(sp spec.Spec, secrets ...*secret.Secret) bool {
for _, vals := range sp.GetEnv() {
for _, val := range vals {
examples := make([]*secret.Secret, 0, 2)
if val.ID != uuid.Nil {
examples = append(examples, &secret.Secret{ID: val.ID})
}
if val.Name != "" {
examples = append(examples, &secret.Secret{Namespace: sp.GetNamespace(), Name: val.Name})
}

for _, sec := range secrets {
if len(resource.Match(sec, examples...)) > 0 {
return true
}
}
}
}
return false
}

// Bind processes the environment variables in the spec using the provided secrets.
func (s *Scheme) Bind(sp spec.Spec, secrets ...*secret.Secret) (spec.Spec, error) {
doc, err := types.Marshal(sp)
if err != nil {
return nil, err
}

unstructured := &spec.Unstructured{}
if err := types.Unmarshal(doc, unstructured); err != nil {
return nil, err
}

env := map[string]any{}
for key, vals := range unstructured.GetEnv() {
for i, val := range vals {
if val.ID != uuid.Nil || val.Name != "" {
example := &secret.Secret{
ID: val.ID,
Namespace: unstructured.GetNamespace(),
Name: val.Name,
}

var sec *secret.Secret
for _, s := range secrets {
if len(resource.Match(s, example)) > 0 {
sec = s
break
}
}
if sec == nil {
continue
}

v, err := template.Execute(val.Value, sec.Data)
if err != nil {
return nil, err
}

val.ID = sec.GetID()
val.Name = sec.GetName()
val.Value = v

vals[i] = val
}

env[key] = val.Value
}

if _, ok := env[key]; !ok {
return nil, errors.WithStack(encoding.ErrUnsupportedValue)
}
}

if len(env) > 0 {
fields, err := template.Execute(unstructured.Fields, env)
if err != nil {
return nil, err
}
unstructured.Fields = fields.(map[string]any)
}

return unstructured, nil
}

// Decode converts the provided spec.Spec into a structured representation using reflection and encoding utilities.
func (s *Scheme) Decode(sp spec.Spec) (spec.Spec, error) {
s.mu.RLock()
Expand Down
63 changes: 1 addition & 62 deletions pkg/scheme/scheme_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/go-faker/faker/v4"
"github.com/gofrs/uuid"
"github.com/siyul-park/uniflow/pkg/node"
"github.com/siyul-park/uniflow/pkg/secret"
"github.com/siyul-park/uniflow/pkg/spec"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -53,36 +52,6 @@ func TestScheme_Codec(t *testing.T) {
assert.False(t, ok)
}

func TestScheme_Bind(t *testing.T) {
s := New()
kind := faker.UUIDHyphenated()

s.AddKnownType(kind, &spec.Meta{})

sec := &secret.Secret{
ID: uuid.Must(uuid.NewV7()),
Data: "foo",
}

meta := &spec.Meta{
ID: uuid.Must(uuid.NewV7()),
Kind: kind,
Env: map[string][]spec.Secret{
"FOO": {
{
ID: sec.ID,
Value: "{{ . }}",
},
},
},
}

bind, err := s.Bind(meta, sec)
assert.NoError(t, err)
assert.Equal(t, "foo", bind.GetEnv()["FOO"][0].Value)
assert.True(t, s.IsBound(bind, sec))
}

func TestScheme_Decode(t *testing.T) {
s := New()
kind := faker.UUIDHyphenated()
Expand All @@ -93,7 +62,7 @@ func TestScheme_Decode(t *testing.T) {
Meta: spec.Meta{
ID: uuid.Must(uuid.NewV7()),
Kind: kind,
Env: map[string][]spec.Secret{
Env: map[string][]spec.Value{
"FOO": {
{
Value: "foo",
Expand Down Expand Up @@ -127,33 +96,3 @@ func TestScheme_Compile(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, n)
}

func TestScheme_IsBound(t *testing.T) {
s := New()
kind := faker.UUIDHyphenated()

s.AddKnownType(kind, &spec.Meta{})

sec1 := &secret.Secret{
ID: uuid.Must(uuid.NewV7()),
}
sec2 := &secret.Secret{
ID: uuid.Must(uuid.NewV7()),
}

meta := &spec.Meta{
ID: uuid.Must(uuid.NewV7()),
Kind: kind,
Env: map[string][]spec.Secret{
"FOO": {
{
ID: sec1.ID,
Value: "foo",
},
},
},
}

assert.True(t, s.IsBound(meta, sec1))
assert.False(t, s.IsBound(meta, sec2))
}
105 changes: 98 additions & 7 deletions pkg/spec/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ package spec

import (
"github.com/gofrs/uuid"
"github.com/pkg/errors"
"github.com/siyul-park/uniflow/pkg/encoding"
"github.com/siyul-park/uniflow/pkg/resource"
"github.com/siyul-park/uniflow/pkg/secret"
"github.com/siyul-park/uniflow/pkg/template"
"github.com/siyul-park/uniflow/pkg/types"
)

// Spec defines the behavior and connections of each node.
Expand Down Expand Up @@ -32,9 +37,9 @@ type Spec interface {
// SetPorts assigns port connections to the node.
SetPorts(val map[string][]Port)
// GetEnv retrieves the environment secrets for the node.
GetEnv() map[string][]Secret
GetEnv() map[string][]Value
// SetEnv assigns environment secrets to the node.
SetEnv(val map[string][]Secret)
SetEnv(val map[string][]Value)
}

// Meta contains metadata for node specifications.
Expand All @@ -52,7 +57,7 @@ type Meta struct {
// Ports define connections to other nodes.
Ports map[string][]Port `json:"ports,omitempty" bson:"ports,omitempty" yaml:"ports,omitempty" map:"ports,omitempty"`
// Env contains sensitive data associated with the node.
Env map[string][]Secret `json:"env,omitempty" bson:"env,omitempty" yaml:"env,omitempty" map:"env,omitempty"`
Env map[string][]Value `json:"env,omitempty" bson:"env,omitempty" yaml:"env,omitempty" map:"env,omitempty"`
}

// Port represents a network port or connection on a node.
Expand All @@ -65,8 +70,8 @@ type Port struct {
Port string `json:"port" bson:"port" yaml:"port" map:"port"`
}

// Secret represents a sensitive piece of data associated with a node.
type Secret struct {
// Value represents a sensitive piece of data associated with a node.
type Value struct {
// ID is the unique identifier of the secret.
ID uuid.UUID `json:"id,omitempty" bson:"_id,omitempty" yaml:"id,omitempty" map:"id,omitempty"`
// Name is the human-readable name of the secret.
Expand All @@ -78,6 +83,92 @@ type Secret struct {
var _ resource.Resource = (Spec)(nil)
var _ Spec = (*Meta)(nil)

// IsBound checks if the spec is bound to any of the provided secrets.
func IsBound(sp Spec, secrets ...*secret.Secret) bool {
for _, vals := range sp.GetEnv() {
for _, val := range vals {
examples := make([]*secret.Secret, 0, 2)
if val.ID != uuid.Nil {
examples = append(examples, &secret.Secret{ID: val.ID})
}
if val.Name != "" {
examples = append(examples, &secret.Secret{Namespace: sp.GetNamespace(), Name: val.Name})
}

for _, sec := range secrets {
if len(resource.Match(sec, examples...)) > 0 {
return true
}
}
}
}
return false
}

// Bind processes the environment variables in the spec using the provided secrets.
func Bind(sp Spec, secrets ...*secret.Secret) (Spec, error) {
doc, err := types.Marshal(sp)
if err != nil {
return nil, err
}

unstructured := &Unstructured{}
if err := types.Unmarshal(doc, unstructured); err != nil {
return nil, err
}

env := map[string]any{}
for key, vals := range unstructured.GetEnv() {
for i, val := range vals {
if val.ID != uuid.Nil || val.Name != "" {
example := &secret.Secret{
ID: val.ID,
Namespace: unstructured.GetNamespace(),
Name: val.Name,
}

var sec *secret.Secret
for _, s := range secrets {
if len(resource.Match(s, example)) > 0 {
sec = s
break
}
}
if sec == nil {
continue
}

v, err := template.Execute(val.Value, sec.Data)
if err != nil {
return nil, err
}

val.ID = sec.GetID()
val.Name = sec.GetName()
val.Value = v

vals[i] = val
}

env[key] = val.Value
}

if _, ok := env[key]; !ok {
return nil, errors.WithStack(encoding.ErrUnsupportedValue)
}
}

if len(env) > 0 {
fields, err := template.Execute(unstructured.Fields, env)
if err != nil {
return nil, err
}
unstructured.Fields = fields.(map[string]any)
}

return unstructured, nil
}

// GetID returns the node's unique identifier.
func (m *Meta) GetID() uuid.UUID {
return m.ID
Expand Down Expand Up @@ -139,11 +230,11 @@ func (m *Meta) SetPorts(val map[string][]Port) {
}

// GetEnv returns the node's environment secrets.
func (m *Meta) GetEnv() map[string][]Secret {
func (m *Meta) GetEnv() map[string][]Value {
return m.Env
}

// SetEnv sets the node's environment secrets.
func (m *Meta) SetEnv(val map[string][]Secret) {
func (m *Meta) SetEnv(val map[string][]Value) {
m.Env = val
}
Loading

0 comments on commit 69fb695

Please sign in to comment.