summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.circleci/config.yml18
-rw-r--r--.gitignore4
-rw-r--r--Makefile2
-rw-r--r--bazel/lib/BUILD0
-rw-r--r--bazel/lib/expand_template.bzl45
-rw-r--r--common/management/BUILD.bazel24
-rw-r--r--common/management/nova.management.v1alpha.proto58
-rw-r--r--common/version.go.in5
-rw-r--r--novactl/cmd/cluster.go96
-rw-r--r--novactl/lib/conn.go19
10 files changed, 267 insertions, 4 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index df9ffe0..5539cce 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -18,16 +18,28 @@ commands:
jobs:
build:
- docker:
- - image: cimg/base:stable
+ machine:
+ image: 'ubuntu-2004:202010-01'
steps:
- checkout
- setup-bazel
+ - restore_cache:
+ keys:
+ - bazel-cache-{{ .Branch }}
- run:
name: "Build"
command: "bazel build //:packages"
+ - save_cache:
+ paths:
+ - ~/.cache/bazel
+ key: bazel-cache-{{ .Branch }}
+ - run:
+ name: "Move artifacts"
+ command: |
+ mkdir ~/project/artifacts
+ mv ~/project/bazel-bin/packages* ~/project/artifacts
- store_artifacts:
- path: ~/project/bazel-bin/packages*
+ path: ~/project/artifacts
workflows:
build-workflow:
diff --git a/.gitignore b/.gitignore
index 2cd395b..161aa0d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,6 @@ bazel-*
.vscode
ratelimiter/target
target/
-**/local* \ No newline at end of file
+**/local*
+.ijwb
+.idea \ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ee23d38
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+gazelle:
+ bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=//bazel:deps.bzl%go_dependencies \ No newline at end of file
diff --git a/bazel/lib/BUILD b/bazel/lib/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bazel/lib/BUILD
diff --git a/bazel/lib/expand_template.bzl b/bazel/lib/expand_template.bzl
new file mode 100644
index 0000000..742565a
--- /dev/null
+++ b/bazel/lib/expand_template.bzl
@@ -0,0 +1,45 @@
+"""Rule for simple expansion of template files. This performs a simple
+search over the template file for the keys in substitutions,
+and replaces them with the corresponding values.
+
+Typical usage:
+ load("//tools/build_rules:expand_template.bzl", "expand_template")
+ expand_template(
+ name = "ExpandMyTemplate",
+ template = "my.template",
+ out = "my.txt",
+ substitutions = {
+ "$VAR1": "foo",
+ "$VAR2": "bar",
+ }
+ )
+
+Args:
+ name: The name of the rule.
+ template: The template file to expand
+ out: The destination of the expanded file
+ substitutions: A dictionary mapping strings to their substitutions
+ is_executable: A boolean indicating whether the output file should be executable
+"""
+
+def expand_template_impl(ctx):
+ ctx.actions.expand_template(
+ template = ctx.file.template,
+ output = ctx.outputs.out,
+ substitutions = {
+ k: ctx.expand_location(v, ctx.attr.data)
+ for k, v in ctx.attr.substitutions.items()
+ },
+ is_executable = ctx.attr.is_executable,
+ )
+
+expand_template = rule(
+ implementation = expand_template_impl,
+ attrs = {
+ "template": attr.label(mandatory = True, allow_single_file = True),
+ "substitutions": attr.string_dict(mandatory = True),
+ "out": attr.output(mandatory = True),
+ "is_executable": attr.bool(default = False, mandatory = False),
+ "data": attr.label_list(allow_files = True),
+ },
+) \ No newline at end of file
diff --git a/common/management/BUILD.bazel b/common/management/BUILD.bazel
new file mode 100644
index 0000000..2e8f53f
--- /dev/null
+++ b/common/management/BUILD.bazel
@@ -0,0 +1,24 @@
+load("@rules_proto//proto:defs.bzl", "proto_library")
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
+
+proto_library(
+ name = "nova_ratelimit_v1_proto",
+ srcs = ["nova.management.v1alpha.proto"],
+ visibility = ["//visibility:public"],
+)
+
+go_proto_library(
+ name = "nova_ratelimit_v1_go_proto",
+ compilers = ["@io_bazel_rules_go//proto:go_grpc"],
+ importpath = "github.com/discordnova/nova/common/management",
+ proto = ":nova_ratelimit_v1_proto",
+ visibility = ["//visibility:public"],
+)
+
+go_library(
+ name = "management",
+ embed = [":nova_ratelimit_v1_go_proto"],
+ importpath = "github.com/discordnova/nova/common/management",
+ visibility = ["//visibility:public"],
+)
diff --git a/common/management/nova.management.v1alpha.proto b/common/management/nova.management.v1alpha.proto
new file mode 100644
index 0000000..f07aa9b
--- /dev/null
+++ b/common/management/nova.management.v1alpha.proto
@@ -0,0 +1,58 @@
+syntax = "proto3";
+package nova.ratelimit.v1;
+
+message Empty {}
+
+// Represents the status of a shard
+enum ShardStatus {
+ DISCONNECTED = 0;
+ RUNNING = 1;
+ RECONNECTING = 2;
+}
+
+// represents the state of a nova shard
+message ShardStatusResponse {
+ // Status of the shard in the cluster
+ ShardStatus status = 1;
+ // Index of the discord shard
+ int64 identifier = 2;
+ // If the cluster have a node assigned
+ string cluster = 3;
+ // the websocket latency of the shard
+ int64 latency = 4;
+}
+
+message ShardStatusRequest {
+ // the id of the shard
+ int64 identifier = 1;
+}
+
+// represents the status of a cluster
+// (an instance of the gateway which holds multiple shards)
+message ClusterStatusResponse {
+ // the unique id of the cluster
+ string id = 1;
+ // the node the cluster is running on
+ string node = 2;
+ // the average latency of the cluster
+ int64 average_latency = 3;
+ // list of all the shards on the cluster
+ repeated ShardStatusResponse shards = 4;
+}
+
+message ClusterStatusRequest {
+ string id = 1;
+}
+
+// Represents the status of all the nova clusters & shards
+message GlobalClusterStatusResponse {
+ int64 size = 1;
+ repeated ClusterStatusResponse shards = 2;
+}
+
+// used by the cli to interact with the nova manager
+service ManagementService {
+ rpc GetGlobalClusterStatus (Empty) returns (GlobalClusterStatusResponse);
+ rpc GetClusterStatus (ClusterStatusRequest) returns (ClusterStatusResponse);
+ rpc GetShardStatus (ShardStatusRequest) returns (ShardStatusResponse);
+} \ No newline at end of file
diff --git a/common/version.go.in b/common/version.go.in
new file mode 100644
index 0000000..aacaad5
--- /dev/null
+++ b/common/version.go.in
@@ -0,0 +1,5 @@
+package common
+
+const (
+ VERSION = "$VERSION"
+)
diff --git a/novactl/cmd/cluster.go b/novactl/cmd/cluster.go
new file mode 100644
index 0000000..3d56c21
--- /dev/null
+++ b/novactl/cmd/cluster.go
@@ -0,0 +1,96 @@
+package cmd
+
+import (
+ "context"
+ "errors"
+ "github.com/discordnova/nova/common/management"
+ "github.com/discordnova/nova/novactl/lib"
+ "github.com/olekukonko/tablewriter"
+ "github.com/rs/zerolog/log"
+ "github.com/spf13/cobra"
+ "os"
+ "strconv"
+ "time"
+)
+
+var ClusterCommand = createClusterCommand()
+
+var (
+ ServerUrl *string = nil
+ Ctx = context.Background()
+)
+
+func createClusterCommand() *cobra.Command {
+ command := &cobra.Command{
+ Use: "cluster",
+ Short: "Commands to interact with the nova cluster",
+ Aliases: []string{"c"},
+ TraverseChildren: true,
+ }
+ // shard sub command
+ shard := cobra.Command{
+ Use: "shard [shard]",
+ Short: "Returns information about a specific shard",
+ Run: shardCommand,
+ Args: func(cmd *cobra.Command, args []string) error {
+ if len(args) != 1 {
+ return errors.New("one shard id must be specified")
+ } else {
+ if _, err := strconv.Atoi(args[0]); err != nil {
+ return errors.New("the shard id must be a string")
+ }
+ }
+ return nil
+ },
+ TraverseChildren: true,
+ }
+ // info sub command
+ info := cobra.Command{
+ Use: "info",
+ Short: "Gets the status of the cluster",
+ Aliases: []string{"i"},
+ Run: infoCommand,
+ TraverseChildren: true,
+ }
+
+ ServerUrl = command.Flags().StringP("server", "s", "localhost:8053", "")
+
+ command.AddCommand(&shard)
+ command.AddCommand(&info)
+
+ return command
+}
+
+func shardCommand(command *cobra.Command, args []string) {
+ id, err := strconv.ParseInt(args[0], 10, 64)
+ if err != nil {
+ log.Err(err).Msg("Failed to parse the shard id")
+ os.Exit(1)
+ }
+
+ log.Info().Msgf("Starting connection with server %s", *ServerUrl)
+ conn, err := lib.NewConn(*ServerUrl)
+
+ if err != nil {
+ log.Err(err).Msg("Failed to connect to the manager")
+ return
+ }
+ ctx, _ := context.WithTimeout(Ctx, time.Second*10)
+
+ manager := *conn
+ data, err := manager.GetShardStatus(ctx, &management.ShardStatusRequest{
+ Identifier: id,
+ })
+
+ if err != nil {
+ log.Err(err).Msg("Failed to get the status of the shard")
+ return
+ }
+
+ table := tablewriter.NewWriter(os.Stdout)
+ table.SetHeader([]string{"Status", "Id", "Cluster", "Latency"})
+ table.Append([]string{string(data.Status), string(data.Identifier), data.Cluster, string(data.Latency)})
+ table.Render()
+}
+
+func infoCommand(command *cobra.Command, args []string) {}
diff --git a/novactl/lib/conn.go b/novactl/lib/conn.go
new file mode 100644
index 0000000..362c9ed
--- /dev/null
+++ b/novactl/lib/conn.go
@@ -0,0 +1,19 @@
+package lib
+
+import (
+ "google.golang.org/grpc"
+
+ "github.com/discordnova/nova/common/management"
+)
+
+// NewConn creates a connection to the manager
+func NewConn(host string) (*management.ManagementServiceClient, error) {
+ lis, err := grpc.Dial(host, grpc.WithInsecure())
+ if err != nil {
+ return nil, err
+ }
+
+ conn := management.NewManagementServiceClient(lis)
+
+ return &conn, nil
+}