|
本文档采用自动化机器翻译技术翻译。 尽管我们力求提供准确的译文,但不对翻译内容的完整性、准确性或可靠性作出任何保证。 若出现任何内容不一致情况,请以原始 英文 版本为准,且原始英文版本为权威文本。 |
|
这是尚未发布的文档。 Admission Controller 1.34-dev. |
定义策略设置
首先,您需要定义一个保存策略设置的结构。
您可以通过修改`settings.go`文件中的代码来实现(来自您本地的Go策略模板)。 您需要在`import`部分添加两行额外的代码,修改`Settings`结构,并添加`RegularExpression`结构。
它应与以下代码匹配:
import (
"encoding/json"
"fmt"
"regexp"
mapset "github.com/deckarep/golang-set/v2"
kubewarden "github.com/kubewarden/policy-sdk-go"
kubewarden_protocol "github.com/kubewarden/policy-sdk-go/protocol"
)
type Settings struct {
DeniedLabels mapset.Set[string] `+json:"denied_labels"+`
ConstrainedLabels map[string]*RegularExpression `+json:"constrained_labels"+`
}
type RegularExpression struct {
*regexp.Regexp
}
|
在`settings.go`中定义的`Settings`结构中不再需要`DeniedNames`。 由于`DeniedNames`不再被定义,您还应该在`settings.go`中删除函数`IsNameDefined`。 您还应该在`settings_test.go`、`TestIsNameDenied`中去除引用它的函数。 |
您正在使用`regexp`软件包来处理正则表达式对象,并使用https://github.com/deckarep/golang-set[mapset.Set]类型来存储被拒绝标签的列表。
由于`regexp.Regexp`不处理解组,您需要定义自定义函数来处理正则表达式的编组和解组:
// UnmarshalText satisfies the encoding.TextMarshaler interface,
// also used by json.Unmarshal.
func (r *RegularExpression) UnmarshalText(text []byte) error {
nativeRegExp, err := regexp.Compile(string(text))
if err != nil {
return err
}
r.Regexp = nativeRegExp
return nil
}
// MarshalText satisfies the encoding.TextMarshaler interface,
// also used by json.Marshal.
func (r *RegularExpression) MarshalText() ([]byte, error) {
if r.Regexp != nil {
return []byte(r.Regexp.String()), nil
}
return nil, nil
}
此外,您需要`UnmarshalJSON`方法来处理`Settings`结构的反序列化:
func (s *Settings) UnmarshalJSON(data []byte) error {
rawSettings := struct {
DeniedLabels []string `+json:"denied_labels"+`
ConstrainedLabels map[string]*RegularExpression `+json:"constrained_labels"+`
}{}
err := json.Unmarshal(data, &rawSettings)
if err != nil {
return err
}
s.DeniedLabels = mapset.NewThreadUnsafeSet[string](rawSettings.DeniedLabels...)
s.ConstrainedLabels = rawSettings.ConstrainedLabels
return nil
}
构建`Settings`实例
一个Admission Controller策略暴露两个不同的函数,这些函数接收策略设置作为输入:
-
validate:当Kubernetes对象需要通过策略进行验证时,请使用此函数。 这些设置是https://pkg.go.dev/github.com/kubewarden/policy-sdk-go@v0.2.1/protocol#ValidationRequest[ValidationRequest]对象的一部分。 -
validate_settings:当策略首次被Admission Controller加载时,请调用此函数。 该函数接收策略设置作为输入并检查有效性。
您需要创建一个辅助函数,从JSON有效负载开始创建`Settings`对象:
func NewSettingsFromValidationReq(validationReq *kubewarden_protocol.ValidationRequest) (Settings, error) {
settings := Settings{}
err := json.Unmarshal(validationReq.Settings, &settings)
if err != nil {
return Settings{}, err
}
return settings, nil
}
|
所有的`mapset.Set`对象都是使用https://pkg.go.dev/github.com/deckarep/golang-set?utm_source=godoc#NewThreadUnsafeSet[线程不安全变体]创建的。 WebAssembly代码在单个线程中执行,因此没有并发问题。 WebAssembly标准尚未涵盖线程。 有关更多详细信息,请参见https://github.com/WebAssembly/threads[官方提案]。 |
实现`Settings`验证
所有Admission Controller策略必须实现设置验证。
您可以通过向`Settings`实例添加`Valid`方法来实现这一点:
func (s *Settings) Valid() (bool, error) {
constrainedLabels := mapset.NewThreadUnsafeSet[string]()
for label := range s.ConstrainedLabels {
constrainedLabels.Add(label)
}
constrainedAndDenied := constrainedLabels.Intersect(s.DeniedLabels)
if constrainedAndDenied.Cardinality() != 0 {
return false,
fmt.Errorf("These labels cannot be constrained and denied at the same time: %v", constrainedAndDenied)
}
return true, nil
}
`Valid`方法确保没有“被拒绝”标签也属于“受限”映射。 使用由`mapset.Set`提供的`Intersect`方法简化了检查。
|
`Valid`方法调用是在已经实例化的`Setting`对象上进行的。 这意味着用户提供的正则表达式的验证已经在`Settings`解组器中进行。 |
最后,您需要将脚手架提供的`validateSettings`函数更改为如下所示:
func validateSettings(payload []byte) ([]byte, error) {
settings := Settings{}
err := json.Unmarshal(payload, &settings)
if err != nil {
return kubewarden.RejectSettings(
kubewarden.Message(fmt.Sprintf("Provided settings are not valid: %v", err)))
}
valid, err := settings.Valid()
if valid {
return kubewarden.AcceptSettings()
}
return kubewarden.RejectSettings(
kubewarden.Message(fmt.Sprintf("Provided settings are not valid: %v", err)))
}
您可以看到,该函数利用了由https://github.com/kubewarden/policy-sdk-go[Admission Controller的SDK提供的辅助函数。]
测试设置代码
确保您编写的代码有良好的测试覆盖率是很重要的。 您使用的来自脚手架的代码附带了一系列在`settings_test.go`文件中定义的单元测试。
您必须更改此文件的内容以反映`Settings`类的新行为。
包含您正在使用的Go软件包:
import (
"testing"
"encoding/json"
kubewarden_protocol "github.com/kubewarden/policy-sdk-go/protocol"
)
您可以通过编写一个单元测试开始,确保您可以从`ValidationRequest`对象分配一个`Settings`实例:
func TestParseValidSettings(t *testing.T) {
settingsJSON := []byte(`
{
"denied_labels": [ "foo", "bar" ],
"constrained_labels": {
"cost-center": "cc-\\d+"
}
}`)
settings := Settings{}
err := json.Unmarshal(settingsJSON, &settings)
if err != nil {
t.Errorf("Unexpected error %+v", err)
}
expected_denied_labels := []string{"foo", "bar"}
for _, exp := range expected_denied_labels {
if !settings.DeniedLabels.Contains(exp) {
t.Errorf("Missing value %s", exp)
}
}
re, found := settings.ConstrainedLabels["cost-center"]
if !found {
t.Error("Didn't find the expected constrained label")
}
expected_regexp := `+cc-\d++`
if re.String() != expected_regexp {
t.Errorf("Expected regexp to be %v - got %v instead",
expected_regexp, re.String())
}
}
接下来,您需要一个测试,检查当用户提供一个损坏的正则表达式时不会生成`Settings`实例:
func TestParseSettingsWithInvalidRegexp(t *testing.T) {
settingsJSON := []byte(`
{
"denied_labels": [ "foo", "bar" ],
"constrained_labels": {
"cost-center": "cc-[a+"
}
}`)
err := json.Unmarshal(settingsJSON, &Settings{})
if err == nil {
t.Errorf("Didn't get expected error")
}
}
现在,您可以定义一个测试来检查`validate_settings`入口点的行为。
您查看由您的`validateSettings`函数返回的`SettingsValidationResponse`对象:
func TestDetectValidSettings(t *testing.T) {
settingsJSON := []byte(`
{
"denied_labels": [ "foo", "bar" ],
"constrained_labels": {
"cost-center": "cc-\\d+"
}
}`)
responsePayload, err := validateSettings(settingsJSON)
if err != nil {
t.Errorf("Unexpected error %+v", err)
}
var response kubewarden_protocol.SettingsValidationResponse
if err := json.Unmarshal(responsePayload, &response); err != nil {
t.Errorf("Unexpected error: %+v", err)
}
if !response.Valid {
t.Errorf("Expected settings to be valid: %s", *response.Message)
}
}
最后,您编写两个测试来检查`validateSettings`函数是否以正确的消息拒绝无效设置:
func TestDetectNotValidSettingsDueToBrokenRegexp(t *testing.T) {
settingsJSON := []byte(`
{
"denied_labels": [ "foo", "bar" ],
"constrained_labels": {
"cost-center": "cc-[a+"
}
}
`)
responsePayload, err := validateSettings(settingsJSON)
if err != nil {
t.Errorf("Unexpected error %+v", err)
}
var response kubewarden_protocol.SettingsValidationResponse
if err := json.Unmarshal(responsePayload, &response); err != nil {
t.Errorf("Unexpected error: %+v", err)
}
if response.Valid {
t.Error("Expected settings to not be valid")
}
if *response.Message != "Provided settings are not valid: error parsing regexp: missing closing ]: `+[a++`" {
t.Errorf("Unexpected validation error message: %s", *response.Message)
}
}
func TestDetectNotValidSettingsDueToConflictingLabels(t *testing.T) {
settingsJSON := []byte(`
{
"denied_labels": [ "foo", "bar", "cost-center" ],
"constrained_labels": {
"cost-center": ".*"
}
}`)
responsePayload, err := validateSettings(settingsJSON)
if err != nil {
t.Errorf("Unexpected error %+v", err)
}
var response kubewarden_protocol.SettingsValidationResponse
if err := json.Unmarshal(responsePayload, &response); err != nil {
t.Errorf("Unexpected error: %+v", err)
}
if response.Valid {
t.Error("Expected settings to not be valid")
}
if *response.Message != "Provided settings are not valid: These labels cannot be constrained and denied at the same time: Set{cost-center}" {
t.Errorf("Unexpected validation error message: %s", *response.Message)
}
}
现在,您可以使用以下命令运行到目前为止定义的测试:
go test -v settings.go settings_test.go
所有测试将通过,输出如下:
=== RUN TestParseValidSettings
--- PASS: TestParseValidSettings (0.00s)
=== RUN TestParseSettingsWithInvalidRegexp
--- PASS: TestParseSettingsWithInvalidRegexp (0.00s)
=== RUN TestDetectValidSettings
--- PASS: TestDetectValidSettings (0.00s)
=== RUN TestDetectNotValidSettingsDueToBrokenRegexp
--- PASS: TestDetectNotValidSettingsDueToBrokenRegexp (0.00s)
=== RUN TestDetectNotValidSettingsDueToConflictingLabels
--- PASS: TestDetectNotValidSettingsDueToConflictingLabels (0.00s)
PASS
ok command-line-arguments 0.002s
您现在可以在下一部分实现实际的验证代码。