Skip to content

Commit 2542d62

Browse files
committed
api wip
1 parent 6de9f4f commit 2542d62

File tree

6 files changed

+196
-11
lines changed

6 files changed

+196
-11
lines changed

.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ SHELLHUB_VERSION=v0.14.2
77
# This specification details the network interface to which the gateway container will be bound.
88
SHELLHUB_BIND_ADDRESS=0.0.0.0
99

10+
SHELLHUB_API_PORT=8080
11+
1012
# The HTTP listen port for the ShellHub web-based GUI, API and Reverse SSH tunnel.
1113
# Values: any free port on host
1214
SHELLHUB_HTTP_PORT=80

api/server.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ var serverCmd = &cobra.Command{
6363
// Provides the configuration for the API service.
6464
// The values are load from the system environment variables.
6565
type config struct {
66+
HttpPort string `env:"HTTP_PORT"`
6667
// MongoDB connection string (URI format)
6768
MongoURI string `env:"MONGO_URI,default=mongodb://mongo:27017/main"`
6869
// Redis connection string (URI format)
@@ -155,7 +156,7 @@ func startServer(cfg *config, store store.Store, cache storecache.Cache) error {
155156
}
156157
})
157158

158-
e.Logger.Fatal(e.Start(":8080"))
159+
e.Logger.Fatal(e.Start(":" + cfg.HttpPort))
159160

160161
return nil
161162
}

docker-compose.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ services:
2727
image: shellhubio/api:${SHELLHUB_VERSION}
2828
restart: unless-stopped
2929
environment:
30+
- HTTP_PORT=${SHELLHUB_API_PORT}
3031
- SHELLHUB_VERSION=${SHELLHUB_VERSION}
3132
- PRIVATE_KEY=/run/secrets/api_private_key
3233
- PUBLIC_KEY=/run/secrets/api_public_key
@@ -57,7 +58,7 @@ services:
5758
networks:
5859
- shellhub
5960
healthcheck:
60-
test: "curl -f http://api:8080/api/healthcheck || exit 1"
61+
test: "curl -f http://api:${SHELLHUB_API_PORT}/api/healthcheck || exit 1"
6162
interval: 30s
6263
start_period: 10s
6364
ui:
@@ -81,6 +82,7 @@ services:
8182
image: shellhubio/gateway:${SHELLHUB_VERSION}
8283
restart: unless-stopped
8384
environment:
85+
- SHELLHUB_API_PORT=${SHELLHUB_API_PORT}
8486
- SHELLHUB_DOMAIN=${SHELLHUB_DOMAIN}
8587
- SHELLHUB_PUBLIC_URL_DOMAIN=${SHELLHUB_PUBLIC_URL_DOMAIN}
8688
- SHELLHUB_VERSION=${SHELLHUB_VERSION}

