@@ -350,6 +350,8 @@ Resources:
350350 ImageId : ami-05603669082b6ebf0
351351 SecurityGroupIds :
352352 - !GetAtt " InstanceSecurityGroup.GroupId"
353+ IamInstanceProfile :
354+ Arn : !GetAtt InstanceProfile.Arn
353355 BlockDeviceMappings :
354356 - DeviceName : /dev/sda1
355357 Ebs :
@@ -360,7 +362,7 @@ Resources:
360362 Fn::Base64 : !Sub |
361363 # !/bin/bash
362364 sudo apt-get update -y
363- sudo apt-get install stunnel python3-pip -y
365+ sudo apt-get install stunnel python3-pip jq -y
364366 sudo tee /etc/stunnel/stunnel.conf > /dev/null <<EOF
365367 fips = no
366368 setuid = root
@@ -377,7 +379,11 @@ Resources:
377379 sudo systemctl enable stunnel4
378380 sudo stunnel /etc/stunnel/stunnel.conf
379381 touch /usr/local/antmedia/conf/initialized
380- bash /usr/local/antmedia/change_server_mode.sh standalone redis://localhost:6379
382+ SECRET_KEY=$(openssl rand -base64 32 | head -c 32)
383+ sudo sed -i "/^server.jwtServerControlEnabled=/s|.*|server.jwtServerControlEnabled=true|" /usr/local/antmedia/conf/red5.properties
384+ sudo sed -i "/^server.jwtServerSecretKey=/s|.*|server.jwtServerSecretKey=$SECRET_KEY|" /usr/local/antmedia/conf/red5.properties
385+ wget -P /usr/local/antmedia/conf/ https://raw.githubusercontent.com/ant-media/Ant-Media-Server/749567900bc6fda5c614b329dceabbf5048485d3/src/main/server/conf/jwt_generator.sh
386+ bash /usr/local/antmedia/change_server_mode.sh cluster redis://localhost:6379
381387 mkdir -p /opt/aws/bin
382388 sudo pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
383389 sudo ln -s /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup
@@ -393,6 +399,16 @@ Resources:
393399 - Key : Name
394400 Value : !Sub ${AWS::StackName}-AntMedia-LaunchTemplateOrigin
395401
402+ OriginCPUPolicy :
403+ Type : AWS::AutoScaling::ScalingPolicy
404+ Properties :
405+ AutoScalingGroupName : !Ref OriginGroup
406+ PolicyType : TargetTrackingScaling
407+ TargetTrackingConfiguration :
408+ PredefinedMetricSpecification :
409+ PredefinedMetricType : ASGAverageCPUUtilization
410+ TargetValue : 50
411+
396412 InstanceSecurityGroup :
397413 Type : ' AWS::EC2::SecurityGroup'
398414 Properties :
@@ -813,48 +829,135 @@ Resources:
813829 Properties :
814830 Code :
815831 ZipFile : |
816- import boto3, os
832+ import boto3, os, time, json
817833
818834 def lambda_handler(event, context):
835+
819836 listener_arn = os.environ['LISTENER_ARN']
820837 origin_asg = os.environ['ORIGIN_ASG']
821838 target_group_arn = os.environ['TARGETGROUP_ARN']
839+ alarm_name = 'AutoScalingGroupScaleDownAlarm'
822840 autoscaling_client = boto3.client('autoscaling')
823841 elb_client = boto3.client('elbv2')
842+ ec2_client = boto3.client("ec2")
843+ ssm_client = boto3.client('ssm')
844+ cloudwatch_client = boto3.client('cloudwatch')
845+
846+ script = """
847+ #/usr/bin/env bash -x
848+ sed -i 's|INSTALL_DIRECTORY="$1"|INSTALL_DIRECTORY="/usr/local/antmedia"|g' /usr/local/antmedia/conf/jwt_generator.sh
849+ . /usr/local/antmedia/conf/jwt_generator.sh
850+ generate_jwt
851+ REST_URL="http://localhost:5080/rest/v2/applications-info"
852+ curl -s -L "$REST_URL" --header "ProxyAuthorization: $JWT_KEY" -o /tmp/curl_output.txt
853+ jq '[.[] | select(.liveStreamCount > 0)] | length > 0' /tmp/curl_output.txt
854+ """
855+
824856 asg_names = autoscaling_client.describe_auto_scaling_groups()
825857 asg_origin_name = [group for group in asg_names['AutoScalingGroups'] if
826- origin_asg in group['AutoScalingGroupName']]
858+ origin_asg in group['AutoScalingGroupName']]
827859 asg_origin_group_names = [group['AutoScalingGroupName'] for group in asg_origin_name][0]
828- response = autoscaling_client.update_auto_scaling_group(
829- AutoScalingGroupName=asg_origin_group_names,
830- MinSize=0,
831- DesiredCapacity=0
832- )
833860
861+ print(f"Auto Scaling Group name: {asg_origin_group_names}")
862+ origin_calculate_total_instance = autoscaling_client.describe_auto_scaling_groups(AutoScalingGroupNames=[asg_origin_group_names])
863+ origin_current_capacity = len(origin_calculate_total_instance['AutoScalingGroups'][0]['Instances'])
864+ instance = origin_calculate_total_instance['AutoScalingGroups'][0]['Instances']
865+ try:
866+ instance_id = instance[0]['InstanceId']
867+ print("Current capacity and Instance ID", {"origin_current_capacity": origin_current_capacity, "instance_id": instance_id})
868+ except IndexError:
869+ cloudwatch_set_ok(alarm_name)
870+ print("No instances found in Auto Scaling Group", asg_origin_group_names)
871+ return {
872+ 'statusCode': 200,
873+ 'body': json.dumps({"message": "No instances found in Auto Scaling Group"})
874+ }
834875
835- create_rule = elb_client.create_rule(
836- Actions=[
837- {
838- 'Type': 'forward',
839- 'TargetGroupArn': target_group_arn
840- }
841- ],
842- Conditions=[
843- {
844- 'Field': 'path-pattern',
845- 'Values': ['*']
846- }
847- ],
848- ListenerArn=listener_arn,
849- Priority=1
850- )
876+ smm_response = ssm_client.send_command(
877+ DocumentName ='AWS-RunShellScript',
878+ Parameters = {'commands': [script]},
879+ InstanceIds = [instance_id]
880+ )
851881
852- print(response)
882+ command_id = smm_response['Command']['CommandId']
853883
884+ while True:
885+ time.sleep(2)
886+ invocation_response = ssm_client.get_command_invocation(
887+ CommandId=command_id,
888+ InstanceId=instance_id,
889+ )
890+ if invocation_response['Status'] not in ['Pending', 'InProgress']:
891+ break
892+
893+ smm_output = invocation_response['StandardOutputContent'].strip()
894+ print(f"SSM command output: {smm_output}")
895+ #debug
896+ #print(invocation_response['StandardOutputContent'])
897+ #error = invocation_response['StandardErrorContent'].strip()
898+
899+ if origin_current_capacity == 1:
900+ if smm_output == 'false':
901+ print("No live streams found. Updating Auto Scaling Group.")
902+ origin_response = autoscaling_client.update_auto_scaling_group(
903+ AutoScalingGroupName=asg_origin_group_names,
904+ MinSize=0,
905+ DesiredCapacity=0
906+ )
907+
908+ create_rule = elb_client.create_rule(
909+ Actions=[
910+ {
911+ 'Type': 'forward',
912+ 'TargetGroupArn': target_group_arn
913+ }
914+ ],
915+ Conditions=[
916+ {
917+ 'Field': 'path-pattern',
918+ 'Values': ['*']
919+ }
920+ ],
921+ ListenerArn=listener_arn,
922+ Priority=1
923+ )
924+ cloudwatch_set_ok(alarm_name)
925+ print(origin_response)
926+ return {
927+ 'statusCode': 200,
928+ 'body': 'Auto Scaling Group updated successfully!'
929+ }
930+ else:
931+ print("Live streams found.")
932+ else:
933+ print(f"Current capacity is not 1, it is {origin_current_capacity}. No updates needed.")
934+
935+ cloudwatch_set_ok(alarm_name)
854936 return {
855937 'statusCode': 200,
856- 'body': 'Auto Scaling Group updated successfully! '
938+ 'body': 'Auto Scaling Group does not require update. '
857939 }
940+
941+
942+ def cloudwatch_set_ok(alarm_name):
943+ try:
944+ cloudwatch_client = boto3.client('cloudwatch')
945+ cloudwatch_response = cloudwatch_client.set_alarm_state(
946+ AlarmName=alarm_name,
947+ StateValue='OK',
948+ StateReason='Updating alarm state to OK'
949+ )
950+ print("Alarm state updated to OK", cloudwatch_response)
951+ return cloudwatch_response
952+ except Exception as e:
953+ error_message = {
954+ "error": str(e),
955+ "function": "cloudwatch_set_ok",
956+ "alarm_name": alarm_name
957+ }
958+ print(json.dumps(error_message))
959+ raise e
960+
858961 # FunctionName: InstanceDeleteFunction
859962 Handler : " index.lambda_handler"
860963 Role : !GetAtt LambdaIamRole.Arn
@@ -928,7 +1031,7 @@ Resources:
9281031 Service : " lambda.amazonaws.com"
9291032 Action : " sts:AssumeRole"
9301033 Policies :
931- - PolicyName : " EC2FullAccessPolicy1 "
1034+ - PolicyName : " AccessPolicy "
9321035 PolicyDocument :
9331036 Version : " 2012-10-17"
9341037 Statement :
@@ -948,6 +1051,9 @@ Resources:
9481051 - " elasticloadbalancing:CreateRule"
9491052 - " apigateway:GET"
9501053 - " acm:DescribeCertificate"
1054+ - " ssm:GetCommandInvocation"
1055+ - " ssm:SendCommand"
1056+ - " cloudwatch:SetAlarmState"
9511057 Resource : " *"
9521058 - PolicyName : " CloudWatchLogsPolicy"
9531059 PolicyDocument :
@@ -977,6 +1083,39 @@ Resources:
9771083 Action : lambda:InvokeFunction
9781084 Principal : elasticloadbalancing.amazonaws.com
9791085
1086+ InstanceProfile :
1087+ Type : ' AWS::IAM::InstanceProfile'
1088+ Properties :
1089+ Roles :
1090+ - !Ref EC2Role
1091+
1092+ EC2Role :
1093+ Type : ' AWS::IAM::Role'
1094+ Properties :
1095+ AssumeRolePolicyDocument :
1096+ Version : ' 2012-10-17'
1097+ Statement :
1098+ - Effect : Allow
1099+ Principal :
1100+ Service : [ec2.amazonaws.com]
1101+ Action : ['sts:AssumeRole']
1102+ Policies :
1103+ - PolicyName : AllowSSM
1104+ PolicyDocument :
1105+ Version : ' 2012-10-17'
1106+ Statement :
1107+ - Effect : Allow
1108+ Action :
1109+ - " ssm:*"
1110+ - " cloudwatch:PutMetricData"
1111+ - " ds:CreateComputer"
1112+ - " ds:DescribeDirectories"
1113+ - " ec2:DescribeInstanceStatus"
1114+ - " logs:*"
1115+ - " ssm:*"
1116+ - " ec2messages:*"
1117+ Resource : ' *'
1118+
9801119 # Cloudwatch rule to set 0 in Autoscale
9811120 AutoScalingGroupScaleDownAlarm :
9821121 Type : ' AWS::CloudWatch::Alarm'
0 commit comments