from aws_cdk import ( aws_ecs_patterns as ecs_patterns, aws_ecs as ecs, aws_ecr_assets as ecr, aws_ec2 as ec2, aws_servicediscovery as sd, aws_iam as iam, aws_logs as logs, aws_elasticloadbalancingv2 as elb, core ) from configparser import ConfigParser config = ConfigParser() config.read('config.ini') class JenkinsLeader(core.Stack): def __init__(self, scope: core.Stack, id: str, cluster, vpc, worker, **kwargs) -> None: super().__init__(scope, id, **kwargs) self.cluster = cluster self.vpc = vpc self.worker = worker # Building a custom image for jenkins leader. self.container_image = ecr.DockerImageAsset( self, "JenkinsleaderDockerImage", directory='./docker/leader/' ) if config['DEFAULT']['fargate_enabled'] == "yes" or not config['DEFAULT']['ec2_enabled'] == "yes": # Task definition details to define the Jenkins leader container self.jenkins_task = ecs_patterns.ApplicationLoadBalancedTaskImageOptions( image=ecs.ContainerImage.from_docker_image_asset(self.container_image), container_port=8080, enable_logging=True, environment={ # https://github.com/jenkinsci/docker/blob/leader/README.md#passing-jvm-parameters 'JAVA_OPTS': '-Djenkins.install.runSetupWizard=false', # https://github.com/jenkinsci/configuration-as-code-plugin/blob/leader/README.md#getting-started 'CASC_JENKINS_CONFIG': '/config-as-code.yaml', 'network_stack': self.vpc.stack_name, 'cluster_stack': self.cluster.stack_name, 'worker_stack': self.worker.stack_name, 'cluster_arn': self.cluster.cluster.cluster_arn, 'aws_region': config['DEFAULT']['region'], 'jenkins_url': config['DEFAULT']['jenkins_url'], 'subnet_ids': ",".join([x.subnet_id for x in self.vpc.vpc.private_subnets]), 'security_group_ids': self.worker.worker_security_group.security_group_id, 'execution_role_arn': self.worker.worker_execution_role.role_arn, 'task_role_arn': self.worker.worker_task_role.role_arn, 'worker_log_group': self.worker.worker_logs_group.log_group_name, 'worker_log_stream_prefix': self.worker.worker_log_stream.log_stream_name }, ) # Create the Jenkins leader service self.jenkins_leader_service_main = ecs_patterns.ApplicationLoadBalancedFargateService( self, "JenkinsleaderService", cpu=int(config['DEFAULT']['fargate_cpu']), memory_limit_mib=int(config['DEFAULT']['fargate_memory_limit_mib']), cluster=self.cluster.cluster, desired_count=1, enable_ecs_managed_tags=True, task_image_options=self.jenkins_task, cloud_map_options=ecs.CloudMapOptions(name="leader", dns_record_type=sd.DnsRecordType('A')) ) self.jenkins_leader_service = self.jenkins_leader_service_main.service self.jenkins_leader_task = self.jenkins_leader_service.task_definition if config['DEFAULT']['ec2_enabled'] == "yes": self.jenkins_load_balancer = elb.ApplicationLoadBalancer( self, "JenkinsleaderELB", vpc=self.vpc.vpc, internet_facing=True, ) self.listener = self.jenkins_load_balancer.add_listener("Listener", port=80) self.jenkins_leader_task = ecs.Ec2TaskDefinition( self, "JenkinsleaderTaskDef", network_mode=ecs.NetworkMode.AWS_VPC, volumes=[ecs.Volume(name="efs_mount", host=ecs.Host(source_path='/mnt/efs'))], ) self.jenkins_leader_task.add_container( "JenkinsleaderContainer", image=ecs.ContainerImage.from_ecr_repository(self.container_image.repository), cpu=int(config['DEFAULT']['ec2_cpu']), memory_limit_mib=int(config['DEFAULT']['ec2_memory_limit_mib']), environment={ # https://github.com/jenkinsci/docker/blob/leader/README.md#passing-jvm-parameters 'JAVA_OPTS': '-Djenkins.install.runSetupWizard=false', # https://github.com/jenkinsci/configuration-as-code-plugin/blob/leader/README.md#getting-started 'CASC_JENKINS_CONFIG': '/config-as-code.yaml', 'network_stack': self.vpc.stack_name, 'cluster_stack': self.cluster.stack_name, 'worker_stack': self.worker.stack_name, 'cluster_arn': self.cluster.cluster.cluster_arn, 'aws_region': config['DEFAULT']['region'], 'jenkins_url': config['DEFAULT']['jenkins_url'], 'subnet_ids': ",".join([x.subnet_id for x in self.vpc.vpc.private_subnets]), 'security_group_ids': self.worker.worker_security_group.security_group_id, 'execution_role_arn': self.worker.worker_execution_role.role_arn, 'task_role_arn': self.worker.worker_task_role.role_arn, 'worker_log_group': self.worker.worker_logs_group.log_group_name, 'worker_log_stream_prefix': self.worker.worker_log_stream.log_stream_name }, logging=ecs.LogDriver.aws_logs( stream_prefix="Jenkinsleader", log_retention=logs.RetentionDays.ONE_WEEK ), ) self.jenkins_leader_task.default_container.add_mount_points( ecs.MountPoint( container_path='/var/jenkins_home', source_volume="efs_mount", read_only=False ) ) self.jenkins_leader_task.default_container.add_port_mappings( ecs.PortMapping( container_port=8080, host_port=8080 ) ) self.jenkins_leader_service = ecs.Ec2Service( self, "EC2leaderService", task_definition=self.jenkins_leader_task, cloud_map_options=ecs.CloudMapOptions(name="leader", dns_record_type=sd.DnsRecordType('A')), desired_count=1, min_healthy_percent=0, max_healthy_percent=100, enable_ecs_managed_tags=True, cluster=self.cluster.cluster, ) self.target_group = self.listener.add_targets( "JenkinsleaderTarget", port=80, targets=[ self.jenkins_leader_service.load_balancer_target( container_name=self.jenkins_leader_task.default_container.container_name, container_port=8080, ) ], deregistration_delay=core.Duration.seconds(10) ) # Opening port 5000 for leader <--> worker communications self.jenkins_leader_service.task_definition.default_container.add_port_mappings( ecs.PortMapping(container_port=50000, host_port=50000) ) # Enable connection between leader and Worker self.jenkins_leader_service.connections.allow_from( other=self.worker.worker_security_group, port_range=ec2.Port( protocol=ec2.Protocol.TCP, string_representation='leader to Worker 50000', from_port=50000, to_port=50000 ) ) # Enable connection between leader and Worker on 8080 self.jenkins_leader_service.connections.allow_from( other=self.worker.worker_security_group, port_range=ec2.Port( protocol=ec2.Protocol.TCP, string_representation='leader to Worker 8080', from_port=8080, to_port=8080 ) ) # IAM Statements to allow jenkins ecs plugin to talk to ECS as well as the Jenkins cluster # self.jenkins_leader_task.add_to_task_role_policy( iam.PolicyStatement( actions=[ "ecs:RegisterTaskDefinition", "ecs:DeregisterTaskDefinition", "ecs:ListClusters", "ecs:DescribeContainerInstances", "ecs:ListTaskDefinitions", "ecs:DescribeTaskDefinition", "ecs:DescribeTasks" ], resources=[ "*" ], ) ) self.jenkins_leader_task.add_to_task_role_policy( iam.PolicyStatement( actions=[ "ecs:ListContainerInstances" ], resources=[ self.cluster.cluster.cluster_arn ] ) ) self.jenkins_leader_task.add_to_task_role_policy( iam.PolicyStatement( actions=[ "ecs:RunTask" ], resources=[ "arn:aws:ecs:{0}:{1}:task-definition/fargate-workers*".format( self.region, self.account, ) ] ) ) self.jenkins_leader_task.add_to_task_role_policy( iam.PolicyStatement( actions=[ "ecs:StopTask" ], resources=[ "arn:aws:ecs:{0}:{1}:task/*".format( self.region, self.account ) ], conditions={ "ForAnyValue:ArnEquals": { "ecs:cluster": self.cluster.cluster.cluster_arn } } ) ) self.jenkins_leader_task.add_to_task_role_policy( iam.PolicyStatement( actions=[ "iam:PassRole" ], resources=[ self.worker.worker_task_role.role_arn, self.worker.worker_execution_role.role_arn ] ) ) # END OF JENKINS ECS PLUGIN IAM POLICIES #