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 }
|