Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions integration-tests/backend/.golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
version: "2"
run:
go: "1.23"
linters:
enable:
- copyloopvar
- errcheck
- govet
- ineffassign
- staticcheck
- unused
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- staticcheck
text: "QF1003:"
- linters:
- staticcheck
text: "QF1008:"
formatters:
enable:
- gofmt
17 changes: 17 additions & 0 deletions integration-tests/backend/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
GOLANGCI_LINT_VERSION = v2.8.0

##@ Development
.PHONY: prereqs
prereqs:
@echo "### Test if prerequisites are met, and installing missing dependencies"
test -f ./bin/golangci-lint-${GOLANGCI_LINT_VERSION} || ( \
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s ${GOLANGCI_LINT_VERSION} \
&& mv ./bin/golangci-lint ./bin/golangci-lint-${GOLANGCI_LINT_VERSION})

fmt: ## Run go fmt against code.
go fmt ./...

.PHONY: lint
lint: prereqs ## Run linter (golangci-lint)
@echo "### Linting code"
./bin/golangci-lint-${GOLANGCI_LINT_VERSION} --config .golangci.yml run --timeout 15m ./...
13 changes: 13 additions & 0 deletions integration-tests/backend/OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
approvers:
- memodi
- mffiedler
- Amoghrd
- oliver-smakal
- kapjain-rh

reviewers:
- memodi
- mffiedler
- Amoghrd
- oliver-smakal
- kapjain-rh
44 changes: 44 additions & 0 deletions integration-tests/backend/alerts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package e2etests

import (
"context"
"encoding/json"
"fmt"
"time"

exutil "github.com/openshift/origin/test/extended/util"
compat_otp "github.com/openshift/origin/test/extended/util/compat_otp"
"k8s.io/apimachinery/pkg/util/wait"
)

func getConfiguredAlertRules(oc *exutil.CLI, ruleName string, namespace string) (string, error) {
return oc.AsAdmin().WithoutNamespace().Run("get").Args("prometheusrules", ruleName, "-o=jsonpath='{.spec.groups[*].rules[*].alert}'", "-n", namespace).Output()
}

func getAlertStatus(oc *exutil.CLI, alertName string) (map[string]interface{}, error) {
alertOut, err := oc.AsAdmin().WithoutNamespace().Run("exec").Args("-n", "openshift-monitoring", "alertmanager-main-0", "--", "amtool", "--alertmanager.url", "http://localhost:9093", "alert", "query", alertName, "-o", "json").Output()
if err != nil {
return make(map[string]interface{}), err
}
var alertStatus []interface{}
_ = json.Unmarshal([]byte(alertOut), &alertStatus)

if len(alertStatus) == 0 {
return make(map[string]interface{}), nil
}
return alertStatus[0].(map[string]interface{}), nil
Comment thread
oliver-smakal marked this conversation as resolved.
}

func waitForAlertToBeActive(oc *exutil.CLI, alertName string) {
err := wait.PollUntilContextTimeout(context.Background(), 10*time.Second, 900*time.Second, false, func(context.Context) (done bool, err error) {
alertStatus, err := getAlertStatus(oc, alertName)
if err != nil {
return false, err
}
if len(alertStatus) == 0 {
return false, nil
}
return alertStatus["status"].(map[string]interface{})["state"] == "active", nil
Comment thread
oliver-smakal marked this conversation as resolved.
})
compat_otp.AssertWaitPollNoErr(err, fmt.Sprintf("%s Alert did not become active", alertName))
}
184 changes: 184 additions & 0 deletions integration-tests/backend/aws_sts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package e2etests

import (
"context"
"fmt"
"os"
"path/filepath"

"github.com/aws/aws-sdk-go-v2/aws"
awsConfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/aws/aws-sdk-go-v2/service/sts"
g "github.com/onsi/ginkgo/v2"
o "github.com/onsi/gomega"
exutil "github.com/openshift/origin/test/extended/util"
e2e "k8s.io/kubernetes/test/e2e/framework"
)

// Check if credentials exist for STS clusters
func checkAWSCredentials() bool {
// set AWS_SHARED_CREDENTIALS_FILE from CLUSTER_PROFILE_DIR as the first priority"
prowConfigDir, present := os.LookupEnv("CLUSTER_PROFILE_DIR")
if present {
awsCredFile := filepath.Join(prowConfigDir, ".awscred")
if _, err := os.Stat(awsCredFile); err == nil {
err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", awsCredFile)
if err == nil {
e2e.Logf("use CLUSTER_PROFILE_DIR/.awscred")
return true
}
}
}

// check if AWS_SHARED_CREDENTIALS_FILE exist
_, present = os.LookupEnv("AWS_SHARED_CREDENTIALS_FILE")
if present {
e2e.Logf("use Env AWS_SHARED_CREDENTIALS_FILE")
return true
}

// check if AWS_SECRET_ACCESS_KEY exist
_, keyIDPresent := os.LookupEnv("AWS_ACCESS_KEY_ID")
_, keyPresent := os.LookupEnv("AWS_SECRET_ACCESS_KEY")
if keyIDPresent && keyPresent {
e2e.Logf("use Env AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY")
return true
}
// check if $HOME/.aws/credentials exist
home, _ := os.UserHomeDir()
if _, err := os.Stat(home + "/.aws/credentials"); err == nil {
e2e.Logf("use HOME/.aws/credentials")
return true
}
return false
}

