diff --git a/build.gradle b/build.gradle index e4dea1f45..55373dbce 100644 --- a/build.gradle +++ b/build.gradle @@ -74,13 +74,6 @@ subprojects { project -> testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") testRuntimeOnly("org.objenesis:objenesis") } - // Keep this constraint till all other components get bumped up to this version and subsequently updated in the kork. - configurations.all { - resolutionStrategy.force 'com.google.apis:google-api-services-storage:v1-rev20200326-1.30.9' - resolutionStrategy.force 'com.google.auth:google-auth-library-oauth2-http:0.20.0' - // TODO(plumpy): remove version once added to kork - resolutionStrategy.force 'com.google.cloud:google-cloud-storage:1.108.0' - } } } diff --git a/front50-gcs/src/main/kotlin/com/netflix/spinnaker/front50/model/GcsStorageService.kt b/front50-gcs/src/main/kotlin/com/netflix/spinnaker/front50/model/GcsStorageService.kt index adcdff6ac..58abab87e 100644 --- a/front50-gcs/src/main/kotlin/com/netflix/spinnaker/front50/model/GcsStorageService.kt +++ b/front50-gcs/src/main/kotlin/com/netflix/spinnaker/front50/model/GcsStorageService.kt @@ -31,6 +31,8 @@ import com.google.common.collect.ImmutableMap import com.google.common.util.concurrent.Futures import com.netflix.spinnaker.front50.api.model.Timestamped import com.netflix.spinnaker.kork.web.exceptions.NotFoundException +import net.logstash.logback.argument.StructuredArguments +import org.slf4j.LoggerFactory import java.io.IOException import java.time.Duration import java.util.concurrent.ExecutorService @@ -38,8 +40,6 @@ import java.util.concurrent.Future import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException import javax.annotation.PostConstruct -import net.logstash.logback.argument.StructuredArguments -import org.slf4j.LoggerFactory class GcsStorageService( private val storage: Storage, @@ -206,7 +206,11 @@ class GcsStorageService( try { // Calling update() is enough to change the modification time on the file, which is all we // care about. It doesn't matter if we don't actually specify any fields to change. - storage.update(blobInfo) + + //if last-modified does not exist, throw exception to create it + val lastModified = storage.get(lastModifiedBlobId(objectType)) ?: throw StorageException(404, "no object $blobInfo.bucket/$blobInfo.name") + val lastUpdated = lastModified.toBuilder().setMetadata(mapOf("updateTrigger" to System.currentTimeMillis().toString())).build() + storage.update(lastUpdated) } catch (e: Exception) { when { e is StorageException && e.code == 404 -> diff --git a/front50-gcs/src/test/kotlin/com/netflix/spinnaker/front50/model/FakeStorageRpc.kt b/front50-gcs/src/test/kotlin/com/netflix/spinnaker/front50/model/FakeStorageRpc.kt index 0fcc19b5c..ed2683194 100644 --- a/front50-gcs/src/test/kotlin/com/netflix/spinnaker/front50/model/FakeStorageRpc.kt +++ b/front50-gcs/src/test/kotlin/com/netflix/spinnaker/front50/model/FakeStorageRpc.kt @@ -18,6 +18,7 @@ package com.netflix.spinnnaker.front50.model import com.google.api.client.util.DateTime +import com.google.api.services.storage.Storage import com.google.api.services.storage.model.Bucket import com.google.api.services.storage.model.BucketAccessControl import com.google.api.services.storage.model.HmacKey @@ -103,13 +104,12 @@ internal class FakeStorageRpc(private val clock: Clock) : StorageRpc { override fun patch(storageObject: StorageObject, options: MutableMap): StorageObject { if (options.isNotEmpty()) throw UnsupportedOperationException("unsupported options to patch: ${options.keys}") - if (storageObject.generation != null) throw UnsupportedOperationException("can't call patch with a specific generation") val blobs = buckets[storageObject.bucket] ?: throw StorageException(404, "bucket ${storageObject.bucket} does not exist") val generations = blobs.getGenerations(storageObject) if (generations.isEmpty()) throw StorageException(404, "no object ${fullPath(storageObject)}") val foundObject = generations[generations.size - 1].storageObject - foundObject.updated = DateTime(clock.millis()) storageObject.forEach { (key, value) -> foundObject.set(key, value) } + foundObject.updated = DateTime(clock.millis()) return foundObject } @@ -192,10 +192,26 @@ internal class FakeStorageRpc(private val clock: Clock) : StorageRpc { TODO("Not yet implemented") } + override fun getStorage(): Storage { + TODO("Not yet implemented") + } + override fun write(uploadId: String?, toWrite: ByteArray?, toWriteOffset: Int, destOffset: Long, length: Int, last: Boolean) { TODO("Not yet implemented") } + override fun getCurrentUploadOffset(uploadId: String?): Long { + TODO("Not yet implemented") + } + + override fun queryCompletedResumableUpload(uploadId: String?, totalBytes: Long): StorageObject { + TODO("Not yet implemented") + } + + override fun writeWithResponse(uploadId: String?, toWrite: ByteArray?, toWriteOffset: Int, destOffset: Long, length: Int, last: Boolean): StorageObject { + TODO("Not yet implemented") + } + override fun create(bucket: Bucket?, options: MutableMap?): Bucket { val name = bucket?.name ?: throw java.lang.UnsupportedOperationException("bucket name must be specified") if (buckets.exists(name)) throw StorageException(409, "bucket $bucket already exists") @@ -264,6 +280,10 @@ internal class FakeStorageRpc(private val clock: Clock) : StorageRpc { TODO("Not yet implemented") } + override fun getNotification(bucket: String?, id: String?): Notification { + TODO("Not yet implemented") + } + override fun listAcls(bucket: String?, options: MutableMap?): MutableList { TODO("Not yet implemented") } diff --git a/front50-gcs/src/test/kotlin/com/netflix/spinnaker/front50/model/TestGcsStorageService.kt b/front50-gcs/src/test/kotlin/com/netflix/spinnaker/front50/model/TestGcsStorageService.kt index d47dcf706..1ae7ad903 100644 --- a/front50-gcs/src/test/kotlin/com/netflix/spinnaker/front50/model/TestGcsStorageService.kt +++ b/front50-gcs/src/test/kotlin/com/netflix/spinnaker/front50/model/TestGcsStorageService.kt @@ -18,6 +18,7 @@ package com.netflix.spinnnaker.front50.model import com.fasterxml.jackson.databind.ObjectMapper +import com.google.cloud.storage.Blob import com.google.cloud.storage.BlobId import com.google.cloud.storage.BlobInfo import com.google.cloud.storage.Bucket @@ -511,6 +512,16 @@ class GcsStorageServiceTest { val finishUpdate = lock.newCondition() val updateTaskCompleted = lock.newCondition() + val lastModified: Blob = mockk() + val blobBuilder: Blob.Builder = mockk() + val updatedBlob: Blob = mockk() + + every { lastModified.toBuilder() } returns blobBuilder + every { blobBuilder.setMetadata(any()) } returns blobBuilder + every { blobBuilder.build() } returns updatedBlob + + every { gcs.get(any()) } answers { lastModified } + // When the service tries to update last-modified, hold until we call `finishUpdate.signal()` every { gcs.update(any()) } answers { lock.lock() diff --git a/gradle.properties b/gradle.properties index 1634626b9..cc69fcbde 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ fiatVersion=1.50.0 includeProviders=azure,gcs,oracle,redis,s3,swift,sql -korkVersion=7.241.0 +korkVersion=7.242.0 org.gradle.parallel=true spinnakerGradleVersion=8.32.1 targetJava17=false