1- /*
2- Copyright © 2025 NAME HERE <EMAIL ADDRESS>
3- */
41package other
52
63import (
4+ "bytes"
75 "encoding/json"
86 "fmt"
7+ "io"
98 "os"
9+ "strings"
1010
1111 "github.com/cloudforet-io/cfctl/pkg/transport"
1212 "github.com/pterm/pterm"
@@ -21,76 +21,146 @@ type ResourceSpec struct {
2121 Spec map [string ]interface {} `yaml:"spec"`
2222}
2323
24+ func parseResourceSpecs (data []byte ) ([]ResourceSpec , error ) {
25+ var resources []ResourceSpec
26+
27+ // Split YAML documents
28+ decoder := yaml .NewDecoder (bytes .NewReader (data ))
29+ for {
30+ var resource ResourceSpec
31+ if err := decoder .Decode (& resource ); err != nil {
32+ if err == io .EOF {
33+ break
34+ }
35+ return nil , fmt .Errorf ("failed to parse YAML: %v" , err )
36+ }
37+ resources = append (resources , resource )
38+ }
39+
40+ return resources , nil
41+ }
42+
2443// ApplyCmd represents the apply command
2544var ApplyCmd = & cobra.Command {
2645 Use : "apply" ,
2746 Short : "Apply a configuration to a resource using a file" ,
2847 Long : `Apply the configuration in the YAML file to create or update a resource` ,
29- Example : ` # Create test.yaml
48+ Example : ` # 01. Create a test.yaml file with service-verb-resource-spec format
3049 service: identity
3150 verb: create
32- resource: user
51+ resource: WorkspaceGroup
52+ spec:
53+ name: Test Workspace Group
54+ ---
55+ service: identity
56+ verb: add_users
57+ resource: WorkspaceGroup
3358 spec:
34- user_id: test-user
35- auth_type: LOCAL
59+ workspace_group_id: wg-12345
60+ users:
61+ - user_id: u-123
62+ role_id: role-123
63+ - user_id: u-456
64+ role_id: role-456
3665
37- # Apply the configuration in test.yaml
38- $ cfctl apply -f test.yaml` ,
66+ # 02. Apply the configuration
67+ cfctl apply -f test.yaml` ,
3968 RunE : func (cmd * cobra.Command , args []string ) error {
4069 filename , _ := cmd .Flags ().GetString ("filename" )
4170 if filename == "" {
4271 return fmt .Errorf ("filename is required (-f flag)" )
4372 }
4473
45- // Read and parse YAML file
74+ // Read YAML file
4675 data , err := os .ReadFile (filename )
4776 if err != nil {
4877 return fmt .Errorf ("failed to read file: %v" , err )
4978 }
5079
51- var resource ResourceSpec
52- if err := yaml .Unmarshal (data , & resource ); err != nil {
53- return fmt .Errorf ("failed to parse YAML: %v" , err )
80+ // Parse all resource specs
81+ resources , err := parseResourceSpecs (data )
82+ if err != nil {
83+ return err
5484 }
5585
56- // Convert spec to parameters
57- var parameters []string
58- for key , value := range resource .Spec {
59- switch v := value .(type ) {
60- case string :
61- parameters = append (parameters , fmt .Sprintf ("%s=%s" , key , v ))
62- case bool , int , float64 :
63- parameters = append (parameters , fmt .Sprintf ("%s=%v" , key , v ))
64- case []interface {}, map [string ]interface {}:
65- // For arrays and maps, convert to JSON string
66- jsonBytes , err := json .Marshal (v )
67- if err != nil {
68- return fmt .Errorf ("failed to marshal parameter %s: %v" , key , err )
69- }
70- parameters = append (parameters , fmt .Sprintf ("%s=%s" , key , string (jsonBytes )))
71- default :
72- // For other complex types, try JSON marshaling
73- jsonBytes , err := json .Marshal (v )
74- if err != nil {
75- return fmt .Errorf ("failed to marshal parameter %s: %v" , key , err )
86+ // Process each resource sequentially
87+ var lastResponse map [string ]interface {}
88+ for i , resource := range resources {
89+ pterm .Info .Printf ("Applying resource %d/%d: %s/%s\n " ,
90+ i + 1 , len (resources ), resource .Service , resource .Resource )
91+
92+ // Convert spec to parameters
93+ parameters := convertSpecToParameters (resource .Spec , lastResponse )
94+
95+ options := & transport.FetchOptions {
96+ Parameters : parameters ,
97+ }
98+
99+ response , err := transport .FetchService (resource .Service , resource .Verb , resource .Resource , options )
100+ if err != nil {
101+ pterm .Error .Printf ("Failed to apply resource %d/%d: %v\n " , i + 1 , len (resources ), err )
102+ return err
103+ }
104+
105+ lastResponse = response
106+ pterm .Success .Printf ("Resource %d/%d applied successfully\n " , i + 1 , len (resources ))
107+ }
108+
109+ return nil
110+ },
111+ }
112+
113+ func convertSpecToParameters (spec map [string ]interface {}, lastResponse map [string ]interface {}) []string {
114+ var parameters []string
115+
116+ for key , value := range spec {
117+ switch v := value .(type ) {
118+ case string :
119+ // Check if value references previous response
120+ if strings .HasPrefix (v , "${" ) && strings .HasSuffix (v , "}" ) {
121+ refPath := strings .Trim (v , "${}" )
122+ if val := getValueFromPath (lastResponse , refPath ); val != "" {
123+ parameters = append (parameters , fmt .Sprintf ("%s=%s" , key , val ))
76124 }
125+ } else {
126+ parameters = append (parameters , fmt .Sprintf ("%s=%s" , key , v ))
127+ }
128+ case []interface {}, map [string ]interface {}:
129+ jsonBytes , err := json .Marshal (v )
130+ if err == nil {
77131 parameters = append (parameters , fmt .Sprintf ("%s=%s" , key , string (jsonBytes )))
78132 }
133+ default :
134+ parameters = append (parameters , fmt .Sprintf ("%s=%v" , key , v ))
79135 }
136+ }
80137
81- options := & transport.FetchOptions {
82- Parameters : parameters ,
83- }
138+ return parameters
139+ }
84140
85- _ , err = transport .FetchService (resource .Service , resource .Verb , resource .Resource , options )
86- if err != nil {
87- pterm .Error .Println (err .Error ())
88- return nil
141+ func getValueFromPath (data map [string ]interface {}, path string ) string {
142+ parts := strings .Split (path , "." )
143+ current := data
144+
145+ for _ , part := range parts {
146+ if v , ok := current [part ]; ok {
147+ switch val := v .(type ) {
148+ case map [string ]interface {}:
149+ current = val
150+ case string :
151+ return val
152+ default :
153+ if str , err := json .Marshal (val ); err == nil {
154+ return string (str )
155+ }
156+ return fmt .Sprintf ("%v" , val )
157+ }
158+ } else {
159+ return ""
89160 }
161+ }
90162
91- pterm .Success .Printf ("Resource %s/%s applied successfully\n " , resource .Service , resource .Resource )
92- return nil
93- },
163+ return ""
94164}
95165
96166func init () {
0 commit comments