Skip to content

Commit d2fd339

Browse files
committed
Improve state handling in OciRegistriesService
1 parent ba04d57 commit d2fd339

1 file changed

Lines changed: 57 additions & 36 deletions

File tree

src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/dsl/OciRegistriesImpl.kt

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import io.netty.buffer.UnpooledByteBufAllocator
1616
import io.netty.channel.ChannelOption
1717
import io.netty.util.concurrent.FastThreadLocal
1818
import org.gradle.api.Action
19-
import org.gradle.api.NamedDomainObjectList
2019
import org.gradle.api.Project
2120
import org.gradle.api.artifacts.ConfigurationContainer
2221
import org.gradle.api.artifacts.ResolvableDependencies
@@ -144,8 +143,8 @@ private const val PORT_HTTP_HEADER_NAME = "port"
144143
internal fun OciRegistriesService(
145144
buildServiceRegistry: BuildServiceRegistry,
146145
name: String,
147-
registries: NamedDomainObjectList<OciRegistry>,
148-
repositoryPort: Property<Int>,
146+
registries: List<OciRegistry>,
147+
repositoryPort: Provider<Int>,
149148
imageMapping: OciImageMappingImpl,
150149
): OciRegistriesService {
151150
val registriesService = buildServiceRegistry.registerIfAbsent(name, OciRegistriesService::class) {}.get()
@@ -154,45 +153,65 @@ internal fun OciRegistriesService(
154153
}
155154

156155
internal abstract class OciRegistriesService : BuildService<BuildServiceParameters.None>, AutoCloseable {
157-
lateinit var registries: NamedDomainObjectList<OciRegistry>
158-
lateinit var repositoryPort: Property<Int>
159-
lateinit var imageMapping: OciImageMappingImpl
160-
private var isStarted = false
161-
private val httpServers = mutableListOf<DisposableServer>()
156+
private var state: State? = null
157+
158+
sealed class State {
159+
class Initialized(
160+
val registries: List<OciRegistry>,
161+
val repositoryPort: Provider<Int>,
162+
val imageMapping: OciImageMappingImpl,
163+
) : State()
164+
165+
class Started(
166+
val httpServers: List<DisposableServer>,
167+
) : State()
162168

163-
fun init(
164-
registries: NamedDomainObjectList<OciRegistry>,
165-
repositoryPort: Property<Int>,
166-
imageMapping: OciImageMappingImpl,
167-
) {
168-
this.registries = registries
169-
this.repositoryPort = repositoryPort
170-
this.imageMapping = imageMapping
169+
object NoRegistries : State()
170+
171+
object Closed : State()
172+
}
173+
174+
fun init(registries: List<OciRegistry>, repositoryPort: Provider<Int>, imageMapping: OciImageMappingImpl) {
175+
if (state != null) {
176+
throw IllegalStateException()
177+
}
178+
state = State.Initialized(registries, repositoryPort, imageMapping)
171179
}
172180

173181
fun start() {
174-
if (isStarted) {
182+
val currentState = state
183+
if (currentState !is State.Initialized) {
184+
if ((currentState == null) || (currentState == State.Closed)) {
185+
throw IllegalStateException()
186+
}
175187
return
176188
}
177-
isStarted = true
178-
if (registries.isNotEmpty()) {
189+
state = if (currentState.registries.isEmpty()) {
190+
State.NoRegistries
191+
} else {
192+
val httpServers = mutableListOf<DisposableServer>()
179193
val loopResources = OciLoopResources.acquire()
180-
startRedirectServer(repositoryPort.get(), loopResources)
194+
val redirectServer = startRedirectServer(currentState.repositoryPort.get(), loopResources)
195+
if (redirectServer != null) {
196+
httpServers += redirectServer
197+
}
181198
val imageMetadataRegistry = OciImageMetadataRegistry(OciRegistryApi(OciRegistryHttpClient.acquire()))
182-
val imageMappingData = imageMapping.getData()
183-
for (registry in registries) {
184-
startRegistryServer(registry, loopResources, imageMetadataRegistry, imageMappingData)
199+
val imageMappingData = currentState.imageMapping.getData()
200+
for (registry in currentState.registries) {
201+
httpServers += startRegistryServer(registry, loopResources, imageMetadataRegistry, imageMappingData)
185202
}
203+
State.Started(httpServers)
186204
}
187205
}
188206

189-
private fun startRedirectServer(port: Int, loopResources: LoopResources) {
190-
try {
207+
private fun startRedirectServer(port: Int, loopResources: LoopResources): DisposableServer? {
208+
return try {
191209
startHttpServer(port, loopResources) { request, response ->
192210
val redirectPort = request.requestHeaders()[PORT_HTTP_HEADER_NAME]
193211
response.sendRedirect("http://localhost:" + redirectPort + request.uri())
194212
}
195213
} catch (_: ChannelBindException) {
214+
null
196215
}
197216
}
198217

@@ -201,20 +220,21 @@ internal abstract class OciRegistriesService : BuildService<BuildServiceParamete
201220
loopResources: LoopResources,
202221
imageMetadataRegistry: OciImageMetadataRegistry,
203222
imageMappingData: OciImageMappingData,
204-
) {
223+
): DisposableServer {
205224
val credentials = registry.credentials.orNull?.let { Credentials(it.username!!, it.password!!) }
206-
val port = startHttpServer(
225+
val httpServer = startHttpServer(
207226
0,
208227
loopResources,
209228
OciRepositoryHandler(imageMetadataRegistry, imageMappingData, credentials),
210-
).port()
229+
)
211230
registry.repository.credentials(HttpHeaderCredentials::class) {
212231
name = PORT_HTTP_HEADER_NAME
213-
value = port.toString()
232+
value = httpServer.port().toString()
214233
}
215234
registry.repository.authentication {
216235
create<HttpHeaderAuthentication>("header")
217236
}
237+
return httpServer
218238
}
219239

220240
private fun startHttpServer(
@@ -223,14 +243,12 @@ internal abstract class OciRegistriesService : BuildService<BuildServiceParamete
223243
handler: BiFunction<in HttpServerRequest, in HttpServerResponse, out Publisher<Void>>,
224244
): DisposableServer {
225245
return try {
226-
val httpServer = HttpServer.create()
246+
HttpServer.create()
227247
.bindAddress { InetSocketAddress("localhost", port) }
228248
.runOn(loopResources)
229249
.childOption(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT)
230250
.handle(handler)
231251
.bindNow()
232-
httpServers += httpServer
233-
httpServer
234252
} finally {
235253
// Netty adds a thread local to the current thread that then retains a reference to the current classloader.
236254
// The current classloader can then not be collected, although it has a narrower scope then the current thread.
@@ -239,14 +257,15 @@ internal abstract class OciRegistriesService : BuildService<BuildServiceParamete
239257
}
240258

241259
final override fun close() {
242-
if (httpServers.isNotEmpty()) {
243-
for (httpServer in httpServers) {
260+
val currentState = state
261+
if (currentState is State.Started) {
262+
for (httpServer in currentState.httpServers) {
244263
httpServer.disposeNow()
245264
}
246-
httpServers.clear()
247265
OciRegistryHttpClient.release()
248266
OciLoopResources.release()
249267
}
268+
state = State.Closed
250269
}
251270
}
252271

@@ -271,9 +290,11 @@ internal fun setupProjectOciRegistries(
271290
registries: OciRegistries,
272291
imageMapping: OciImageMappingImpl,
273292
) {
293+
var isOciRegistriesStarted = false
274294
configurationContainer.configureEach {
275295
incoming.beforeResolve {
276-
if (resolvesOciImages()) {
296+
if (!isOciRegistriesStarted && resolvesOciImages()) {
297+
isOciRegistriesStarted = true
277298
val settingsRegistration = buildServiceRegistry.registrations.findByName(SERVICE_BASE_NAME)
278299
if (settingsRegistration != null) {
279300
(settingsRegistration.service.get() as OciRegistriesService).start()

0 commit comments

Comments
 (0)