|
Ce document a été traduit à l'aide d'une technologie de traduction automatique. Bien que nous nous efforcions de fournir des traductions exactes, nous ne fournissons aucune garantie quant à l'exhaustivité, l'exactitude ou la fiabilité du contenu traduit. En cas de divergence, la version originale anglaise prévaut et fait foi. |
|
Il s'agit d'une documentation non publiée pour Admission Controller 1.34-dev. |
Définir les paramètres de stratégie
Tout d’abord, vous devez définir la structure qui contient les paramètres de stratégie.
Vous le faites en modifiant le code dans le fichier settings.go (à partir de votre version locale du modèle de stratégie Go).
Vous devez ajouter deux lignes supplémentaires à la section import, changer la structure Settings, et ajouter la structure RegularExpression.
Cela devrait correspondre au code suivant :
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
}
|
Comme |
Vous utilisez le paquet regexp pour gérer les objets d’expressions régulières et le type mapset.Set pour stocker la liste des étiquettes refusées.
Puisque regexp.Regexp ne gère pas la désérialisation, vous devez définir des fonctions personnalisées pour gérer la sérialisation et la désérialisation des expressions régulières :
// 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
}
De plus, vous avez besoin de la méthode UnmarshalJSON pour gérer la désérialisation de la structure 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
}
Création d’instances de Settings
Une stratégie Admission Controller expose deux fonctions différentes qui reçoivent les paramètres de stratégie en entrée :
-
validate: Utilisez cette fonction lorsque l’objet Kubernetes nécessite une validation par la stratégie. Les paramètres font partie d’un objetValidationRequest. -
validate_settings: Appelez cette fonction lorsque la politique est chargée pour la première fois par Admission Controller. La fonction reçoit les paramètres de stratégie en entrée et vérifie leur validité.
Vous devez créer une fonction d’assistance qui crée un objet Settings à partir de la charge utile JSON :
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
}
|
Tous les La norme WebAssembly ne couvre pas encore les threads. Voir la proposition officielle pour plus de détails. |
Mise en œuvre de la validation Settings
Toutes les politiques Admission Controller doivent mettre en œuvre la validation des paramètres.
Vous le faites en ajoutant une méthode Valid aux instances Settings :
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
}
La méthode Valid garantit qu’aucun label "refusé" ne fait également partie de la carte "contraint".
L’utilisation de la méthode Intersect fournie par mapset.Set simplifie la vérification.
|
L’invocation de la méthode |
Enfin, vous avez besoin de la fonction validateSettings, fournie par l’échafaudage, pour changer afin qu’elle ressemble à ceci :
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)))
}
Vous pouvez voir que la fonction tire parti des fonctions utilitaires fournies par le SDK de Admission Controller.
Tester le code des paramètres
Il est important d’avoir une bonne couverture de tests du code que vous écrivez.
Le code que vous utilisez, provenant de l’échafaudage, est livré avec une série de tests unitaires définis dans le fichier settings_test.go.
Vous devez changer le contenu de ce fichier pour refléter le nouveau comportement de la classe Settings.
Incluez les paquets Go que vous utilisez :
import (
"testing"
"encoding/json"
kubewarden_protocol "github.com/kubewarden/policy-sdk-go/protocol"
)
Vous pouvez commencer par écrire un test unitaire qui garantit que vous pouvez assigner une instance Settings à partir d’un objet ValidationRequest :
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())
}
}
Ensuite, vous avez besoin d’un test qui vérifie qu’une instance Settings n’est pas générée lorsque l’utilisateur fournit une expression régulière incorrecte :
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")
}
}
Vous pouvez maintenant définir un test qui vérifie le comportement du point d’entrée validate_settings.
Vous examinez l’objet SettingsValidationResponse retourné par votre fonction validateSettings :
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)
}
}
Enfin, vous écrivez deux autres tests pour vérifier que la fonction validateSettings rejette les paramètres invalides avec les bons messages :
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)
}
}
Vous pouvez maintenant exécuter les tests que vous avez définis jusqu’à présent en utilisant la commande suivante :
go test -v settings.go settings_test.go
Tous les tests réussiront avec la sortie suivante :
=== 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
Vous pouvez maintenant implémenter le code de validation réel dans la section suivante.