Skip to content

Commit

Permalink
New frontend: add destinations APIs (#294)
Browse files Browse the repository at this point in the history
This PR is another part in our transition to a new UI (both backend and
frontend).
We now use YAML files (Kubernetes style) for persisting destination
details
  • Loading branch information
edeNFed authored Jul 13, 2023
1 parent 94bd377 commit 301f43b
Show file tree
Hide file tree
Showing 9 changed files with 319 additions and 2 deletions.
32 changes: 31 additions & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- main

jobs:
build:
build-autoscaler:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand All @@ -18,32 +18,62 @@ jobs:
push: false
tags: autoscaler:pr-${{ github.event.number }}
build-args: SERVICE_NAME=autoscaler
build-scheduler:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build Scheduler Image
uses: docker/build-push-action@v4
with:
push: false
tags: scheduler:pr-${{ github.event.number }}
build-args: SERVICE_NAME=scheduler
build-instrumentor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build Instrumentor Image
uses: docker/build-push-action@v4
with:
push: false
tags: instrumentor:pr-${{ github.event.number }}
build-args: SERVICE_NAME=instrumentor
build-odiglet:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build Odiglet Image
uses: docker/build-push-action@v4
with:
file: odiglet/Dockerfile
context: .
push: false
tags: odiglet:pr-${{ github.event.number }}
build-ui:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build UI Image
uses: docker/build-push-action@v4
with:
file: ui/Dockerfile
context: ui/
push: false
tags: ui:pr-${{ github.event.number }}
build-frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build Frontend Image
uses: docker/build-push-action@v4
with:
Expand Down
22 changes: 22 additions & 0 deletions frontend/destinations/data/chronosphere.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
apiVersion: internal.odigos.io/v1beta1
kind: Destination
metadata:
type: chronosphere
displayName: Chronosphere
category: managed
spec:
image: chronosphere.svg
signals:
traces:
supported: true
metrics:
supported: true
logs:
supported: false
fields:
- name: collector_endpoint
displayName: Collector Endpoint
videoUrl: https://www.youtube.com/watch?v=9QZxw-mtZmU
componentType: input
componentProps:
type: text
34 changes: 34 additions & 0 deletions frontend/destinations/data/datadog.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
apiVersion: internal.odigos.io/v1beta1
kind: Destination
metadata:
type: datadog
displayName: Datadog
category: managed
spec:
image: datadog.svg
signals:
traces:
supported: true
metrics:
supported: true
logs:
supported: true
fields:
- name: api_key
displayName: API Key
videoUrl: https://www.youtube.com/watch?v=9QZxw-mtZmU
componentType: input
componentProps:
type: password
- name: site
displayName: Site
videoUrl: https://www.youtube.com/watch?v=9QZxw-mtZmU
componentType: dropdown
componentProps:
values:
- us3.datadoghq.com
- datadoghq.com
- us5.datadoghq.com
- datadoghq.eu
- ddog-gov.com
- ap1.datadoghq.com
22 changes: 22 additions & 0 deletions frontend/destinations/data/jaeger.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
apiVersion: internal.odigos.io/v1beta1
kind: Destination
metadata:
type: jaeger
displayName: Jaeger
category: self hosted
spec:
image: jaeger.svg
signals:
traces:
supported: true
metrics:
supported: false
logs:
supported: false
fields:
- name: endpoint
displayName: Endpoint
videoUrl: https://www.youtube.com/watch?v=9QZxw-mtZmU
componentType: input
componentProps:
type: text
49 changes: 49 additions & 0 deletions frontend/destinations/load.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package destinations

import (
"embed"

"gopkg.in/yaml.v3"
)

//go:embed data/*
var destsFS embed.FS

var loadedDestinations []Destination

func Load() error {
return load(destsFS)
}

func Get() []Destination {
return loadedDestinations
}

func load(fs embed.FS) error {
var dests []Destination

// load all files in the data directory
files, err := fs.ReadDir("data")
if err != nil {
return err
}

for _, file := range files {
// load each file
bytesData, err := fs.ReadFile("data/" + file.Name())
if err != nil {
return err
}

var dest Destination
err = yaml.Unmarshal(bytesData, &dest)
if err != nil {
return err
}

dests = append(dests, dest)
}

loadedDestinations = dests
return nil
}
38 changes: 38 additions & 0 deletions frontend/destinations/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package destinations

type Destination struct {
ApiVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
Metadata Metadata `yaml:"metadata"`
Spec Spec `yaml:"spec"`
}

type Metadata struct {
Type string `yaml:"type"`
DisplayName string `yaml:"displayName"`
Category string `yaml:"category"`
}

type Spec struct {
Image string `yaml:"image"`
Signals struct {
Traces struct {
Supported bool `yaml:"supported"`
}
Metrics struct {
Supported bool `yaml:"supported"`
}
Logs struct {
Supported bool `yaml:"supported"`
}
}
Fields []Field `yaml:"fields"`
}

type Field struct {
Name string `yaml:"name"`
DisplayName string `yaml:"displayName"`
VideoURL string `yaml:"videoUrl"`
ComponentType string `yaml:"componentType"`
ComponentProps map[string]interface{} `yaml:"componentProps"`
}
103 changes: 103 additions & 0 deletions frontend/endpoints/destinations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package endpoints

import (
"github.com/gin-gonic/gin"
"github.com/keyval-dev/odigos/frontend/destinations"
)

type GetDestinationsResponse struct {
Categories []DestinationsCategory `json:"categories"`
}

type DestinationsCategory struct {
Name string `json:"name"`
Items []DestinationsCategoryItem `json:"items"`
}

type DestinationsCategoryItem struct {
Type string `json:"type"`
DisplayName string `json:"display_name"`
ImageUrl string `json:"image_url"`
SupportedSignals SupportedSignals `json:"supported_signals"`
}

type SupportedSignals struct {
Traces ObservabilitySignal `json:"traces"`
Metrics ObservabilitySignal `json:"metrics"`
Logs ObservabilitySignal `json:"logs"`
}

type ObservabilitySignal struct {
Supported bool `json:"supported"`
}

func GetDestinations(c *gin.Context) {
var resp GetDestinationsResponse
itemsByCategory := make(map[string][]DestinationsCategoryItem)
for _, dest := range destinations.Get() {
item := DestinationsCategoryItem{
Type: dest.Metadata.Type,
DisplayName: dest.Metadata.DisplayName,
ImageUrl: GetImageURL(dest.Spec.Image),
SupportedSignals: SupportedSignals{
Traces: ObservabilitySignal{
Supported: dest.Spec.Signals.Traces.Supported,
},
Metrics: ObservabilitySignal{
Supported: dest.Spec.Signals.Metrics.Supported,
},
Logs: ObservabilitySignal{
Supported: dest.Spec.Signals.Logs.Supported,
},
},
}

itemsByCategory[dest.Metadata.Category] = append(itemsByCategory[dest.Metadata.Category], item)
}

for category, items := range itemsByCategory {
resp.Categories = append(resp.Categories, DestinationsCategory{
Name: category,
Items: items,
})
}

c.JSON(200, resp)
}

type GetDestinationDetailsResponse struct {
Fields []Field `json:"fields"`
}

type Field struct {
Name string `json:"name"`
DisplayName string `json:"display_name"`
ComponentType string `json:"component_type"`
ComponentProperties map[string]interface{} `json:"component_properties"`
VideoUrl string `json:"video_url"`
}

func GetDestinationDetails(c *gin.Context) {
destType := c.Param("type")
for _, dest := range destinations.Get() {
if dest.Metadata.Type == destType {
var resp GetDestinationDetailsResponse
for _, field := range dest.Spec.Fields {
resp.Fields = append(resp.Fields, Field{
Name: field.Name,
DisplayName: field.DisplayName,
ComponentType: field.ComponentType,
ComponentProperties: field.ComponentProps,
VideoUrl: field.VideoURL,
})
}

c.JSON(200, resp)
return
}
}

c.JSON(404, gin.H{
"error": "destination not found",
})
}
9 changes: 9 additions & 0 deletions frontend/endpoints/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package endpoints

import "path"

const cdnUrl = "https://d15jtxgb40qetw.cloudfront.net"

func GetImageURL(image string) string {
return path.Join(cdnUrl, image)
}
12 changes: 11 additions & 1 deletion frontend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"net/http"
"path/filepath"

"github.com/keyval-dev/odigos/frontend/destinations"

"github.com/gin-contrib/cors"

"github.com/keyval-dev/odigos/frontend/kube"
Expand Down Expand Up @@ -88,6 +90,8 @@ func startHTTPServer(flags *Flags) (*gin.Engine, error) {
apis.POST("/namespaces", endpoints.PersistNamespaces)
apis.GET("/applications/:namespace", endpoints.GetApplicationsInNamespace)
apis.GET("/config", endpoints.GetConfig)
apis.GET("/destinations", endpoints.GetDestinations)
apis.GET("/destinations/:type", endpoints.GetDestinationDetails)
}

return r, nil
Expand All @@ -107,8 +111,14 @@ func httpFileServerWith404(fs http.FileSystem) http.Handler {
func main() {
flags := parseFlags()

// Load destinations data
err := destinations.Load()
if err != nil {
log.Fatalf("Error loading destinations data: %s", err)
}

// Connect to Kubernetes
err := initKubernetesClient(&flags)
err = initKubernetesClient(&flags)
if err != nil {
log.Fatalf("Error creating Kubernetes client: %s", err)
}
Expand Down

0 comments on commit 301f43b

Please sign in to comment.