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
49 changes: 36 additions & 13 deletions chaosLib/litmus/aws_az_chaos/lib/aws_az_chaos.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ def PrepareAWSAZExperiment(experimentsDetails , resultDetails, eventsDetails, ch
if err != None:
return err
elif experimentsDetails.Sequence.lower() == "parallel":
err = injectChaosInParallelMode(experimentsDetails, chaosDetails, eventsDetails, resultDetails, clients, statusAws)
if experimentsDetails.LoadBalancerVersion == "elb":
err = injectChaosInParallelMode(experimentsDetails, chaosDetails, eventsDetails, resultDetails, clients, statusAws)
elif experimentsDetails.LoadBalancerVersion == "elbv2":
logging.info("[Sequence]: aws az chaos is not available in parallel mode with elbv2 as it requires at least one subnet per LoadBalancer")
return ValueError("aws az chaos is not available in parallel mode with elbv2 as it requires at least one subnet per LoadBalancer")
if err != None:
return err
else:
Expand All @@ -39,7 +43,6 @@ def injectChaosInSerialMode(experimentsDetails , chaosDetails , eventsDetails ,
#ChaosStartTimeStamp contains the start timestamp, when the chaos injection begin
ChaosStartTimeStamp = datetime.now()
duration = (datetime.now() - ChaosStartTimeStamp).seconds

while duration < experimentsDetails.ChaosDuration:

# Get the target available zones for the chaos execution
Expand All @@ -54,17 +57,27 @@ def injectChaosInSerialMode(experimentsDetails , chaosDetails , eventsDetails ,

# Detaching the target zones from loa balancer
for azone in targetZones:

logging.info("[Info]: Detaching the following zone, Zone Name %s", azone)
targetSubnet, err = statusAws.getTargetSubnet(experimentsDetails, azone)
subnetList = list(targetSubnet.split(" "))
if err != None:
return err
logging.info("[Info]: Detaching the following subnet, %s", subnetList)
err = statusAws.detachSubnet(experimentsDetails, subnetList)
if err != None:
return err

if targetSubnet is None:
logging.error("[Error]: No subnet found for the zone %s", azone)
return ValueError("No subnet found for the zone %s" % azone)

if experimentsDetails.LoadBalancerVersion == "elb":
subnetList = list(targetSubnet.split(" "))
logging.info("[Info]: Detaching the following subnet, %s", subnetList)
err = statusAws.detachSubnet(experimentsDetails, subnetList)
if err != None:
return err
elif experimentsDetails.LoadBalancerVersion == "elbv2":
logging.info("[Info]: Detaching the following subnet, %s", targetSubnet)
err = statusAws.detachSubnetv2(experimentsDetails, targetSubnet)
if err != None:
return err

if chaosDetails.Randomness:
err = common.RandomInterval(experimentsDetails.ChaosInterval)
if err != None:
Expand All @@ -76,11 +89,21 @@ def injectChaosInSerialMode(experimentsDetails , chaosDetails , eventsDetails ,
waitTime = maths.atoi(experimentsDetails.ChaosInterval)
common.WaitForDuration(waitTime)

# Attaching the target available zone after the chaos injection
logging.info("[Status]: Attach the available zone back to load balancer")
err = statusAws.attachAZtoLB(experimentsDetails, azone)
if err != None:
return err
if experimentsDetails.LoadBalancerVersion == "elb":

# Attaching the target available zone after the chaos injection
logging.info("[Status]: Attach the available zone back to load balancer")
err = statusAws.attachSubnet(experimentsDetails, subnetList)
if err != None:
return err

elif experimentsDetails.LoadBalancerVersion == "elbv2":

# Attaching again the target availability zone subnet after chaos injection
logging.info("[Status]: Attach the available zone back to load balancer")
err = statusAws.attachSubnetv2(experimentsDetails, targetSubnet)
if err != None:
return err

#Verify the status of available zone after the chaos injection
logging.info("[Status]: Checking AWS load balancer's AZ status")
Expand Down
28 changes: 16 additions & 12 deletions okteto.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
name: litmus-experiment
autocreate: true
image: okteto/python:3
command: bash
volumes:
- /root/.cache/pip
sync:
- .:/usr/src/app
forward:
- 8080:8080
reverse:
- 9000:9000
dev:
litmus-experiment:
image: okteto/python:3
command: bash -c "apt-get update && apt-get install -y build-essential python3-dev && pip install cython && bash"
volumes:
- /root/.cache/pip
sync:
- .:/usr/src/app
reverse:
- 9000:9000
persistentVolume:
enabled: true
storageClass: gp3
serviceAccount: "<your-litmus-sa-name>"
environment:
- <your-env-variables>
7 changes: 4 additions & 3 deletions pkg/aws_az/environment/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@ def GetENV(experimentDetails):
experimentDetails.ChaosNamespace = os.getenv("CHAOS_NAMESPACE", "")
experimentDetails.EngineName = os.getenv("CHAOSENGINE", "")
experimentDetails.ChaosDuration = maths.atoi(os.getenv("TOTAL_CHAOS_DURATION", "60"))
experimentDetails.ChaosInterval = os.getenv("CHAOS_INTERVAL", "30")
experimentDetails.ChaosInterval = os.getenv("CHAOS_INTERVAL", "60")
experimentDetails.RampTime = maths.atoi(os.getenv("RAMP_TIME", ""))
experimentDetails.ChaosLib = os.getenv("LIB", "litmus")
experimentDetails.ChaosUID = os.getenv("CHAOS_UID", "")
experimentDetails.InstanceID = os.getenv("INSTANCE_ID", "")
experimentDetails.ChaosPodName = os.getenv("POD_NAME", "")
experimentDetails.Delay = maths.atoi(os.getenv("STATUS_CHECK_DELAY", "2"))
experimentDetails.Timeout = maths.atoi(os.getenv("STATUS_CHECK_TIMEOUT", "180"))
experimentDetails.Sequence = os.getenv("SEQUENCE", "parallel")
experimentDetails.AWSRegion = os.getenv("AWS_DEFAULT_REGION", "")
experimentDetails.Sequence = os.getenv("SEQUENCE", "serial")
experimentDetails.AWSRegion = os.getenv("AWS_REGION", "")
experimentDetails.LoadBalancerName = os.getenv("LOAD_BALANCER_NAME", "")
experimentDetails.LoadBalancerZones = os.getenv("LOAD_BALANCER_ZONES", "")
experimentDetails.LoadBalancerNameARN = os.getenv("LOAD_BALANCERNAME_ARN", "na")
experimentDetails.LoadBalancerVersion = os.getenv("LOAD_BALANCER_VERSION", "elb")

#InitialiseChaosVariables initialise all the global variables
def InitialiseChaosVariables(chaosDetails, experimentDetails):
Expand Down
142 changes: 112 additions & 30 deletions pkg/aws_status/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ def __init__(self, client=None):
# CheckAWSStatus checks target load balancer availability
def CheckAWSStatus(self, experimentsDetails):

self.clients = client.AWSClient().clientElb
self.clients = client.AWSClient(experimentsDetails).clientElb

if experimentsDetails.LoadBalancerName == "" or experimentsDetails.LoadBalancerZones == "" :
return ValueError("Provided LoadBalancer Name or LoadBalanerZoner are empty")
return ValueError("Provided LoadBalancer Name or LoadBalancerZoner are empty")

try:
self.clients.describe_load_balancers()['LoadBalancerDescriptions']
Expand All @@ -23,36 +23,65 @@ def CheckAWSStatus(self, experimentsDetails):
logging.info("[Info]: LoadBalancer and Availablity of zone has been checked")

def getSubnetFromVPC(self, experimentsDetails):
client = boto3.client('elb')
try:
response = client.describe_load_balancers(
LoadBalancerNames=[
experimentsDetails.LoadBalancerName,
]
)
return (response['LoadBalancerDescriptions'][0]['Subnets'])
except (self.clients.exceptions.AccessPointNotFoundException, self.clients.exceptions.InvalidConfigurationRequestException) as exp:
return ValueError(exp)

def getTargetSubnet(self, experimentsDetails, zone):
client = boto3.client('ec2')
try:
lst=self.getSubnetFromVPC(experimentsDetails)
i=0
for i in range(len(lst)):
response = client.describe_subnets(
SubnetIds=[
lst[i],
],

if experimentsDetails.LoadBalancerVersion == "elb":

client = boto3.client('elb', region_name=experimentsDetails.AWSRegion)
try:
response = client.describe_load_balancers(
LoadBalancerNames=[
experimentsDetails.LoadBalancerName,
]
)
if(response['Subnets'][0]['AvailabilityZone']) == zone:
return lst[i], None
except (self.clients.exceptions.AccessPointNotFoundException, self.clients.exceptions.InvalidConfigurationRequestException) as exp:
return lst[i], ValueError(exp)
return (response['LoadBalancerDescriptions'][0]['Subnets'])
except (self.clients.exceptions.AccessPointNotFoundException, self.clients.exceptions.InvalidConfigurationRequestException) as exp:
return ValueError(exp)

# Use dedicated client for elv2 when env var has been set
elif experimentsDetails.LoadBalancerVersion == "elbv2":

client = boto3.client('elbv2', region_name=experimentsDetails.AWSRegion)
try:
response = client.describe_load_balancers(
Names=[
experimentsDetails.LoadBalancerName,
]
)
return (response['LoadBalancers'][0]['AvailabilityZones'])
except (self.clients.exceptions.AccessPointNotFoundException, self.clients.exceptions.InvalidConfigurationRequestException) as exp:
return ValueError(exp)

def getTargetSubnet(self, experimentsDetails, zone):
client = boto3.client('ec2', region_name=experimentsDetails.AWSRegion)
if experimentsDetails.LoadBalancerVersion == "elb":

try:
lst=self.getSubnetFromVPC(experimentsDetails)
i=0
for i in range(len(lst)):
response = client.describe_subnets(
SubnetIds=[
lst[i],
],
)
if(response['Subnets'][0]['AvailabilityZone']) == zone:
return lst[i], None
except (self.clients.exceptions.AccessPointNotFoundException, self.clients.exceptions.InvalidConfigurationRequestException) as exp:
return lst[i], ValueError(exp)

# Specific section to deal with the output of the elv2 client
elif experimentsDetails.LoadBalancerVersion == "elbv2":
try:
lst =self.getSubnetFromVPC(experimentsDetails)
i=0
for i in range(len(lst)):
if (lst[i]['ZoneName']) == zone:
return lst[i]['SubnetId'], None
except (self.clients.exceptions.AccessPointNotFoundException, self.clients.exceptions.InvalidConfigurationRequestException) as exp:
return lst[i], ValueError(exp)

def detachSubnet(self, experimentsDetails, subnet):
client = boto3.client('elb')
client = boto3.client('elb', region_name=experimentsDetails.AWSRegion)
try:
response = client.detach_load_balancer_from_subnets(
LoadBalancerName=experimentsDetails.LoadBalancerName,
Expand All @@ -63,8 +92,38 @@ def detachSubnet(self, experimentsDetails, subnet):
except (self.clients.exceptions.AccessPointNotFoundException, self.clients.exceptions.InvalidConfigurationRequestException) as exp:
return ValueError(exp)

# Specific method to detach the subnet with elbv2 client
def detachSubnetv2(self, experimentsDetails, subnet):
client = boto3.client('elbv2', region_name=experimentsDetails.AWSRegion)
try:

subnetsToKeep = []
response = client.describe_load_balancers(
Names=[
experimentsDetails.LoadBalancerName,
]
)

# List subnet to keep that are currently attached to the LB
for az in response['LoadBalancers'][0]['AvailabilityZones']:
if az['SubnetId'] not in subnet:
subnetsToKeep.append(az['SubnetId'])
if experimentsDetails.Sequence is "parallel":
response = client.set_subnets(
LoadBalancerArn=response['LoadBalancers'][0]['LoadBalancerArn'],
Subnets=[""]
)
else:
response = client.set_subnets(
LoadBalancerArn=response['LoadBalancers'][0]['LoadBalancerArn'],
Subnets=subnetsToKeep
)

except (self.clients.exceptions.AccessPointNotFoundException, self.clients.exceptions.InvalidConfigurationRequestException) as exp:
return ValueError(exp)

def attachSubnet(self, experimentsDetails, subnet):
client = boto3.client('elb')
client = boto3.client('elb', region_name=experimentsDetails.AWSRegion)
try:
response = client.attach_load_balancer_to_subnets(
LoadBalancerName=experimentsDetails.LoadBalancerName,
Expand All @@ -74,4 +133,27 @@ def attachSubnet(self, experimentsDetails, subnet):
ValueError("[Error]: Fail to attach the target subnet %s", subnet)
except (self.clients.exceptions.AccessPointNotFoundException, self.clients.exceptions.InvalidConfigurationRequestException) as exp:
return ValueError(exp)


# Specific method to attach the removed subnet with elbv2 client after Chaos
def attachSubnetv2(self, experimentsDetails, subnet):
client = boto3.client('elbv2', region_name=experimentsDetails.AWSRegion)
try:

subnetsToKeep = [subnet]
response = client.describe_load_balancers(
Names=[
experimentsDetails.LoadBalancerName,
]
)

for az in response['LoadBalancers'][0]['AvailabilityZones']:
if az['SubnetId'] not in subnet:
subnetsToKeep.append(az['SubnetId'])

response = client.set_subnets(
LoadBalancerArn=response['LoadBalancers'][0]['LoadBalancerArn'],
Subnets=subnetsToKeep
)

except (self.clients.exceptions.AccessPointNotFoundException, self.clients.exceptions.InvalidConfigurationRequestException) as exp:
return ValueError(exp)
4 changes: 2 additions & 2 deletions pkg/events/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#CreateEvents create the events in the desired resource
def CreateEvents(eventsDetails , chaosDetails, kind, eventName, clients):

event = client.V1Event(
event = client.CoreV1Event(
first_timestamp = datetime.now(pytz.utc),
last_timestamp = datetime.now(pytz.utc),
event_time = datetime.now(pytz.utc),
Expand Down Expand Up @@ -52,7 +52,7 @@ def GenerateEvents(eventsDetails, chaosDetails, kind, clients):
return err
elif kind == "ChaosEngine":
eventName = eventsDetails.Reason + chaosDetails.ExperimentName + str(chaosDetails.ChaosUID)
event = client.V1Event
event = client.CoreV1Event
try:
event = clients.clientCoreV1.read_namespaced_event(name = eventName,namespace = chaosDetails.ChaosNamespace)
except Exception as exp:
Expand Down
11 changes: 7 additions & 4 deletions pkg/utils/client/client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import os, boto3
from kubernetes import client, config, dynamic
from kubernetes.client import api_client

import pkg.aws_az.types.types as experimentDetails
from botocore.config import Config
import pkg.aws_az.environment.environment as experimentEnv

# Client Class is maintaining clients for k8s
class K8sClient(object):
Expand All @@ -12,9 +14,10 @@ def __init__(self, conf=None):

# AWSClient is maintaining clients for aws
class AWSClient(object):
def __init__(self):
self.clientElb = boto3.client('elb')
self.clientElbv2 = boto3.client('elbv2')
def __init__(self, experimentsDetails):

self.clientElb = boto3.client('elb', region_name=experimentsDetails.AWSRegion)
self.clientElbv2 = boto3.client('elbv2', region_name=experimentsDetails.AWSRegion)

# Config maintain configuration for in and out cluster
class Configuration(object):
Expand Down
10 changes: 6 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
cachetools==4.2.2
chaostoolkit-kubernetes==0.25.1
chaostoolkit-lib==1.19.0
chaostoolkit-lib==1.44.0
chaostoolkit-kubernetes==0.29.0
dateparser==1.0.0
decorator==5.0.9
idna==2.10
Jinja2==2.11.3
kubernetes==17.17.0
kubernetes==34.1.0
protobuf==3.17.1
py==1.10.0
pyasn1==0.4.8
Expand All @@ -15,4 +15,6 @@ python-dateutil==2.8.1
pytz==2021.1
pytzdata==2020.1
boto3==1.20.13
markupsafe==2.0.1
markupsafe==2.0.1
setuptools==75.3.3
legacy-cgi==2.6.4
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def get_version_from_package() -> str:
include_package_data=True,
install_requires=install_require,
setup_requires=pytest_runner,
python_requires='>=3.5.*'
python_requires='>=3.5'
)


Expand Down