Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added REST proxy over existing RPCs #200

Merged
merged 1 commit into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
results.db
.vscode
# IDE
.vscode
.idea

# Testing
temp
bin
results.db
25 changes: 14 additions & 11 deletions cmd/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@

## Variables

| Environment Variable | Description | Example |
|----------------------|---------------------------------|----------------------------------------------|
| DB_USER | MySQL Database user | user |
| DB_PASSWORD | MySQL Database Password | hunter2 |
| DB_PROTOCOL | MySQL Database Network Protocol | unix |
| DB_ADDR | MySQL Database address | /cloudsql/my-project:us-east1:tekton-results |
| DB_NAME | MySQL Database name | tekton_results |
| DB_SSLMODE | Database SSL mode | verify-full |
| Environment Variable | Description | Example |
|----------------------|----------------------------|----------------------------------------------|
| DB_USER | Postgres Database user | user |
| DB_PASSWORD | Postgres Database Password | hunter2 |
| DB_HOST | Postgres Database host | /cloudsql/my-project:us-east1:tekton-results |
| DB_NAME | Postgres Database name | tekton_results |
| DB_SSLMODE | Database SSL mode | verify-full |
| GRPC_PORT | gRPC Server Port | 50051 (default) |
| REST_PORT | REST proxy Port | 8080 (default) |
| PROMETHEUS_PORT | Prometheus Port | 9090 (default) |
| TLS_HOSTNAME_OVERRIDE| Override the hostname used to serve TLS. This should not be set (or set to the empty string) in production environments. | results.tekton.dev |
| TLS_PATH | Path to TLS files | /etc/tls |

These values can also be set in the config file located in the `env/config` directory.

