gitlab4j-api icon indicating copy to clipboard operation
gitlab4j-api copied to clipboard

Add endpoint to create a gitlab runner the new way (POST /user/runners)

Open pascaldornfeld opened this issue 10 months ago • 2 comments

Since Gitlab 16.0 there is a new way to create Gitlab Runners (https://docs.gitlab.com/security/tokens/#runner-authentication-tokens). The old way is deprecated and some gitlab instances have it disabled by the admin. The new way is not yet implemented in gitlab4j.

The endpoint to create a gitlab runner is in the UsersApi: POST /user/runners https://docs.gitlab.com/api/users/#create-a-runner-linked-to-a-user. Here is the UserApi in gitlab4j: https://github.com/gitlab4j/gitlab4j-api/blob/main/gitlab4j-api/src/main/java/org/gitlab4j/api/UserApi.java.

Here some details how the old and new way work when you want to create a new group runner: Old way: You go to the group page and copy the registration token, that is the same for every runner in the group. You use the gitlab runner cli to register the runner with that registration token and provide all metadata in the runner (tags, description, ...). The cli will generate a runner token and throw that in the config.toml. New way: From the group page you create a new gitlab runner. You get a token that is unique for the runner. You provide the metadata in the ui. The gitlab runner cli is not needed. You just add the token in your gitlab runner config.toml.

pascaldornfeld avatar May 12 '25 11:05 pascaldornfeld

I extended the userapi in my project and it works:

class ExtendedUserApi(gitLabApi: GitLabApi) : UserApi(gitLabApi) {
    /**
     * Create a gitlab runner the new way https://docs.gitlab.com/api/users/#create-a-runner-linked-to-a-user
     */
    fun createRunner(
        runnerType: RunnerType,
        groupId: Int? = null,
        projectId: Int? = null,
        description: String? = null,
        paused: Boolean? = null,
        locked: Boolean? = null,
        runUntagged: Boolean? = null,
        tagList: List<String>? = null,
        accessLevel: String? = null,
        maximumTimeout: Int? = null,
        maintenanceNote: String? = null,
    ): CreateRunnerResponse {
        val formData = GitLabApiForm()
            .withParam("runner_type", runnerType)
            .withParam("group_id", groupId)
            .withParam("project_id", projectId)
            .withParam("description", description)
            .withParam("paused", paused)
            .withParam("locked", locked)
            .withParam("run_untagged", runUntagged)
            .withParam("tag_list", tagList)
            .withParam("access_level", accessLevel)
            .withParam("maximum_timeout", maximumTimeout)
            .withParam("maintenance_note", maintenanceNote)
        val response = post(Response.Status.OK, formData.asMap(), "user", "runners")
        return response.readEntity(CreateRunnerResponse::class.java)
    }

    data class CreateRunnerResponse(
        val id: Int = 0,
        val token: String = "",
        val tokenExpiresAt: Date? = null,
    )
}

val userApi = ExtendedUserApi(gitLabApi)
userApi.createRunner(RunnerType.GROUP_TYPE, 1234, tagList = listOf("test"))

pascaldornfeld avatar May 12 '25 11:05 pascaldornfeld

This sounds like a good addition.

If you convert it back to Java it can be integrated to this project. Feel free to create a pull request

jmini avatar May 12 '25 19:05 jmini