Skip to content
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
11 changes: 11 additions & 0 deletions controlplane/chunk/chunk.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import (
"github.com/spacechunks/explorer/controlplane/contextkey"
apierrs "github.com/spacechunks/explorer/controlplane/errors"
"github.com/spacechunks/explorer/internal/resource"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
)

func (s *svc) CreateChunk(ctx context.Context, chunk resource.Chunk) (resource.Chunk, error) {
Expand All @@ -52,6 +54,15 @@ func (s *svc) CreateChunk(ctx context.Context, chunk resource.Chunk) (resource.C
if err != nil {
return resource.Chunk{}, err
}

s.metrics.createdChunksCount.Add(
ctx,
1,
metric.WithAttributes(
attribute.String("name", chunk.Name),
attribute.String("owner_name", chunk.Owner.Nickname),
),
)
return ret, nil
}

Expand Down
36 changes: 20 additions & 16 deletions controlplane/chunk/flavor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,18 @@ func TestCreateFlavor(t *testing.T) {
ctx = context.Background()
mockRepo = mock.NewMockChunkRepository(t)
mockAccess = mock.NewMockAuthzAccessEvaluator(t)
svc = chunk.NewService(
slog.New(slog.NewTextHandler(os.Stdout, nil)),
mockRepo,
nil,
nil,
mockAccess,
chunk.Config{},
)
)

svc, err := chunk.NewService(
slog.New(slog.NewTextHandler(os.Stdout, nil)),
mockRepo,
nil,
nil,
mockAccess,
chunk.Config{},
)
require.NoError(t, err)

ctx = context.WithValue(ctx, contextkey.ActorID, "blabla")

tt.prep(mockRepo, mockAccess)
Expand Down Expand Up @@ -584,16 +586,18 @@ func TestCreateFlavorVersion(t *testing.T) {
ctx = context.Background()
mockAccess = mock.NewMockAuthzAccessEvaluator(t)
mockRepo = mock.NewMockChunkRepository(t)
svc = chunk.NewService(
slog.New(slog.NewTextHandler(os.Stdout, nil)),
mockRepo,
nil,
nil,
mockAccess,
chunk.Config{},
)
)

svc, err := chunk.NewService(
slog.New(slog.NewTextHandler(os.Stdout, nil)),
mockRepo,
nil,
nil,
mockAccess,
chunk.Config{},
)
require.NoError(t, err)

ctx = context.WithValue(ctx, contextkey.ActorID, "blabla")

tt.prep(mockRepo, tt.newVersion, tt.prevVersion, mockAccess)
Expand Down
45 changes: 45 additions & 0 deletions controlplane/chunk/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
A basic matchmaking service for the Chunk Explorer.
Copyright (C) 2026 Yannic Rieger <oss@76k.io>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package chunk

import (
"fmt"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric"
)

type metrics struct {
createdChunksCount metric.Int64Counter
}

func initMetrics() (metrics, error) {
meter := otel.Meter("github.com/spacechunks/explorer/controlplane/chunk")

createdCount, err := meter.Int64Counter(
"explorer.control_plane.chunk.created.count",
metric.WithDescription("Total number of chunks created"),
)
if err != nil {
return metrics{}, fmt.Errorf("chunk created counter: %w", err)
}

return metrics{
createdChunksCount: createdCount,
}, nil
}
11 changes: 9 additions & 2 deletions controlplane/chunk/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type svc struct {
s3Store blob.S3Store
cfg Config
access authz.AccessEvaluator
metrics metrics
}

func NewService(
Expand All @@ -78,13 +79,19 @@ func NewService(
s3Store blob.S3Store,
access authz.AccessEvaluator,
cfg Config,
) Service {
) (Service, error) {
m, err := initMetrics()
if err != nil {
return nil, err
}

return &svc{
logger: logger,
repo: repo,
jobClient: jobClient,
s3Store: s3Store,
access: access,
cfg: cfg,
}
metrics: m,
}, nil
}
45 changes: 45 additions & 0 deletions controlplane/instance/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
A basic matchmaking service for the Chunk Explorer.
Copyright (C) 2026 Yannic Rieger <oss@76k.io>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package instance

import (
"fmt"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric"
)

type metrics struct {
instanceCreatedCount metric.Int64Counter
}

func initMetrics() (metrics, error) {
meter := otel.Meter("github.com/spacechunks/explorer/controlplane/instance")

createdCount, err := meter.Int64Counter(
"explorer.control_plane.instance.created.count",
metric.WithDescription("Total number of instances started"),
)
if err != nil {
return metrics{}, fmt.Errorf("started counter: %w", err)
}

return metrics{
instanceCreatedCount: createdCount,
}, nil
}
30 changes: 28 additions & 2 deletions controlplane/instance/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import (
apierrs "github.com/spacechunks/explorer/controlplane/errors"
"github.com/spacechunks/explorer/controlplane/node"
"github.com/spacechunks/explorer/internal/resource"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
)

type Service interface {
Expand All @@ -52,15 +54,27 @@ type svc struct {
insRepo Repository
nodeRepo node.Repository
chunkRepo chunk.Repository
metrics metrics
}

func NewService(logger *slog.Logger, insRepo Repository, nodeRepo node.Repository, chunkRepo chunk.Repository) Service {
func NewService(
logger *slog.Logger,
insRepo Repository,
nodeRepo node.Repository,
chunkRepo chunk.Repository,
) (Service, error) {
m, err := initMetrics()
if err != nil {
return nil, err
}

return &svc{
logger: logger,
insRepo: insRepo,
nodeRepo: nodeRepo,
chunkRepo: chunkRepo,
}
metrics: m,
}, nil
}

func (s *svc) GetInstance(ctx context.Context, id string) (resource.Instance, error) {
Expand Down Expand Up @@ -116,6 +130,8 @@ func (s *svc) RunFlavorVersion(
return resource.Instance{}, fmt.Errorf("instance id: %w", err)
}

version := flavor.Versions[idx]

ins, err := s.insRepo.CreateInstance(ctx, resource.Instance{
ID: instanceID.String(),
FlavorVersion: flavor.Versions[idx],
Expand All @@ -131,6 +147,16 @@ func (s *svc) RunFlavorVersion(
return resource.Instance{}, fmt.Errorf("create instance: %w", err)
}

s.metrics.instanceCreatedCount.Add(
ctx,
1,
metric.WithAttributes(
attribute.String("flavor_name", flavor.Name),
attribute.String("flavor_version", version.Version),
attribute.String("chunk_name", ins.Chunk.Name),
),
)

return ins, nil
}

Expand Down
58 changes: 35 additions & 23 deletions controlplane/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,40 @@ func (s *Server) Run(ctx context.Context) error {
return fmt.Errorf("create validator: %w", err)
}

insService, err := instance.NewService(s.logger, db, db, db)
if err != nil {
return fmt.Errorf("instance service: %w", err)
}

userService, err := user.NewService(
db,
oidcProvider,
s.cfg.OAuthClientID,
s.cfg.APITokenIssuer,
s.cfg.APITokenExpiry,
key,
)
if err != nil {
return fmt.Errorf("user service: %w", err)
}

chunkService, err := chunk.NewService(
s.logger.With("component", "chunk-service"),
db,
db,
blobStore,
authz.NewRuleEvaluator(db),
chunk.Config{
Registry: s.cfg.OCIRegistry,
Bucket: s.cfg.Bucket,
PresignedURLExpiry: s.cfg.PresignedURLExpiry,
ThumbnailMaxSizeKB: s.cfg.ThumbnailMaxSizeKB,
ChangesetTarballMaxSizeBytes: s.cfg.ChangeSetTarballMaxSizeBytes,
})
if err != nil {
return fmt.Errorf("chunk service: %w", err)
}

var (
grpcServer = grpc.NewServer(
grpc.Creds(insecure.NewCredentials()),
Expand All @@ -196,30 +230,8 @@ func (s *Server) Run(ctx context.Context) error {
),
)

userService = user.NewService(
db,
oidcProvider,
s.cfg.OAuthClientID,
s.cfg.APITokenIssuer,
s.cfg.APITokenExpiry,
key,
)
userServer = user.NewServer(userService)
chunkService = chunk.NewService(
s.logger.With("component", "chunk-service"),
db,
db,
blobStore,
authz.NewRuleEvaluator(db),
chunk.Config{
Registry: s.cfg.OCIRegistry,
Bucket: s.cfg.Bucket,
PresignedURLExpiry: s.cfg.PresignedURLExpiry,
ThumbnailMaxSizeKB: s.cfg.ThumbnailMaxSizeKB,
ChangesetTarballMaxSizeBytes: s.cfg.ChangeSetTarballMaxSizeBytes,
})
userServer = user.NewServer(userService)
chunkServer = chunk.NewServer(chunkService)
insService = instance.NewService(s.logger, db, db, db)
insServer = instance.NewServer(insService)
)

Expand Down
45 changes: 45 additions & 0 deletions controlplane/user/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
A basic matchmaking service for the Chunk Explorer.
Copyright (C) 2026 Yannic Rieger <oss@76k.io>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package user

import (
"fmt"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric"
)

type metrics struct {
registeredCount metric.Int64Counter
}

func initMetrics() (metrics, error) {
meter := otel.Meter("github.com/spacechunks/explorer/controlplane/user")

registeredCount, err := meter.Int64Counter(
"explorer.control_plane.user.registered.count",
metric.WithDescription("Total number of users registered"),
)
if err != nil {
return metrics{}, fmt.Errorf("user registered counter: %w", err)
}

return metrics{
registeredCount: registeredCount,
}, nil
}
Loading