Values derived from MySQL DSN (see
https://github.com/go-sql-driver/mysql#dsn-data-source-name)
Values derived from Postgres DSN

If you use the default MySQL server we provide, the `DB_ADDR` can be set as `tekton-results-mysql.tekton-pipelines`.
If you use the default postgres database we provide, the `DB_HOST` can be set as `tekton-results-postgres-service.tekton-pipelines`.
128 changes: 75 additions & 53 deletions cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,23 @@ limitations under the License.
package main

import (
"flag"
"context"
"fmt"
"github.com/tektoncd/results/pkg/api/server/v1alpha2/auth"
_ "go.uber.org/automaxprocs"
"google.golang.org/grpc/credentials/insecure"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"log"
"net"
"net/http"
"os"

// Go runtime is unaware of CPU quota which means it will set GOMAXPROCS
// to underlying host vm node. This high value means that GO runtime
// scheduler assumes that it has more threads and does context switching
// when it might work with fewer threads.
// This doesn't happen# with our other controllers and services because
// sharedmain already import this package for them.
_ "go.uber.org/automaxprocs"
"path"

grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/spf13/viper"
v1alpha2 "github.com/tektoncd/results/pkg/api/server/v1alpha2"
"github.com/tektoncd/results/pkg/api/server/v1alpha2/auth"
v1alpha2pb "github.com/tektoncd/results/proto/v1alpha2/results_go_proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
Expand All @@ -45,17 +42,20 @@ import (
"google.golang.org/grpc/reflection"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)

type ConfigFile struct {
DB_USER string `mapstructure:"DB_USER"`
DB_PASSWORD string `mapstructure:"DB_PASSWORD"`
DB_PROTOCOL string `mapstructure:"DB_PROTOCOL"`
DB_ADDR string `mapstructure:"DB_ADDR"`
DB_NAME string `mapstructure:"DB_NAME"`
DB_SSLMODE string `mapstructure:"DB_SSLMODE"`
DB_USER string `mapstructure:"DB_USER"`
DB_PASSWORD string `mapstructure:"DB_PASSWORD"`
DB_HOST string `mapstructure:"DB_HOST"`
DB_PORT string `mapstructure:"DB_PORT"`
DB_NAME string `mapstructure:"DB_NAME"`
DB_SSLMODE string `mapstructure:"DB_SSLMODE"`
GRPC_PORT string `mapstructure:"GRPC_PORT"`
REST_PORT string `mapstructure:"REST_PORT"`
PROMETHEUS_PORT string `mapstructure:"PROMETHEUS_PORT"`
TLS_HOSTNAME_OVERRIDE string `mapstructure:"TLS_HOSTNAME_OVERRIDE"`
TLS_PATH string `mapstructure:"TLS_PATH"`
}

func main() {
Expand All @@ -75,54 +75,49 @@ func main() {
err = viper.Unmarshal(&configFile)

if err != nil {
log.Fatal("cannot load config:", err)
log.Fatal("Cannot load config:", err)
}

flag.Parse()

user, pass := configFile.DB_USER, configFile.DB_PASSWORD
if user == "" || pass == "" {
if configFile.DB_USER == "" || configFile.DB_PASSWORD == "" {
log.Fatal("Must provide both DB_USER and DB_PASSWORD")
}
// Connect to the database.
// DSN derived from https://pkg.go.dev/gorm.io/driver/postgres

sslmode := configFile.DB_SSLMODE
if sslmode == "" {
sslmode = "disable"
}
dbURI := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=%s", configFile.DB_HOST, configFile.DB_USER, configFile.DB_PASSWORD, configFile.DB_NAME, configFile.DB_PORT, configFile.DB_SSLMODE)

dbURI := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=5432 sslmode=%s", configFile.DB_ADDR, user, pass, configFile.DB_NAME, configFile.DB_SSLMODE)
db, err := gorm.Open(postgres.Open(dbURI), &gorm.Config{})
if err != nil {
log.Fatalf("failed to open the results.db: %v", err)
log.Fatalf("Failed to open the results.db: %v", err)
}

// Create k8s client
config, err := rest.InClusterConfig()
if err != nil {
log.Fatal("error getting kubernetes client config:", err)
log.Fatal("Error getting kubernetes client config:", err)
}
k8s, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatal("error creating kubernetes clientset:", err)
log.Fatal("Error creating kubernetes clientset:", err)
}

// Load TLS cert
creds, err := credentials.NewServerTLSFromFile("/etc/tls/tls.crt", "/etc/tls/tls.key")
if err != nil {
log.Fatalf("error loading TLS key pair: %v", err)
// Load TLS cert for server
creds, tlsError := credentials.NewServerTLSFromFile(path.Join(configFile.TLS_PATH, "tls.crt"), path.Join(configFile.TLS_PATH, "tls.key"))
if tlsError != nil {
log.Printf("Error loading TLS key pair for server: %v", tlsError)
log.Println("Creating server without TLS")
creds = insecure.NewCredentials()
}

// Register API server(s)
v1a2, err := v1alpha2.New(db, v1alpha2.WithAuth(auth.NewRBAC(k8s)))
if err != nil {
log.Fatalf("failed to create server: %v", err)
log.Fatalf("Failed to create server: %v", err)
}
s := grpc.NewServer(
grpc.Creds(creds),
grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor),
grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor),
grpc.StreamInterceptor(prometheus.StreamServerInterceptor),
grpc.UnaryInterceptor(prometheus.UnaryServerInterceptor),
)
v1alpha2pb.RegisterResultsServer(s, v1a2)

Expand All @@ -134,25 +129,52 @@ func main() {
hs.SetServingStatus("tekton.results.v1alpha2.Results", healthpb.HealthCheckResponse_SERVING)
healthpb.RegisterHealthServer(s, hs)

// Prometheus metrics
grpc_prometheus.Register(s)
// Start prometheus metrics server
prometheus.Register(s)
http.Handle("/metrics", promhttp.Handler())
go func() {
if err := http.ListenAndServe(":8080", promhttp.Handler()); err != nil {
log.Fatalf("error running Prometheus HTTP handler: %v", err)
log.Printf("Prometheus server listening on: %s", configFile.PROMETHEUS_PORT)
if err := http.ListenAndServe(":"+configFile.PROMETHEUS_PORT, promhttp.Handler()); err != nil {
log.Fatalf("Error running Prometheus HTTP handler: %v", err)
}
}()

// Listen on port and serve.
port := os.Getenv("PORT")
if port == "" {
// Default gRPC server port to this value from tutorials (e.g., https://grpc.io/docs/guides/auth/#go)
port = "50051"
// Start gRPC server
lis, err := net.Listen("tcp", ":"+configFile.GRPC_PORT)
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
go func() {
log.Printf("gRPC server listening on: %s", configFile.GRPC_PORT)
log.Fatal(s.Serve(lis))
}()

// Load REST client TLS cert to connect to the gRPC server
if tlsError == nil {
creds, err = credentials.NewClientTLSFromFile(path.Join(configFile.TLS_PATH, "tls.crt"), configFile.TLS_HOSTNAME_OVERRIDE)
if err != nil {
log.Fatalf("Error loading TLS certificate for REST: %v", err)
}
}
lis, err := net.Listen("tcp", ":"+port)

opts := []grpc.DialOption{grpc.WithTransportCredentials(creds)}

// Register gRPC server endpoint for gRPC gateway
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
err = v1alpha2pb.RegisterResultsHandlerFromEndpoint(ctx, mux, ":"+configFile.GRPC_PORT, opts)
sayan-biswas marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
log.Fatalf("failed to listen: %v", err)
log.Fatal("Error registering gRPC server endpoint: ", err)
}
log.Printf("Listening on :%s...", port)
log.Fatal(s.Serve(lis))

// Start REST proxy server
log.Printf("REST server Listening on: %s", configFile.REST_PORT)
if tlsError != nil {
log.Fatal(http.ListenAndServe(":"+configFile.REST_PORT, mux))
} else {
log.Fatal(http.ListenAndServeTLS(":"+configFile.REST_PORT, path.Join(configFile.TLS_PATH, "tls.crt"), path.Join(configFile.TLS_PATH, "tls.key"), mux))
}

}
10 changes: 6 additions & 4 deletions config/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ spec:
secretKeyRef:
name: tekton-results-postgres
key: POSTGRES_PASSWORD
- name: DB_PROTOCOL
value: tcp
- name: DB_ADDR
- name: DB_HOST
value: tekton-results-postgres-service.tekton-pipelines.svc.cluster.local
- name: DB_NAME
value: tekton-results
Expand All @@ -75,7 +73,11 @@ spec:
protocol: TCP
port: 50051
targetPort: 50051
- name: prometheus
- name: rest
protocol: TCP
port: 8080
targetPort: 8080
- name: prometheus
protocol: TCP
port: 9090
targetPort: 9090
11 changes: 8 additions & 3 deletions env/config
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
DB_USER=
DB_PASSWORD=
DB_PROTOCOL=
DB_ADDR=
DB_HOST=
DB_PORT=5432
DB_NAME=
DB_SSLMODE=
DB_SSLMODE=disable
GRPC_PORT=50051
REST_PORT=8080
PROMETHEUS_PORT=9090
TLS_HOSTNAME_OVERRIDE=
TLS_PATH=/etc/tls
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.3
github.com/jackc/pgconn v1.11.0
github.com/jonboulle/clockwork v0.2.3
github.com/mattn/go-sqlite3 v2.0.3+incompatible
Expand Down Expand Up @@ -76,7 +77,7 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/docker/cli v20.10.12+incompatible // indirect
github.com/docker/distribution v2.8.0+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v20.10.12+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4 // indirect
github.com/emicklei/go-restful v2.15.0+incompatible // indirect
Expand Down
7 changes: 5 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/docker/cli v20.10.12+incompatible h1:lZlz0uzG+GH+c0plStMUdF/qk3ppmgnswpR5EbqzVGA=
github.com/docker/cli v20.10.12+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY=
github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.12+incompatible h1:CEeNmFM0QZIsJCZKMkZx0ZcahTiewkrgiwfYD+dfl1U=
github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
Expand Down Expand Up @@ -401,6 +401,7 @@ github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw
github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog=
github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -560,6 +561,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqC
github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.3 h1:BGNSrTRW4rwfhJiFwvwF4XQ0Y72Jj9YEgxVrtovbD5o=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.3/go.mod h1:VHn7KgNsRriXa4mcgtkpR00OXyQY6g67JWMvn+R27A4=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
Expand Down
2 changes: 1 addition & 1 deletion proto/v1alpha2/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@

//go:generate rm -rf results_go_proto
//go:generate mkdir results_go_proto
//go:generate protoc --go_out=results_go_proto --go-grpc_out=results_go_proto --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative -I$GOPATH/src/github.com/googleapis/googleapis -I../pipeline/v1beta1 -I. api.proto resources.proto
//go:generate protoc --go_out=results_go_proto --go-grpc_out=results_go_proto --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --grpc-gateway_out=results_go_proto --grpc-gateway_opt paths=source_relative -I$GOPATH/src/github.com/googleapis/googleapis -I../pipeline/v1beta1 -I. api.proto resources.proto

package v1alpha2
Loading