11package io .dagger .modules .ci ;
22
33import static io .dagger .client .Dagger .dag ;
4- import static io .dagger .modules .ci .Utils .*;
54
6- import io .dagger .client .AwsCli ;
75import io .dagger .client .CacheVolume ;
6+ import io .dagger .client .Client .AwsCliArguments ;
87import io .dagger .client .Container ;
9- import io .dagger .client .Container .PublishArguments ;
108import io .dagger .client .DaggerQueryException ;
119import io .dagger .client .Directory ;
1210import io .dagger .client .Directory .DockerBuildArguments ;
@@ -27,120 +25,134 @@ public class Ci {
2725
2826 static final Logger LOG = LoggerFactory .getLogger (Ci .class );
2927
30- private static final List <String > ARCHS = List .of ("amd64" , "arm64" );
31-
32- /** Build and test the application */
28+ /**
29+ * Build and test the application
30+ *
31+ * @param source the project source directory. Default is the current directory
32+ * @param skipTests whether to skip tests or not
33+ */
3334 @ Function
34- public Container build (@ DefaultPath ("." ) Directory source , @ Default ("false" ) boolean skipTests )
35- throws ExecutionException , DaggerQueryException , InterruptedException {
36- return buildEnv (source )
35+ public Container buildAndTest (@ DefaultPath ("." ) Directory source , @ Default ("false" ) boolean skipTests ) {
36+ CacheVolume mavenCache = dag ().cacheVolume ("m2" );
37+ return dag ()
38+ .container ()
39+ .from ("maven:3-eclipse-temurin-21" )
40+ .withDirectory ("/src" , source )
41+ .withWorkdir ("/src" )
42+ .withMountedCache ("/root/.m2/" , mavenCache )
3743 .withExec (List .of ("mvn" , "-B" , "clean" , "package" , "-DskipTests=%s" .formatted (skipTests )));
3844 }
3945
46+ private Secret ecrToken (Secret awsAccessKeyId , Secret awsSecretAccessKey , String region ) {
47+ return dag ().awsCli ()
48+ .withRegion (region )
49+ .withStaticCredentials (awsAccessKeyId , awsSecretAccessKey )
50+ .ecr ().getLoginPassword ();
51+ }
52+
53+ private Container buildImage (Directory source , Platform platform ) {
54+ Container ctr = buildAndTest (source , true );
55+ return ctr .directory ("." )
56+ .dockerBuild (new DockerBuildArguments ()
57+ .withPlatform (platform )
58+ .withDockerfile ("src/main/docker/Dockerfile.jvm" ));
59+ }
60+
61+ private String address (Directory source , String account , String region , String repoName )
62+ throws ExecutionException , DaggerQueryException , InterruptedException {
63+ String hash = dag ().gitInfo (source ).commitHash ().substring (0 ,8 );
64+ return "%s.dkr.ecr.%s.amazonaws.com/%s:%s" .formatted (account , region , repoName , hash );
65+ }
66+
4067 /**
4168 * Builds the application and create a Docker image
4269 */
43- @ Function
44- public Container buildImage (@ DefaultPath ("." ) Directory source )
45- throws ExecutionException , DaggerQueryException , InterruptedException {
46- Container ctr = build (source , true );
47- return ctr .directory ("." )
70+ // @Function
71+ public Container buildImage (@ DefaultPath ("." ) Directory source ) {
72+ return buildAndTest (source , true )
73+ .directory ("." )
4874 .dockerBuild (new DockerBuildArguments ()
4975 .withDockerfile ("src/main/docker/Dockerfile.jvm" ));
5076 }
5177
5278 /**
53- * Build a list of Docker images for multiple architectures
54- * @param source the source directory
79+ * Pushes a Docker image to ECR
80+ *
81+ * @param image the Docker image to push
82+ * @param address the ECR image address of the form <account_id>.dkr.ecr.<region>.amazonaws.com/<repository>:<tag>
83+ * @param token the ECR authentication token
84+ *
85+ * @return the image address
5586 */
56- private List <Container > buildImageMultiarch (Directory source , List <String > variants ) {
57- List <Container > images = variants .stream ().map (platform -> {
58- try {
59- LOG .info ("Building image for {}" , platform );
60- return buildImage (source , platform );
61- } catch (ExecutionException | DaggerQueryException | InterruptedException e ) {
62- throw new RuntimeException (e );
63- }
64- }).toList ();
65- return images ;
66- };
67-
68- private Container buildImage (Directory source , String platform )
87+ private String pushImage (Container image , String address , Secret token )
6988 throws ExecutionException , DaggerQueryException , InterruptedException {
70- Container ctr = build (source , true );
71- return ctr .directory ("." )
72- .dockerBuild (new DockerBuildArguments ()
73- .withPlatform (Platform .from (platform ))
74- .withDockerfile ("src/main/docker/Dockerfile.jvm" ));
89+ return image
90+ .withRegistryAuth (address , "AWS" , token )
91+ .publish (address );
7592 }
7693
7794 /**
78- * Publishes the Docker image to ECR
95+ * Builds and publishes the Docker image to ECR
7996 *
8097 * @param source the source directory
8198 * @param awsAccessKeyId the AWS access key ID
8299 * @param awsSecretAccessKey the AWS secret access key
100+ * @param accountId the AWS account ID
83101 * @param region the AWS region
84102 */
85103 @ Function
86- public String publish (@ DefaultPath ("." ) Directory source , Secret awsAccessKeyId ,
87- Secret awsSecretAccessKey , @ Default ("eu-west-1" ) String region )
104+ public String buildAndPushImage (@ DefaultPath ("." ) Directory source , String repoName , Secret awsAccessKeyId , Secret awsSecretAccessKey , String accountId , @ Default ("eu-west-1" ) String region )
88105 throws ExecutionException , DaggerQueryException , InterruptedException {
89- AwsCli awsCli = aws (region , awsAccessKeyId , awsSecretAccessKey );
90- Secret token = awsCli .ecr ().getLoginPassword ();
91- String accountId = awsCli .sts ().getCallerIdentity ().account ();
92- String address = "%s.dkr.ecr.%s.amazonaws.com/parisjug-dagger-demo/translate-api:%s"
93- .formatted (accountId , region , dag ().gitInfo (source ).commitHash ().substring (0 , 8 ));
94- dag ().container ()
95- .withRegistryAuth (address , "AWS" , token )
96- .publish (address , new PublishArguments ()
97- .withPlatformVariants (buildImageMultiarch (source , ARCHS )));
98- return address ;
106+ Container image = buildImage (source , Platform .from ("amd64" ));
107+ String address = address (source , accountId , region , repoName );
108+ Secret token = ecrToken (awsAccessKeyId , awsSecretAccessKey , region );
109+ return pushImage (image , address , token );
110+ }
111+
112+ private Container kubectl (String clusterName , Secret awsAccessKeyId , Secret awsSecretAccessKey , String region ) {
113+ Container customCtr = dag ().container ().from ("alpine" )
114+ .withExec (List .of ("apk" , "add" , "aws-cli" , "kubectl" ));
115+ List <String > cmd = List .of ("eks" , "update-kubeconfig" , "--name" , clusterName );
116+ return dag ().awsCli (new AwsCliArguments ().withContainer (customCtr ))
117+ .withRegion (region )
118+ .withStaticCredentials (awsAccessKeyId , awsSecretAccessKey )
119+ .exec (cmd );
99120 }
100121
101122 /**
102123 * Deploys the application to EKS
103124 *
104- * @param source the source directory
105- * @param image the image address to deploy
106- * @param clusterName the name of the EKS cluster
107- * @param awsAccessKeyId the AWS access key ID
125+ * @param source the source directory
126+ * @param image the image address to deploy
127+ * @param clusterName the name of the EKS cluster
128+ * @param awsAccessKeyId the AWS access key ID
108129 * @param awsSecretAccessKey the AWS secret access key
109- * @param region the AWS region
130+ * @param region the AWS region
110131 */
111132 @ Function
112133 public String deploy (@ DefaultPath ("." ) Directory source , String image , String clusterName ,
113134 Secret awsAccessKeyId , Secret awsSecretAccessKey , @ Default ("eu-west-1" ) String region )
114135 throws ExecutionException , DaggerQueryException , InterruptedException {
115- String appYaml = envsubst ( source .file ("src/main/kube/app.yaml" ).contents (), " IMAGE_TAG" , image );
116- return kubectl (clusterName , region , awsAccessKeyId , awsSecretAccessKey )
136+ String appYaml = source .file ("src/main/kube/app.yaml" ).contents (). replace ( "${ IMAGE_TAG} " , image );
137+ return kubectl (clusterName , awsAccessKeyId , awsSecretAccessKey , region )
117138 .withNewFile ("/tmp/app.yaml" , appYaml )
118139 .withExec (List .of ("kubectl" , "apply" , "-f" , "/tmp/app.yaml" ))
119140 .stdout ();
120141 }
121142
122143 /**
123144 * Returns the ingress address of the application
145+ *
124146 * @return the ingress address
125147 */
126148 @ Function
127149 public String getIngress (String clusterName , Secret awsAccessKeyId , Secret awsSecretAccessKey ,
128150 @ Default ("eu-west-1" ) String region )
129151 throws ExecutionException , DaggerQueryException , InterruptedException {
130- String host = kubectl (clusterName , region , awsAccessKeyId , awsSecretAccessKey )
131- .withExec (List .of ("kubectl" , "-n" , "devoxxfr-dagger" , "get" , "ingress" , "-o" , "jsonpath={.items[0].status.loadBalancer.ingress[0].hostname}" ))
152+ String host = kubectl (clusterName , awsAccessKeyId , awsSecretAccessKey , region )
153+ .withExec (List .of ("kubectl" , "-n" , "devoxxfr-dagger" , "get" , "ingress" , "-o" ,
154+ "jsonpath={.items[0].status.loadBalancer.ingress[0].hostname}" ))
132155 .stdout ();
133156 return "http://%s" .formatted (host );
134157 }
135-
136- /** Build a ready-to-use development environment */
137- private Container buildEnv (Directory source ) {
138- CacheVolume mavenCache = dag ().cacheVolume ("m2" );
139- return dag ()
140- .container ()
141- .from ("maven:3-eclipse-temurin-21" )
142- .withDirectory ("/src" , source )
143- .withMountedCache (".m2/" , mavenCache )
144- .withWorkdir ("/src" );
145- }
146158}
0 commit comments