// get AWS Account ID
func getAwsAccount(stsClient *sts.Client) (string, string) {
result, err := stsClient.GetCallerIdentity(context.TODO(), &sts.GetCallerIdentityInput{})
o.Expect(err).NotTo(o.HaveOccurred())
awsAccount := aws.ToString(result.Account)
awsUserArn := aws.ToString(result.Arn)
return awsAccount, awsUserArn
}

func readDefaultSDKExternalConfigurations(ctx context.Context, region string) aws.Config {
cfg, err := awsConfig.LoadDefaultConfig(ctx,
awsConfig.WithRegion(region),
)
o.Expect(err).NotTo(o.HaveOccurred())
return cfg
}

// new AWS STS client
func newStsClient(cfg aws.Config) *sts.Client {
if !checkAWSCredentials() {
g.Skip("Skip since no AWS credetial! No Env AWS_SHARED_CREDENTIALS_FILE, Env CLUSTER_PROFILE_DIR or $HOME/.aws/credentials file")
}
return sts.NewFromConfig(cfg)
}

// Create AWS IAM client
func newIamClient(cfg aws.Config) *iam.Client {
if !checkAWSCredentials() {
g.Skip("Skip since no AWS credetial! No Env AWS_SHARED_CREDENTIALS_FILE, Env CLUSTER_PROFILE_DIR or $HOME/.aws/credentials file")
}
return iam.NewFromConfig(cfg)
}

// This func creates a IAM role, attaches custom trust policy and managed permission policy
func createIAMRoleOnAWS(iamClient *iam.Client, trustPolicy string, roleName string, policyArn string) string {
result, err := iamClient.CreateRole(context.TODO(), &iam.CreateRoleInput{
AssumeRolePolicyDocument: aws.String(trustPolicy),
RoleName: aws.String(roleName),
})
o.Expect(err).NotTo(o.HaveOccurred(), "Couldn't create role %v", roleName)
roleArn := aws.ToString(result.Role.Arn)

// Adding managed permission policy if provided
if policyArn != "" {
_, err = iamClient.AttachRolePolicy(context.TODO(), &iam.AttachRolePolicyInput{
PolicyArn: aws.String(policyArn),
RoleName: aws.String(roleName),
})
o.Expect(err).NotTo(o.HaveOccurred())
}
return roleArn
}

// Deletes IAM role and attached policies
func deleteIAMroleonAWS(iamClient *iam.Client, roleName string) {
// List attached policies of the IAM role
listAttachedPoliciesOutput, err := iamClient.ListAttachedRolePolicies(context.TODO(), &iam.ListAttachedRolePoliciesInput{
RoleName: aws.String(roleName),
})
if err != nil {
e2e.Logf("Error listing attached policies of IAM role %s", roleName)
}

if len(listAttachedPoliciesOutput.AttachedPolicies) == 0 {
e2e.Logf("No attached policies under IAM role: %s", roleName)
Comment thread
oliver-smakal marked this conversation as resolved.
}

if len(listAttachedPoliciesOutput.AttachedPolicies) != 0 {
// Detach attached policy from the IAM role
for _, policy := range listAttachedPoliciesOutput.AttachedPolicies {
_, err := iamClient.DetachRolePolicy(context.TODO(), &iam.DetachRolePolicyInput{
RoleName: aws.String(roleName),
PolicyArn: policy.PolicyArn,
})
if err != nil {
e2e.Logf("Error detaching policy: %v", *policy.PolicyName)
} else {
e2e.Logf("Detached policy: %v", *policy.PolicyName)
}
}
}

// Delete the IAM role
_, err = iamClient.DeleteRole(context.TODO(), &iam.DeleteRoleInput{
RoleName: aws.String(roleName),
})
if err != nil {
e2e.Logf("Error deleting IAM role: %s", roleName)
} else {
e2e.Logf("IAM role deleted successfully: %s", roleName)
}
}

// Create role_arn required for Loki deployment on STS clusters
func createIAMRoleForLokiSTSDeployment(iamClient *iam.Client, oidcName, awsAccountID, partition, lokiNamespace, lokiStackName, roleName string) string {
e2e.Logf("Running createIAMRoleForLokiSTSDeployment")
policyArn := "arn:" + partition + ":iam::aws:policy/AmazonS3FullAccess"

lokiTrustPolicy := `{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:%s:iam::%s:oidc-provider/%s"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"%s:sub": [
"system:serviceaccount:%s:%s",
"system:serviceaccount:%s:%s-ruler"
]
}
}
}
]
}`
lokiTrustPolicy = fmt.Sprintf(lokiTrustPolicy, partition, awsAccountID, oidcName, oidcName, lokiNamespace, lokiStackName, lokiNamespace, lokiStackName)
roleArn := createIAMRoleOnAWS(iamClient, lokiTrustPolicy, roleName, policyArn)
return roleArn
}

// Creates Loki object storage secret on AWS STS cluster
func createObjectStorageSecretOnAWSSTSCluster(oc *exutil.CLI, region, storageSecret, bucketName, namespace string) {
err := oc.NotShowInfo().AsAdmin().WithoutNamespace().Run("create").Args("secret", "generic", storageSecret, "--from-literal=region="+region, "--from-literal=bucketnames="+bucketName, "--from-literal=audience=openshift", "-n", namespace).Execute()
o.Expect(err).NotTo(o.HaveOccurred())
}
Loading
Loading