gateway/conf.d/shellhub.conf

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ server {
2424
server_name {{ (env.Getenv "SHELLHUB_DOMAIN") }};
2525
resolver 127.0.0.11 ipv6=off;
2626

27+
set $api_port {{ env.Getenv "SHELLHUB_API_PORT" }};
28+
set $api_endpoint "api:${api_port}";
29+
30+
# set $api_endpoint "api:${(env.Getenv 'SHELLHUB_API_PORT')}";
31+
2732
# Load configuration files for the default server block
2833
include /etc/nginx/default.d/*.conf;
2934

@@ -53,7 +58,7 @@ server {
5358
}
5459

5560
location /api {
56-
set $upstream api:8080;
61+
set $upstream $api_endpoint;
5762

5863
auth_request /auth;
5964
auth_request_set $tenant_id $upstream_http_x_tenant_id;
@@ -75,7 +80,7 @@ server {
7580
}
7681

7782
location ~ ^/(install.sh|kickstart.sh)$ {
78-
set $upstream api:8080;
83+
set $upstream $api_endpoint;
7984
rewrite ^/(.*)$ /api/install break;
8085
proxy_set_header X-Forwarded-Host $host;
8186
proxy_set_header X-Forwarded-Proto $x_forwarded_proto;
@@ -84,7 +89,7 @@ server {
8489
}
8590

8691
location /api/auth/user {
87-
set $upstream api:8080;
92+
set $upstream $api_endpoint;
8893

8994
auth_request /auth/skip;
9095
auth_request_set $tenant_id $upstream_http_x_tenant_id;
@@ -188,7 +193,7 @@ server {
188193
}
189194

190195
location /ssh/auth {
191-
set $upstream api:8080;
196+
set $upstream $api_endpoint;
192197
auth_request /auth;
193198
auth_request_set $device_uid $upstream_http_x_device_uid;
194199
error_page 500 =401 /auth;
@@ -357,7 +362,7 @@ server {
357362
}
358363

359364
location /api/devices/auth {
360-
set $upstream api:8080;
365+
set $upstream $api_endpoint;
361366
auth_request off;
362367
rewrite ^/api/(.*)$ /api/$1 break;
363368
{{ if bool (env.Getenv "SHELLHUB_PROXY") -}}
@@ -369,7 +374,7 @@ server {
369374
}
370375

371376
location /api/login {
372-
set $upstream api:8080;
377+
set $upstream $api_endpoint;
373378
auth_request off;
374379
rewrite ^/api/(.*)$ /api/$1 break;
375380
proxy_pass http://$upstream;
@@ -383,14 +388,14 @@ server {
383388
}
384389

385390
location /auth {
386-
set $upstream_auth api:8080;
391+
set $upstream_auth $api_endpoint;
387392
internal;
388393
rewrite ^/(.*)$ /internal/$1 break;
389394
proxy_pass http://$upstream_auth;
390395
}
391396

392397
location /auth/skip {
393-
set $upstream_auth api:8080;
398+
set $upstream_auth $api_endpoint;
394399
internal;
395400
rewrite ^/auth/(.*)$ /internal/auth?args=$1 break;
396401
proxy_pass http://$upstream_auth;
@@ -414,7 +419,7 @@ server {
414419
}
415420

416421
location /info {
417-
set $upstream api:8080;
422+
set $upstream $api_endpoint;
418423

419424
proxy_set_header X-Forwarded-Host $host;
420425
proxy_set_header X-Forwarded-Port $x_forwarded_port;

tests/api_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"strconv"
7+
"testing"
8+
9+
"github.com/go-resty/resty/v2"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestInfo(t *testing.T) {
14+
t.Parallel()
15+
16+
type Expected struct {
17+
body map[string]interface{}
18+
status int
19+
err error
20+
}
21+
22+
cases := []struct {
23+
description string
24+
compose *Enviroment
25+
// Unlike other expected values, this is a function because the body can change based on
26+
// the variables. For that reason, 'expected' retrieves 'httpPort' and 'sshPort', which
27+
// will be used to generate the correct body.
28+
expected func(httpPort, sshPort string) Expected
29+
}{
30+
{
31+
description: "case 1",
32+
compose: New().
33+
WithEnv("SHELLHUB_API_PORT", strconv.Itoa(getFreePort(t))).
34+
WithEnv("SHELLHUB_HTTP_PORT", strconv.Itoa(getFreePort(t))).
35+
WithEnv("SHELLHUB_SSH_PORT", strconv.Itoa(getFreePort(t))).
36+
WithEnv("SHELLHUB_VERSION", "v0.25.2").
37+
Build(),
38+
expected: func(httpPort, sshPort string) Expected {
39+
return Expected{
40+
body: map[string]interface{}{
41+
"endpoints": map[string]interface{}{
42+
"api": fmt.Sprintf("localhost:%s", httpPort),
43+
"ssh": fmt.Sprintf("localhost:%s", sshPort),
44+
},
45+
"version": "v0.25.2",
46+
},
47+
status: 200,
48+
err: nil,
49+
}
50+
},
51+
},
52+
{
53+
description: "case 2",
54+
compose: New().
55+
WithEnv("SHELLHUB_API_PORT", strconv.Itoa(getFreePort(t))).
56+
WithEnv("SHELLHUB_HTTP_PORT", strconv.Itoa(getFreePort(t))).
57+
WithEnv("SHELLHUB_SSH_PORT", strconv.Itoa(getFreePort(t))).
58+
WithEnv("SHELLHUB_VERSION", "v0.10.2").
59+
Build(),
60+
expected: func(httpPort, sshPort string) Expected {
61+
return Expected{
62+
body: map[string]interface{}{
63+
"endpoints": map[string]interface{}{
64+
"api": fmt.Sprintf("localhost:%s", httpPort),
65+
"ssh": fmt.Sprintf("localhost:%s", sshPort),
66+
},
67+
"version": "v0.10.2",
68+
},
69+
status: 200,
70+
err: nil,
71+
}
72+
},
73+
},
74+
}
75+
76+
for _, tt := range cases {
77+
// Avoid "loop variable <var> captured by func literal"
78+
tc := tt
79+
80+
tc.compose.Run(t, tc.description, func(t *testing.T) {
81+
t.Parallel()
82+
83+
res, err := resty.
84+
New().
85+
R().
86+
Get(fmt.Sprintf("http://localhost:%s/info", tc.compose.GetEnv("SHELLHUB_HTTP_PORT")))
87+
88+
body := map[string]interface{}{}
89+
assert.NoError(t, json.Unmarshal(res.Body(), &body))
90+
91+
assert.Equal(
92+
t,
93+
tc.expected(tc.compose.GetEnv("SHELLHUB_HTTP_PORT"), tc.compose.GetEnv("SHELLHUB_SSH_PORT")),
94+
Expected{body, res.StatusCode(), err},
95+
)
96+
})
97+
}
98+
}

tests/enviroment.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/joho/godotenv"
9+
"github.com/stretchr/testify/assert"
10+
testcontainers "github.com/testcontainers/testcontainers-go/modules/compose"
11+
"github.com/testcontainers/testcontainers-go/wait"
12+
)
13+
14+
type Enviroment struct {
15+
env map[string]string
16+
}
17+
18+
type EnviromentBuilder struct {
19+
env map[string]string
20+
}
21+
22+
func New() *EnviromentBuilder {
23+
env, _ := godotenv.Read("../.env")
24+
return &EnviromentBuilder{
25+
env: env,
26+
}
27+
}
28+
29+
func (e *EnviromentBuilder) WithEnv(key, val string) *EnviromentBuilder {
30+
e.env[key] = val
31+
32+
return e
33+
}
34+
35+
func (e *EnviromentBuilder) Build() *Enviroment {
36+
return &Enviroment{
37+
env: e.env,
38+
}
39+
}
40+
41+
///////////////////////
42+
///////////////////////
43+
///////////////////////
44+
///////////////////////
45+
///////////////////////
46+
47+
func (e *Enviroment) GetEnv(key string) string {
48+
return e.env[key]
49+
}
50+
51+
// Run starts and executes a callback cb within the specified compose environment.
52+
// It's a wrapper around t.Run().
53+
func (e *Enviroment) Run(t *testing.T, description string, cb func(t *testing.T)) {
54+
t.Run(description, func(t *testing.T) {
55+
compose, err := testcontainers.NewDockerCompose("../docker-compose.yml", "../docker-compose.dev.yml")
56+
assert.NoError(t, err)
57+
58+
t.Cleanup(func() {
59+
err := compose.Down(context.Background(), testcontainers.RemoveOrphans(true), testcontainers.RemoveImagesLocal)
60+
assert.NoError(t, err)
61+
})
62+
63+
ctx, cancel := context.WithCancel(context.Background())
64+
t.Cleanup(cancel)
65+
66+
assert.NoError(
67+
t,
68+
compose.
69+
WithEnv(e.env).
70+
// TODO: how can we wait for "api"?
71+
WaitForService("gateway", wait.ForHTTP("/healthcheck").WithStartupTimeout(10*time.Second)).
72+
Up(ctx, testcontainers.Wait(true)),
73+
)
74+
75+
cb(t)
76+
})
77+
}

0 commit comments

Comments
 (0)