|
Dieses Dokument wurde mithilfe automatisierter maschineller Übersetzungstechnologie übersetzt. Wir bemühen uns um korrekte Übersetzungen, übernehmen jedoch keine Gewähr für die Vollständigkeit, Richtigkeit oder Zuverlässigkeit der übersetzten Inhalte. Im Falle von Abweichungen ist die englische Originalversion maßgebend und stellt den verbindlichen Text dar. |
|
Dies ist eine unveröffentlichte Dokumentation für Admission Controller 1.34-dev. |
Festlegung von Richtlinieneinstellungen
Zuerst müssen Sie die Struktur definieren, die die Richtlinieneinstellungen enthält.
Sie tun dies, indem Sie den Code in der settings.go-Datei (aus Ihrer lokalen Version der Go-Richtlinienevorlage) ändern.
Sie müssen zwei zusätzliche Zeilen zum import-Abschnitt hinzufügen, die Settings-Struktur ändern und die RegularExpression-Struktur hinzufügen.
Es sollte dem folgenden Code entsprechen:
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
}
|
Da |
Sie verwenden das regexp-Paket, um Objekte regulärer Ausdrücke zu verwalten, und den mapset.Set-Typ, um die Liste der abgelehnten Labels zu speichern.
Da regexp.Regexp die Deserialisierung nicht behandelt, müssen Sie benutzerdefinierte Funktionen definieren, um das Marshaling und Unmarshaling von regulären Ausdrücken zu behandeln:
// 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
}
Außerdem benötigen Sie die UnmarshalJSON-Methode, um die Deserialisierung der Settings-Struktur zu behandeln:
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
}
Erstellen von Settings-Instanzen
Eine Admission Controller-Richtlinie bietet zwei verschiedene Funktionen, die die Richtlinieneinstellungen als Eingabe erhalten:
-
validate: Verwenden Sie diese Funktion, wenn das Kubernetes-Objekt eine Validierung durch die Richtlinie erfordert. Die Einstellungen sind Teil einesValidationRequest-Objekts. -
validate_settings: Rufen Sie diese Funktion auf, wenn die Richtlinie erstmals von Admission Controller geladen wird. Die Funktion erhält die Richtlinieneinstellungen als Eingabe und überprüft die Gültigkeit.
Sie müssen eine Hilfsfunktion erstellen, die ein Settings-Objekt aus dem JSON-Payload erstellt:
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
}
|
Alle Der WebAssembly-Standard behandelt noch keine Threads. Siehe den offiziellen Vorschlag für weitere Details. |
Implementierung der Settings-Validierung
Alle Admission Controller-Richtlinien müssen die Einstellungsvalidierung implementieren.
Sie tun dies, indem Sie eine Valid-Methode zu den Settings-Instanzen hinzufügen:
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
}
Die Valid-Methode stellt sicher, dass kein "verweigert"-Label auch Teil der "eingeschränkten" Map ist.
Die Verwendung der Intersect-Methode, die von mapset.Set bereitgestellt wird, vereinfacht die Überprüfung.
|
Der Aufruf der |
Schließlich benötigen Sie die validateSettings-Funktion, die vom Gerüst bereitgestellt wird, um so auszusehen:
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)))
}
Sie können sehen, dass die Funktion die Hilfsfunktionen nutzt, die von Admission Controller’s SDK bereitgestellt werden.
Testen des Einstellungs-Codes
Es ist wichtig, eine gute Testabdeckung für den Code zu haben, den Sie schreiben.
Der Code, den Sie verwenden, stammt aus dem Gerüst und enthält eine Reihe von Unit-Tests, die in der settings_test.go-Datei definiert sind.
Sie müssen den Inhalt dieser Datei ändern, um das neue Verhalten der Settings-Klasse widerzuspiegeln.
Fügen Sie die Go-Pakete hinzu, die Sie verwenden:
import (
"testing"
"encoding/json"
kubewarden_protocol "github.com/kubewarden/policy-sdk-go/protocol"
)
Sie können damit beginnen, einen Unit-Test zu schreiben, der sicherstellt, dass Sie eine Settings-Instanz von einem ValidationRequest-Objekt zuweisen können:
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())
}
}
Als Nächstes benötigen Sie einen Test, der überprüft, dass eine Settings-Instanz nicht generiert wird, wenn der Benutzer einen fehlerhaften regulären Ausdruck bereitstellt:
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")
}
}
Jetzt können Sie einen Test definieren, der das Verhalten des validate_settings-Einstiegspunkts überprüft.
Sie betrachten das SettingsValidationResponse-Objekt, das von Ihrer validateSettings-Funktion zurückgegeben wird:
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)
}
}
Schließlich schreiben Sie zwei weitere Tests, um zu überprüfen, ob die validateSettings-Funktion ungültige Einstellungen mit den richtigen Nachrichten ablehnt:
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)
}
}
Jetzt können Sie die Tests ausführen, die Sie bisher definiert haben, indem Sie den folgenden Befehl verwenden:
go test -v settings.go settings_test.go
Alle Tests werden mit der folgenden Ausgabe bestehen:
=== 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
Sie können jetzt den tatsächlichen Validierungscode im nächsten Abschnitt implementieren.