如何在非集群环境下验证OpenAPI格式YAML的字段键与值?
解决YAML字段键验证问题(集群外)
核心问题
Go标准库的json.Unmarshal和常用YAML库默认会忽略未知字段,所以用户输入不存在的字段(比如name1代替name)时不会触发错误。要实现字段键的合法性验证,需要开启解析器的"严格模式",让其遇到未知字段时直接返回错误。
方法1:直接用yaml.v3开启严格解析
如果不需要转JSON,直接使用gopkg.in/yaml.v3解析YAML,通过配置解码器开启未知字段检测:
import ( "bytes" "fmt" "os" "gopkg.in/yaml.v3" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type Test_manifest struct { metav1.TypeMeta `yaml:",inline"` metav1.ObjectMeta `yaml:"metadata"` Spec spec `yaml:"spec"` } type spec struct { Policies []string `yaml:"policies"` Resources resources `yaml:"resources"` Results []results `yaml:"results"` Variables variables `yaml:"variables"` } // 需提前定义resources、results、variables结构体 func main() { yamlFile, err := os.ReadFile("cmd/cli/kubectl-kyverno/test/test.yaml") if err != nil { fmt.Printf("Error: failed to read file %v", err) return } var tests Test_manifest decoder := yaml.NewDecoder(bytes.NewReader(yamlFile)) decoder.KnownFields(true) // 开启严格模式,检测未知字段 if err := decoder.Decode(&tests); err != nil { fmt.Printf("Invalid YAML: %v", err) return } // 后续值验证逻辑 if tests.APIVersion == "" { fmt.Println("Error: apiVersion is required") } if tests.Kind != "KyvernoTest" { fmt.Printf("Error: kind must be `KyvernoTest`, got %s", tests.Kind) } }
方法2:转JSON后开启严格解析
如果必须保留转JSON的流程,可使用json.Decoder的DisallowUnknownFields()方法实现字段验证:
import ( "bytes" "encoding/json" "fmt" "io/ioutil" "gopkg.in/yaml.v3" ) func main() { test := "cmd/cli/kubectl-kyverno/test/test.yaml" yamlFile, err := ioutil.ReadFile(test) if err != nil { fmt.Printf("Error: failed to read file %v", err) return } // YAML转JSON var rawJSON interface{} if err := yaml.Unmarshal(yamlFile, &rawJSON); err != nil { fmt.Printf("Failed to parse YAML: %v", err) return } policyBytes, err := json.Marshal(rawJSON) if err != nil { fmt.Printf("Failed to convert to JSON: %v", err) return } tests := &kyvernov1.Test_manifest{} decoder := json.NewDecoder(bytes.NewReader(policyBytes)) decoder.DisallowUnknownFields() // 禁止解析未知字段 if err := decoder.Decode(tests); err != nil { fmt.Printf("Invalid fields found: %v", err) return } // 后续值验证逻辑 if tests.TypeMeta.APIVersion == "" { fmt.Println("Error: apiVersion not found") } if tests.TypeMeta.Kind != "KyvernoTest" { fmt.Printf("Error: kind is not `KyvernoTest`, got %s", tests.TypeMeta.Kind) } }
关键注意点
- 确保结构体的字段标签(
yaml:"xxx"或json:"xxx")与YAML/JSON字段名完全对应,否则会被误判为未知字段。 - 对于
metav1.TypeMeta这类内嵌结构体,需添加yaml:",inline"标签保证解析正确。 - 两种方案均无需连接K8s集群,完全在集群外执行验证。
内容的提问来源于stack exchange,提问作者Prateek Nandle
相关产品推荐
相关产品推荐

