この文書は自動機械翻訳技術を使用して翻訳されています。 正確な翻訳を提供するように努めておりますが、翻訳された内容の完全性、正確性、信頼性については一切保証いたしません。 相違がある場合は、元の英語版 英語 が優先され、正式なテキストとなります。

これは未公開の文書です Admission Controller 1.34-dev.

ポリシー設定の定義

まず、ポリシー設定を保持する構造を定義する必要があります。

これは、`settings.go`ファイルのコードを修正することで行います(ローカルのGoポリシーテンプレートから)。 `import`セクションに2行追加し、`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
}

`DeniedNames`は、`settings.go`で定義された`Settings`構造ではもはや必要ありません。

DeniedNames`がもはや定義されていないため、`IsNameDefined`の`settings.go`関数も削除する必要があります。 `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
}

また、`Settings`構造のデシリアライズを処理するために`UnmarshalJSON`メソッドが必要です:

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ポリシーは、ポリシー設定を入力として受け取る2つの異なる関数を公開します:

  • 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 関数が正しいメッセージで無効な設定を拒否することを確認するために、さらに2つのテストを書きます:

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

次のセクションで実際のバリデーションコードを実装できます。