All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pl.allegro.tech.servicemesh.envoycontrol.snapshot.debug.SnapshotDebugController.kt Maven / Gradle / Ivy

package pl.allegro.tech.servicemesh.envoycontrol.snapshot.debug

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.SerializerProvider
import com.google.protobuf.Any
import com.google.protobuf.BoolValue
import com.google.protobuf.Duration
import com.google.protobuf.Message
import com.google.protobuf.Struct
import com.google.protobuf.Value
import com.google.protobuf.util.JsonFormat
import com.google.protobuf.util.JsonFormat.TypeRegistry
import io.envoyproxy.envoy.config.rbac.v3.RBAC
import io.envoyproxy.envoy.extensions.filters.http.header_to_metadata.v3.Config
import io.envoyproxy.envoy.extensions.filters.http.lua.v3.Lua
import io.envoyproxy.envoy.extensions.filters.http.router.v3.Router
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
import io.envoyproxy.envoy.extensions.retry.host.omit_canary_hosts.v3.OmitCanaryHostsPredicate
import io.envoyproxy.envoy.extensions.retry.host.omit_host_metadata.v3.OmitHostMetadataConfig
import io.envoyproxy.envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate
import io.envoyproxy.envoy.extensions.transport_sockets.tap.v3.Tap
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
import io.envoyproxy.envoy.type.matcher.PathMatcher
import io.envoyproxy.envoy.type.matcher.StringMatcher
import org.springframework.boot.jackson.JsonComponent
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseBody
import org.springframework.web.bind.annotation.RestController
import pl.allegro.tech.servicemesh.envoycontrol.groups.Group
import io.envoyproxy.envoy.config.core.v3.Node as NodeV3
import io.envoyproxy.envoy.extensions.filters.http.rbac.v3.RBAC as RBACFilter

@RestController
class SnapshotDebugController(val debugService: SnapshotDebugService) {

    /**
     * Returns a textual representation of the snapshot for debugging purposes.
     * It contains the versions of XDS resources and the contents for a provided node JSON
     * extracted from Envoy's config_dump endpoint.
     */
    @PostMapping(
        value = ["/snapshot"],
        consumes = ["application/json"],
        produces = ["application/v3+json", "application/json"]
    )
    fun snapshot(@RequestBody node: NodeV3): ResponseEntity {
        return ResponseEntity(
            debugService.snapshot(node),
            HttpStatus.OK
        )
    }

    @GetMapping("/groups")
    fun globalSnapshot(@RequestParam(required = false) discoveryServiceName: String?)
        : ResponseEntity> {
        return ResponseEntity(
            debugService.groups(discoveryServiceName),
            HttpStatus.OK
        )
    }

    @GetMapping("/snapshot-global")
    fun globalSnapshot(@RequestParam(defaultValue = "false") xds: Boolean): ResponseEntity {
        return ResponseEntity(
            debugService.globalSnapshot(xds),
            HttpStatus.OK
        )
    }

    @GetMapping("/snapshot-global/{service}")
    fun globalSnapshot(
        @PathVariable service: String,
        @RequestParam dc: String?,
        @RequestParam(defaultValue = "false") xds: Boolean
    ): ResponseEntity {
        return ResponseEntity(
            debugService.globalSnapshot(service, dc, xds),
            HttpStatus.OK
        )
    }

    @JsonComponent
    class ProtoSerializer : JsonSerializer() {
        final val typeRegistry: TypeRegistry = TypeRegistry.newBuilder()
            .add(HttpConnectionManager.getDescriptor())
            .add(Config.getDescriptor())
            .add(BoolValue.getDescriptor())
            .add(Duration.getDescriptor())
            .add(Struct.getDescriptor())
            .add(Value.getDescriptor())
            .add(RBAC.getDescriptor())
            .add(RBACFilter.getDescriptor())
            .add(Any.getDescriptor())
            .add(PathMatcher.getDescriptor())
            .add(StringMatcher.getDescriptor())
            .add(Router.getDescriptor())
            .add(PreviousHostsPredicate.getDescriptor())
            .add(OmitCanaryHostsPredicate.getDescriptor())
            .add(OmitHostMetadataConfig.getDescriptor())
            .add(Tap.getDescriptor())
            .add(UpstreamTlsContext.getDescriptor())
            .add(Lua.getDescriptor())
            .build()

        val printer: JsonFormat.Printer = JsonFormat.printer().usingTypeRegistry(typeRegistry)

        override fun serialize(message: Message, gen: JsonGenerator, serializers: SerializerProvider) {
            gen.writeRawValue(printer.print(message))
        }
    }

    @ExceptionHandler
    @ResponseBody
    fun handleSnapshotMissing(exception: SnapshotNotFoundException): ResponseEntity = ResponseEntity
        .status(HttpStatus.NOT_FOUND)
        .body(exception.message)

    @ExceptionHandler
    @ResponseBody
    fun handleGlobalSnapshotMissing(exception: GlobalSnapshotNotFoundException): ResponseEntity = ResponseEntity
        .status(HttpStatus.NOT_FOUND)
        .body(exception.message)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy