AbstractOCITask.groovy
001 /*
002  * SPDX-License-Identifier: Apache-2.0
003  *
004  * Copyright 2019-2022 Andres Almiray.
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 package org.kordamp.gradle.plugin.oci.tasks
019 
020 import com.oracle.bmc.ConfigFileReader
021 import com.oracle.bmc.Region
022 import com.oracle.bmc.apigateway.DeploymentClient
023 import com.oracle.bmc.apigateway.GatewayClient
024 import com.oracle.bmc.auth.AuthenticationDetailsProvider
025 import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider
026 import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider
027 import com.oracle.bmc.core.BlockstorageClient
028 import com.oracle.bmc.core.ComputeClient
029 import com.oracle.bmc.core.VirtualNetworkClient
030 import com.oracle.bmc.database.DatabaseClient
031 import com.oracle.bmc.identity.IdentityClient
032 import com.oracle.bmc.objectstorage.ObjectStorageAsyncClient
033 import com.oracle.bmc.objectstorage.ObjectStorageClient
034 import com.oracle.bmc.resourcesearch.ResourceSearchClient
035 import groovy.transform.CompileStatic
036 import org.gradle.api.provider.Property
037 import org.gradle.api.provider.Provider
038 import org.gradle.api.tasks.Input
039 import org.gradle.api.tasks.Internal
040 import org.gradle.api.tasks.Optional
041 import org.gradle.api.tasks.TaskAction
042 import org.gradle.api.tasks.options.Option
043 import org.kordamp.gradle.plugin.base.tasks.AbstractReportingTask
044 import org.kordamp.gradle.plugin.oci.OCIConfigExtension
045 import org.kordamp.gradle.plugin.oci.tasks.interfaces.OCITask
046 import org.kordamp.gradle.util.AnsiConsole
047 
048 import java.text.SimpleDateFormat
049 import java.util.function.Supplier
050 
051 import static org.kordamp.gradle.PropertyUtils.stringProvider
052 import static org.kordamp.gradle.property.PropertyUtils.booleanProvider
053 import static org.kordamp.gradle.util.StringUtils.isNotBlank
054 
055 /**
056  @author Andres Almiray
057  @since 0.1.0
058  */
059 @CompileStatic
060 abstract class AbstractOCITask extends AbstractReportingTask implements OCITask {
061     protected static final String CONFIG_LOCATION = '~/.oci/config'
062     protected static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
063 
064     protected final OCIConfigExtension ociConfig
065     protected final List<AutoCloseable> closeables = []
066     private AuthenticationDetailsProvider authenticationDetailsProvider
067 
068     AbstractOCITask() {
069         ociConfig = extensions.create('ociConfig', OCIConfigExtension, project)
070     }
071 
072     @Internal
073     final Property<String> profile = project.objects.property(String)
074 
075     @Input
076     @Optional
077     final Provider<String> resolvedProfile = stringProvider(
078         'OCI_PROFILE',
079         'oci.profile',
080         profile,
081         project,
082         this)
083         .orElse('DEFAULT')
084 
085     @Internal
086     final Property<String> region = project.objects.property(String)
087 
088     @Input
089     @Optional
090     final Provider<String> resolvedRegion = stringProvider(
091         'OCI_REGION',
092         'oci.region',
093         region,
094         project,
095         this)
096 
097     @Internal
098     final Property<Boolean> displayStreamLogs = project.objects.property(Boolean)
099 
100     @Input
101     @Optional
102     final Provider<Boolean> resolvedDisplayStreamLogs = booleanProvider(
103         'OCI_DISPLAY_STREAM_LOGS',
104         'oci.display.stream.logs',
105         displayStreamLogs,
106         project,
107         this)
108 
109     @Option(option = 'oci-profile', description = 'The profile to use. Defaults to DEFAULT (OPTIONAL).')
110     void setProfile(String profile) {
111         this.profile.set(profile)
112     }
113 
114     @Option(option = 'region', description = 'The region to use (OPTIONAL).')
115     void setRegion(String region) {
116         this.region.set(region)
117     }
118 
119     @Option(option = 'display-stream-logs', description = 'Display extra stream log warnings (OPTIONAL).')
120     void setDisplayStreamLogs(Boolean displayStreamLogs) {
121         this.displayStreamLogs.set(displayStreamLogs)
122     }
123 
124     @Internal
125     @Override
126     AnsiConsole getConsole() {
127         this.@console
128     }
129 
130     @TaskAction
131     void executeTask() {
132         System.setProperty('sun.net.http.allowRestrictedHeaders', 'true')
133         System.setProperty('oci.javasdk.extra.stream.logs.enabled', resolvedDisplayStreamLogs.getOrElse(false).toString())
134 
135         doExecuteTask()
136 
137         closeables.each c -> c.close() }
138         closeables.clear()
139     }
140 
141     abstract protected void doExecuteTask()
142 
143     protected AuthenticationDetailsProvider resolveAuthenticationDetailsProvider() {
144         if (authenticationDetailsProvider) {
145             return authenticationDetailsProvider
146         }
147 
148         if (ociConfig.empty) {
149             ConfigFileReader.ConfigFile configFile = ConfigFileReader.parse(CONFIG_LOCATION, getResolvedProfile().get())
150             ConfigFileAuthenticationDetailsProvider provider = new ConfigFileAuthenticationDetailsProvider(configFile)
151             setRegion(provider.region.regionId)
152             return provider
153         }
154 
155         List<String> errors = []
156         if (!ociConfig.userId.present) {
157             errors << "Missing value for 'ociConfig.userId' for Task $path".toString()
158         }
159         if (!ociConfig.tenantId.present) {
160             errors << "Missing value for 'ociConfig.tenantId' for Task $path".toString()
161         }
162         if (!ociConfig.fingerprint.present) {
163             errors << "Missing value for 'ociConfig.fingerprint' for Task $path".toString()
164         }
165         if (!ociConfig.region.present) {
166             errors << "Missing value for 'ociConfig.region' for Task $path".toString()
167         }
168         if (!ociConfig.keyfile.present) {
169             errors << "Missing value for 'ociConfig.keyfile' for Task $path".toString()
170         }
171 
172         if (errors.size() 0) {
173             throw new IllegalStateException(errors.join('\n'))
174         }
175 
176         authenticationDetailsProvider = SimpleAuthenticationDetailsProvider.builder()
177             .userId(ociConfig.userId.get())
178             .tenantId(ociConfig.tenantId.get())
179             .fingerprint(ociConfig.fingerprint.get())
180             .region(Region.fromRegionId(ociConfig.region.get()))
181             .privateKeySupplier(new Supplier<InputStream>() {
182                 @Override
183                 InputStream get() {
184                     new FileInputStream(ociConfig.keyfile.asFile.get())
185                 }
186             })
187             .passPhrase(ociConfig.passphrase.present ? ociConfig.passphrase.get() '')
188             .build()
189 
190         authenticationDetailsProvider
191     }
192 
193     @Override
194     protected void doPrintMapEntry(String key, value, int offset) {
195         if (value instanceof CharSequence) {
196             if (isNotBlank((String.valueOf(value)))) {
197                 super.doPrintMapEntry(key, value, offset)
198             }
199         else {
200             super.doPrintMapEntry(key, value, offset)
201         }
202     }
203 
204     @Override
205     void printKeyValue(String key, Object value, int offset) {
206         doPrintMapEntry(key, value, offset)
207     }
208 
209     @Override
210     void printMap(String key, Map<String, ?> map, int offset) {
211         if (map && !map.isEmpty()) {
212             println(('    ' * offset+ key + ':')
213             doPrintMap(map, offset + 1)
214         }
215     }
216 
217     @Override
218     void printCollection(String key, Collection<?> collection, int offset) {
219         if (collection && !collection.isEmpty()) {
220             println(('    ' * offset+ key + ':')
221             doPrintCollection(collection, offset + 1)
222         }
223     }
224 
225     @Override
226     String state(String state) {
227         if (isNotBlank(state)) {
228             switch (state) {
229                 case 'Creating':
230                 case 'Provisioning':
231                 case 'Restoring':
232                 case 'Importing':
233                 case 'Exporting':
234                 case 'Starting':
235                 case 'CreatingImage':
236                 case 'InProgress':
237                 case 'Canceling':
238                 case 'Deleting':
239                 case 'Terminating':
240                 case 'Moving':
241                     return console.yellow(state)
242                 case 'Available':
243                 case 'Running':
244                 case 'Active':
245                 case 'Completed':
246                     return console.green(state)
247                 case 'Inactive':
248                 case 'Stopping':
249                 case 'Stopped':
250                 case 'Accepted':
251                     return console.cyan(state)
252                 case 'Disabled':
253                 case 'Deleted':
254                 case 'Terminated':
255                 case 'Faulty':
256                 case 'Failed':
257                 case 'Canceled':
258                     return console.red(state)
259             }
260         }
261         state
262     }
263 
264     static String format(Date date) {
265         return new SimpleDateFormat(TIMESTAMP_FORMAT).format(date)
266     }
267 
268     protected IdentityClient createIdentityClient() {
269         IdentityClient client = new IdentityClient(resolveAuthenticationDetailsProvider())
270         if (isNotBlank(getResolvedRegion().orNull)) {
271             client.setRegion(getResolvedRegion().get())
272         }
273         closeables << client
274         client
275     }
276 
277     protected ComputeClient createComputeClient() {
278         ComputeClient client = new ComputeClient(resolveAuthenticationDetailsProvider())
279         if (isNotBlank(getResolvedRegion().orNull)) {
280             client.setRegion(getResolvedRegion().get())
281         }
282         closeables << client
283         client
284     }
285 
286     protected VirtualNetworkClient createVirtualNetworkClient() {
287         VirtualNetworkClient client = new VirtualNetworkClient(resolveAuthenticationDetailsProvider())
288         if (isNotBlank(getResolvedRegion().orNull)) {
289             client.setRegion(getResolvedRegion().get())
290         }
291         closeables << client
292         client
293     }
294 
295     protected BlockstorageClient createBlockstorageClient() {
296         BlockstorageClient client = new BlockstorageClient(resolveAuthenticationDetailsProvider())
297         if (isNotBlank(getResolvedRegion().orNull)) {
298             client.setRegion(getResolvedRegion().get())
299         }
300         closeables << client
301         client
302     }
303 
304     protected ResourceSearchClient createResourceSearchClient() {
305         ResourceSearchClient client = new ResourceSearchClient(resolveAuthenticationDetailsProvider())
306         if (isNotBlank(getResolvedRegion().orNull)) {
307             client.setRegion(getResolvedRegion().get())
308         }
309         closeables << client
310         client
311     }
312 
313     protected ObjectStorageClient createObjectStorageClient() {
314         ObjectStorageClient client = new ObjectStorageClient(resolveAuthenticationDetailsProvider())
315         if (isNotBlank(getResolvedRegion().orNull)) {
316             client.setRegion(getResolvedRegion().get())
317         }
318         closeables << client
319         client
320     }
321 
322     protected ObjectStorageAsyncClient createObjectStorageAsyncClient() {
323         ObjectStorageAsyncClient client = new ObjectStorageAsyncClient(resolveAuthenticationDetailsProvider())
324         if (isNotBlank(getResolvedRegion().orNull)) {
325             client.setRegion(getResolvedRegion().get())
326         }
327         closeables << client
328         client
329     }
330 
331     protected DatabaseClient createDatabaseClient() {
332         DatabaseClient client = new DatabaseClient(resolveAuthenticationDetailsProvider())
333         if (isNotBlank(getResolvedRegion().orNull)) {
334             client.setRegion(getResolvedRegion().get())
335         }
336         closeables << client
337         client
338     }
339 
340     protected GatewayClient createGatewayClient() {
341         GatewayClient client = new GatewayClient(resolveAuthenticationDetailsProvider())
342         if (isNotBlank(getResolvedRegion().orNull)) {
343             client.setRegion(getResolvedRegion().get())
344         }
345         closeables << client
346         client
347     }
348 
349     protected DeploymentClient createDeploymentClient() {
350         DeploymentClient client = new DeploymentClient(resolveAuthenticationDetailsProvider())
351         if (isNotBlank(getResolvedRegion().orNull)) {
352             client.setRegion(getResolvedRegion().get())
353         }
354         closeables << client
355         client
356     }
357 }