generated from kyma-project/template-repository
-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create Audit Log FSM state #344
Merged
Merged
Changes from 29 commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
c05d995
Create Audit Log FSM state
mvshao e1af088
Create new FSM states, fix linter
mvshao a2d1df9
Repair linter
mvshao 425c02f
Repair linter among whole project
mvshao bd3b770
Comment case for testing
mvshao 40aca00
Add Gardenert to scheme
mvshao 45700ed
Change k8s client
mvshao 0715199
Remove gardenere from runtime schema
mvshao c4e4982
Refactor
mvshao c923e98
Pass additional parameter
mvshao b348fda
First phase of fixing tests
mvshao f431a3b
Second phase of fixing tests
mvshao ccad08b
Create generic function
mvshao a884581
Add testdata
mvshao d0c21b7
Third round of fixing controller tests
mvshao abb3486
Create unit tests for Audit Log state
mvshao 57e83ad
Comment tests
mvshao 964b2b1
Add mocks
mvshao 9479ce7
Fix linter errors
mvshao e594864
Fix linter
mvshao d62e17a
Fix linter
mvshao 316a37a
Add unit test for logic
mvshao 9620513
Add unit tests
mvshao 0ee1e98
Add new condition
mvshao 320ddd6
Merge branch 'main' into auditlog-fsm
mvshao a0a0b83
Adjust controller tests
mvshao 0902ae7
Change the type of resons in status, fix the flow
mvshao 477fc94
Update program flow
mvshao cb72a1b
Fix linter
mvshao a207bcf
Apply review sugestiosn
mvshao d6b5669
Trigger GA
mvshao File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,290 @@ | ||
package auditlogging | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
|
||
gardener "github.com/gardener/gardener/pkg/apis/core/v1beta1" | ||
"github.com/pkg/errors" | ||
v12 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/types" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
const ( | ||
auditlogSecretReference = "auditlog-credentials" | ||
auditlogExtensionType = "shoot-auditlog-service" | ||
) | ||
|
||
//go:generate mockery --name=AuditLogging | ||
type AuditLogging interface { | ||
Enable(ctx context.Context, shoot *gardener.Shoot) (bool, error) | ||
} | ||
|
||
//go:generate mockery --name=AuditLogConfigurator | ||
type AuditLogConfigurator interface { | ||
CanEnableAuditLogsForShoot(seedName string) bool | ||
GetPolicyConfigMapName() string | ||
GetSeedObj(ctx context.Context, seedKey types.NamespacedName) (gardener.Seed, error) | ||
UpdateShoot(ctx context.Context, shoot *gardener.Shoot) error | ||
GetConfigFromFile() (data map[string]map[string]AuditLogData, err error) | ||
} | ||
|
||
type AuditLog struct { | ||
AuditLogConfigurator | ||
} | ||
|
||
type auditLogConfig struct { | ||
tenantConfigPath string | ||
policyConfigMapName string | ||
client client.Client | ||
} | ||
|
||
type AuditLogData struct { | ||
TenantID string `json:"tenantID"` | ||
ServiceURL string `json:"serviceURL"` | ||
SecretName string `json:"secretName"` | ||
} | ||
|
||
type AuditlogExtensionConfig struct { | ||
metav1.TypeMeta `json:",inline"` | ||
|
||
// Type is the type of auditlog service provider. | ||
Type string `json:"type"` | ||
// TenantID is the id of the tenant. | ||
TenantID string `json:"tenantID"` | ||
// ServiceURL is the URL of the auditlog service. | ||
ServiceURL string `json:"serviceURL"` | ||
// SecretReferenceName is the name of the reference for the secret containing the auditlog service credentials. | ||
SecretReferenceName string `json:"secretReferenceName"` | ||
} | ||
|
||
func NewAuditLogging(auditLogTenantConfigPath, auditLogPolicyConfigMapName string, k8s client.Client) AuditLogging { | ||
return &AuditLog{ | ||
AuditLogConfigurator: newAuditLogConfigurator(auditLogTenantConfigPath, auditLogPolicyConfigMapName, k8s), | ||
} | ||
} | ||
|
||
func newAuditLogConfigurator(auditLogTenantConfigPath, auditLogPolicyConfigMapName string, k8s client.Client) AuditLogConfigurator { | ||
return &auditLogConfig{ | ||
tenantConfigPath: auditLogTenantConfigPath, | ||
policyConfigMapName: auditLogPolicyConfigMapName, | ||
client: k8s, | ||
} | ||
} | ||
|
||
func (a *auditLogConfig) CanEnableAuditLogsForShoot(seedName string) bool { | ||
return seedName != "" && a.tenantConfigPath != "" | ||
} | ||
|
||
func (a *auditLogConfig) GetPolicyConfigMapName() string { | ||
return a.policyConfigMapName | ||
} | ||
|
||
func (a *auditLogConfig) GetSeedObj(ctx context.Context, seedKey types.NamespacedName) (gardener.Seed, error) { | ||
var seed gardener.Seed | ||
if err := a.client.Get(ctx, seedKey, &seed); err != nil { | ||
return gardener.Seed{}, err | ||
} | ||
return seed, nil | ||
} | ||
|
||
func (al *AuditLog) Enable(ctx context.Context, shoot *gardener.Shoot) (bool, error) { | ||
seedName := getSeedName(*shoot) | ||
|
||
if !al.CanEnableAuditLogsForShoot(seedName) { | ||
return false, errors.New("Seed name on shoot or tenantConfigPath is empty") | ||
} | ||
|
||
auditConfigFromFile, err := al.GetConfigFromFile() | ||
if err != nil { | ||
return false, errors.Wrap(err, "Cannot get Audit Log config from file") | ||
} | ||
|
||
configureAuditPolicy(shoot, al.GetPolicyConfigMapName()) | ||
|
||
seedKey := types.NamespacedName{Name: seedName, Namespace: ""} | ||
seed, err := al.GetSeedObj(ctx, seedKey) | ||
if err != nil { | ||
return false, errors.Wrap(err, "Cannot get Gardener Seed object") | ||
} | ||
|
||
annotated, err := ApplyAuditLogConfig(shoot, auditConfigFromFile, seed.Spec.Provider.Type) | ||
|
||
if err != nil { | ||
return false, errors.Wrap(err, "Error during enabling Audit Logs on shoot: "+shoot.Name) | ||
} | ||
|
||
if annotated { | ||
if err = al.UpdateShoot(ctx, shoot); err != nil { | ||
return false, errors.Wrap(err, "Cannot update shoot") | ||
} | ||
} | ||
|
||
return true, nil | ||
} | ||
|
||
func ApplyAuditLogConfig(shoot *gardener.Shoot, auditConfigFromFile map[string]map[string]AuditLogData, providerType string) (bool, error) { | ||
providerConfig := auditConfigFromFile[providerType] | ||
if providerConfig == nil { | ||
return false, fmt.Errorf("cannot find config for provider %s", providerType) | ||
Disper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
auditID := shoot.Spec.Region | ||
if auditID == "" { | ||
return false, fmt.Errorf("shoot has no region set") | ||
} | ||
|
||
tenant, ok := providerConfig[auditID] | ||
if !ok { | ||
return false, fmt.Errorf("auditlog config for region %s, provider %s is empty", auditID, providerType) | ||
} | ||
|
||
changedExt, err := configureExtension(shoot, tenant) | ||
changedSec := configureSecret(shoot, tenant) | ||
|
||
return changedExt || changedSec, err | ||
} | ||
|
||
func configureExtension(shoot *gardener.Shoot, config AuditLogData) (changed bool, err error) { | ||
changed = false | ||
const ( | ||
extensionKind = "AuditlogConfig" | ||
extensionVersion = "service.auditlog.extensions.gardener.cloud/v1alpha1" | ||
extensionType = "standard" | ||
) | ||
|
||
ext := findExtension(shoot) | ||
if ext != nil { | ||
cfg := AuditlogExtensionConfig{} | ||
err = json.Unmarshal(ext.ProviderConfig.Raw, &cfg) | ||
if err != nil { | ||
return | ||
} | ||
|
||
if cfg.Kind == extensionKind && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about extracting this check to some separate method like |
||
cfg.Type == extensionType && | ||
cfg.TenantID == config.TenantID && | ||
cfg.ServiceURL == config.ServiceURL && | ||
cfg.SecretReferenceName == auditlogSecretReference { | ||
return false, nil | ||
} | ||
} else { | ||
shoot.Spec.Extensions = append(shoot.Spec.Extensions, gardener.Extension{ | ||
Type: auditlogExtensionType, | ||
}) | ||
ext = &shoot.Spec.Extensions[len(shoot.Spec.Extensions)-1] | ||
} | ||
|
||
changed = true | ||
|
||
cfg := AuditlogExtensionConfig{ | ||
TypeMeta: metav1.TypeMeta{ | ||
Kind: extensionKind, | ||
APIVersion: extensionVersion, | ||
}, | ||
Type: extensionType, | ||
TenantID: config.TenantID, | ||
ServiceURL: config.ServiceURL, | ||
SecretReferenceName: auditlogSecretReference, | ||
} | ||
|
||
ext.ProviderConfig = &runtime.RawExtension{} | ||
ext.ProviderConfig.Raw, err = json.Marshal(cfg) | ||
|
||
return | ||
} | ||
|
||
func findExtension(shoot *gardener.Shoot) *gardener.Extension { | ||
for i, e := range shoot.Spec.Extensions { | ||
if e.Type == auditlogExtensionType { | ||
return &shoot.Spec.Extensions[i] | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func findSecret(shoot *gardener.Shoot) *gardener.NamedResourceReference { | ||
for i, e := range shoot.Spec.Resources { | ||
if e.Name == auditlogSecretReference { | ||
return &shoot.Spec.Resources[i] | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func configureSecret(shoot *gardener.Shoot, config AuditLogData) (changed bool) { | ||
changed = false | ||
|
||
sec := findSecret(shoot) | ||
if sec != nil { | ||
if sec.Name == auditlogSecretReference && | ||
sec.ResourceRef.APIVersion == "v1" && | ||
sec.ResourceRef.Kind == "Secret" && | ||
sec.ResourceRef.Name == config.SecretName { | ||
return | ||
} | ||
} else { | ||
shoot.Spec.Resources = append(shoot.Spec.Resources, gardener.NamedResourceReference{}) | ||
sec = &shoot.Spec.Resources[len(shoot.Spec.Resources)-1] | ||
} | ||
|
||
changed = true | ||
|
||
sec.Name = auditlogSecretReference | ||
sec.ResourceRef.APIVersion = "v1" | ||
sec.ResourceRef.Kind = "Secret" | ||
sec.ResourceRef.Name = config.SecretName | ||
|
||
return | ||
} | ||
|
||
func (a *auditLogConfig) GetConfigFromFile() (data map[string]map[string]AuditLogData, err error) { | ||
file, err := os.Open(a.tenantConfigPath) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
defer func(file *os.File) { | ||
_ = file.Close() | ||
}(file) | ||
|
||
if err := json.NewDecoder(file).Decode(&data); err != nil { | ||
return nil, err | ||
} | ||
return data, nil | ||
} | ||
|
||
func getSeedName(shoot gardener.Shoot) string { | ||
if shoot.Spec.SeedName != nil { | ||
return *shoot.Spec.SeedName | ||
} | ||
return "" | ||
} | ||
|
||
func configureAuditPolicy(shoot *gardener.Shoot, policyConfigMapName string) { | ||
if shoot.Spec.Kubernetes.KubeAPIServer == nil { | ||
shoot.Spec.Kubernetes.KubeAPIServer = &gardener.KubeAPIServerConfig{} | ||
} | ||
|
||
shoot.Spec.Kubernetes.KubeAPIServer.AuditConfig = newAuditPolicyConfig(policyConfigMapName) | ||
} | ||
|
||
func newAuditPolicyConfig(policyConfigMapName string) *gardener.AuditConfig { | ||
return &gardener.AuditConfig{ | ||
AuditPolicy: &gardener.AuditPolicy{ | ||
ConfigMapRef: &v12.ObjectReference{Name: policyConfigMapName}, | ||
}, | ||
} | ||
} | ||
|
||
func (a *auditLogConfig) UpdateShoot(ctx context.Context, shoot *gardener.Shoot) error { | ||
return a.client.Update(ctx, shoot) | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't find the usage of it - was this addition intentional?