@@ -4,9 +4,11 @@ import (
4
4
"bytes"
5
5
"fmt"
6
6
"os"
7
+ "path/filepath"
7
8
"reflect"
8
9
"text/template"
9
10
11
+ "github.com/santhosh-tekuri/jsonschema/v6"
10
12
"gopkg.in/yaml.v3"
11
13
)
12
14
@@ -89,6 +91,62 @@ func mergeVariables(base, override Variables) Variables {
89
91
return base
90
92
}
91
93
94
+ // Needed to convert Variables to map[string]interface{} for jsonschema validation
95
+ // see: https://github.com/santhosh-tekuri/jsonschema/blob/boon/schema.go#L124
96
+ func convertToInterface (variables Variables ) map [string ]any {
97
+ m := map [string ]any {}
98
+ for k , v := range variables {
99
+ if subMap , ok := v .(Variables ); ok {
100
+ m [k ] = convertToInterface (subMap )
101
+ } else {
102
+ m [k ] = v
103
+ }
104
+ }
105
+ return m
106
+ }
107
+
108
+ func (cp * configProviderImpl ) loadSchema () (any , error ) {
109
+ schemaPath := cp .schema
110
+ if ! filepath .IsAbs (schemaPath ) {
111
+ schemaPath = filepath .Join (filepath .Dir (cp .config ), schemaPath )
112
+ }
113
+ reader , err := os .Open (schemaPath )
114
+ if err != nil {
115
+ return nil , fmt .Errorf ("failed to open schema file: %v" , err )
116
+ }
117
+
118
+ schema , err := jsonschema .UnmarshalJSON (reader )
119
+ if err != nil {
120
+ return nil , fmt .Errorf ("failed to unmarshal schema: %v" , err )
121
+ }
122
+
123
+ return schema , nil
124
+ }
125
+
126
+ func (cp * configProviderImpl ) validateSchema (variables Variables ) error {
127
+ c := jsonschema .NewCompiler ()
128
+
129
+ schema , err := cp .loadSchema ()
130
+ if err != nil {
131
+ return fmt .Errorf ("failed to load schema: %v" , err )
132
+ }
133
+
134
+ err = c .AddResource (cp .schema , schema )
135
+ if err != nil {
136
+ return fmt .Errorf ("failed to add schema resource: %v" , err )
137
+ }
138
+ sch , err := c .Compile (cp .schema )
139
+ if err != nil {
140
+ return fmt .Errorf ("failed to compile schema: %v" , err )
141
+ }
142
+
143
+ err = sch .Validate (convertToInterface (variables ))
144
+ if err != nil {
145
+ return fmt .Errorf ("failed to validate schema: %v" , err )
146
+ }
147
+ return nil
148
+ }
149
+
92
150
func (cp * configProviderImpl ) GetVariables (cloud , deployEnv , region string , configReplacements * ConfigReplacements ) (Variables , error ) {
93
151
variables , err := cp .GetDeployEnvVariables (cloud , deployEnv , configReplacements )
94
152
if err != nil {
@@ -102,6 +160,11 @@ func (cp *configProviderImpl) GetVariables(cloud, deployEnv, region string, conf
102
160
}
103
161
mergeVariables (variables , regionOverrides )
104
162
163
+ // validate schema
164
+ err = cp .validateSchema (variables )
165
+ if err != nil {
166
+ return nil , err
167
+ }
105
168
return variables , nil
106
169
}
107
170
@@ -117,6 +180,10 @@ func (cp *configProviderImpl) Validate(cloud, deployEnv string) error {
117
180
if ok := config .HasDeployEnv (cloud , deployEnv ); ! ok {
118
181
return fmt .Errorf ("the deployment env %s is not found under cloud %s" , deployEnv , cloud )
119
182
}
183
+
184
+ if ! config .HasSchema () {
185
+ return fmt .Errorf ("$schema not found in config" )
186
+ }
120
187
return nil
121
188
}
122
189
@@ -135,6 +202,8 @@ func (cp *configProviderImpl) GetDeployEnvVariables(cloud, deployEnv string, con
135
202
mergeVariables (variables , config .GetCloudOverrides (cloud ))
136
203
mergeVariables (variables , config .GetDeployEnvOverrides (cloud , deployEnv ))
137
204
205
+ cp .schema = config .GetSchema ()
206
+
138
207
return variables , nil
139
208
}
140
209
0 commit comments