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

refactoring harbor satellite and fixing test cases. #44

Merged
merged 10 commits into from
Sep 20, 2024
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ dist/
.DS_Store
zot/cache.db
secrets.txt
__debug_bin1949266242
13 changes: 11 additions & 2 deletions ci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,23 @@ To run a particular function, run:
To build the satellite binaries, use the following command:
- ```sh
dagger call build --source=. --name=satellite export --path=./bin
This would spin up a container and install required dependencies and build various architecture binaries and export them to the host on path ./bin for testing on the host.
This would spin up a container and install required dependencies and build various architecture binaries and export them to the host on path ./bin for testing on the host.
- #### Example: Releasing to GitHub
To release the project on GitHub, use the following command
- ```sh
dagger call release --directory=. --token=<your_github_token> --name=satellite
The above function would then proceed to release the project on github for the name provided. The above function also takes argument `--release-type` which would tell the release what kind of release it is i.e major, minor or path, The default value is set to be path release
The above function would then proceed to release the project on github for the name provided. The above function also takes argument `--release-type` which would tell the release what kind of release it is i.e major, minor or path, The default value is set to be path release
- #### Example: Releasing to GitHub with type of release
To release the project on GitHub, use the following command
- ```sh
dagger call release --directory=. --token=<your_github_token> --name=satellite --release-type=minor
The above function would release the minor version for the project mentioned
- #### Example: Running test cases using dagger
To run the test cases using dagger use the following command
- ```sh
dagger run go test ./... -v -count=1
This would run the test cases present in the entire project.
To run the test cases without dagger use the following command
- ```sh
go test ./... -v -count=1 -args -abs=false
This would set the config file to use the relative path.
34 changes: 34 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package config

import (
"fmt"

"github.com/spf13/viper"
)

type Config struct {
LogLevel string
OwnRegistry bool
OwnRegistryAdr string
OwnRegistryPort string
ZotConfigPath string
Input string
}

func LoadConfig() (*Config, error) {
viper.SetConfigName("config")
viper.SetConfigType("toml")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
return nil, fmt.Errorf("error reading config file: %w", err)
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved
}

return &Config{
LogLevel: viper.GetString("log_level"),
OwnRegistry: viper.GetBool("bring_own_registry"),
OwnRegistryAdr: viper.GetString("own_registry_adr"),
OwnRegistryPort: viper.GetString("own_registry_port"),
ZotConfigPath: viper.GetString("zotConfigPath"),
Input: viper.GetString("url_or_file"),
}, nil
}
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved
34 changes: 34 additions & 0 deletions internal/satellite/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package satellite

import (
"encoding/json"
"net/http"

"container-registry.com/harbor-satellite/internal/server"
)

type SatelliteRegistrar struct{}

type SatelliteResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
StatusCode int `json:"status_code"`
}

func (sr *SatelliteRegistrar) RegisterRoutes(router server.Router) {
satelliteGroup := router.Group("/satellite")
satelliteGroup.HandleFunc("/ping", sr.Ping)
}

func (sr *SatelliteRegistrar) Ping(w http.ResponseWriter, r *http.Request) {
response := SatelliteResponse{
Success: true,
Message: "Ping satellite successful",
StatusCode: http.StatusOK,
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
28 changes: 28 additions & 0 deletions internal/server/middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package server

import (
"log"
"net/http"
"time"
)

type Middleware func(http.Handler) http.Handler

// LoggingMiddleware creates a new logging middleware
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()

// Call the next handler
next.ServeHTTP(w, r)

// Log the request
log.Printf(
"%s %s %s %s",
r.RemoteAddr,
r.Method,
r.URL.Path,
time.Since(start),
)
})
}
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved
30 changes: 30 additions & 0 deletions internal/server/profiling.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package server

import (
"net/http/pprof"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

type MetricsRegistrar struct{}

type DebugRegistrar struct{}

func (m *MetricsRegistrar) RegisterRoutes(router Router) {
metricsGroup := router.Group("/metrics")
metricsGroup.Handle("", promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{}))
}

func (dr *DebugRegistrar) RegisterRoutes(router Router) {
debugGroup := router.Group("/debug/pprof")
debugGroup.HandleFunc("/", pprof.Index)
debugGroup.HandleFunc("/profile", pprof.Profile)
debugGroup.HandleFunc("/trace", pprof.Trace)
debugGroup.HandleFunc("/symbol", pprof.Symbol)
debugGroup.Handle("/heap", pprof.Handler("heap"))
debugGroup.Handle("/goroutine", pprof.Handler("goroutine"))
debugGroup.Handle("/threadcreate", pprof.Handler("threadcreate"))
debugGroup.Handle("/block", pprof.Handler("block"))
debugGroup.Handle("/mutex", pprof.Handler("mutex"))
}
66 changes: 66 additions & 0 deletions internal/server/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package server

import "net/http"

type Router interface {
// Handle registers a new route with the given pattern and handler on the router mux
Handle(pattern string, handler http.Handler)
// HandleFunc registers a new route with the given pattern and handler function on the router mux
HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
// ServeHTTP would dispatch the request to the default mux
ServeHTTP(w http.ResponseWriter, r *http.Request)
// Use adds middleware to the default router
Use(middleware ...Middleware)
// Group called on the router would create a group with the given prefix
//and would inherit the middleware from the router and would be added to the root group of the router
Group(prefix string) *RouterGroup
}

type Endpoint struct {
Method string
Path string
}
// DefaultRouter
type DefaultRouter struct {
// mux is the default http.ServeMux
mux *http.ServeMux
// middleware is the list of middleware to be applied to default router and all groups inside it
middleware []Middleware
// rootGroup is the root RouterGroup
rootGroup *RouterGroup
// endpoints is the list of all registered endpoints
Endpoints []Endpoint
}

// NewDefaultRouter creates a new DefaultRouter with the given prefix
func NewDefaultRouter(prefix string) *DefaultRouter {
dr := &DefaultRouter{mux: http.NewServeMux()}
dr.rootGroup = &RouterGroup{prefix: prefix, router: dr}
return dr
}


func (r *DefaultRouter) Handle(pattern string, handler http.Handler) {
r.mux.Handle(pattern, handler)
}


func (r *DefaultRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
r.mux.ServeHTTP(w, req)
}


func (dr *DefaultRouter) HandleFunc(pattern string, handlerFunc func(http.ResponseWriter, *http.Request)) {
dr.Handle(pattern, http.HandlerFunc(handlerFunc))
}


func (dr *DefaultRouter) Use(middleware ...Middleware) {
dr.middleware = append(dr.middleware, middleware...)
}


// Group creates a new RouterGroup under the rootGroup with the given prefix
func (dr *DefaultRouter) Group(prefix string) *RouterGroup {
return dr.rootGroup.Group(prefix)
}
36 changes: 36 additions & 0 deletions internal/server/router_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

package server

import "net/http"

// RouterGroup represents a group of routes with a common prefix and middleware
type RouterGroup struct {
prefix string
middleware []Middleware
router Router
}

func (rg *RouterGroup) Use(middleware ...Middleware) {
rg.middleware = append(rg.middleware, middleware...)
}

func (rg *RouterGroup) Handle(pattern string, handler http.Handler) {
fullPattern := rg.prefix + pattern
wrappedHandler := handler
for i := len(rg.middleware) - 1; i >= 0; i-- {
wrappedHandler = rg.middleware[i](wrappedHandler)
}
rg.router.Handle(fullPattern, wrappedHandler)
}

func (rg *RouterGroup) HandleFunc(pattern string, handlerFunc func(http.ResponseWriter, *http.Request)) {
rg.Handle(pattern, http.HandlerFunc(handlerFunc))
}

func (rg *RouterGroup) Group(prefix string) *RouterGroup {
return &RouterGroup{
prefix: rg.prefix + prefix,
middleware: append([]Middleware{}, rg.middleware...),
router: rg.router,
}
}
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved
68 changes: 68 additions & 0 deletions internal/server/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package server

import (
"context"
"errors"
"net/http"

"container-registry.com/harbor-satellite/internal/config"
"github.com/rs/zerolog"
"golang.org/x/sync/errgroup"
)

type RouteRegistrar interface {
RegisterRoutes(router Router)
}

// App struct with middleware support
type App struct {
router Router
registrars []RouteRegistrar
server *http.Server
ctx context.Context
Logger *zerolog.Logger
config *config.Config
}

func NewApp(router Router, ctx context.Context, logger *zerolog.Logger, config *config.Config, registrars ...RouteRegistrar) *App {
return &App{
router: router,
registrars: registrars,
ctx: ctx,
Logger: logger,
config: config,
server: &http.Server{Addr: ":9090", Handler: router},
}
}

func (a *App) SetupRoutes() {
for _, registrar := range a.registrars {
registrar.RegisterRoutes(a.router)
}
}

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.router.ServeHTTP(w, r)
}
func (a *App) Start() error {
return a.server.ListenAndServe()
}

func (a *App) Shutdown(ctx context.Context) error {
return a.server.Shutdown(ctx)
}

func (a *App) SetupServer(g *errgroup.Group) {
g.Go(func() error {
a.Logger.Info().Msg("Starting server on :9090")
if err := a.Start(); err != nil && !errors.Is(err, http.ErrServerClosed) {
return err
}
return nil
})
g.Go(func() error {
<-a.ctx.Done()
a.Logger.Info().Msg("Shutting down server")
return a.Shutdown(a.ctx)
})
}
6 changes: 6 additions & 0 deletions logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,9 @@ func FromContext(ctx context.Context) *zerolog.Logger {
}
return logger
}

func SetupLogger(ctx context.Context, logLevel string) *zerolog.Logger{
ctx = AddLoggerToContext(ctx, logLevel)
logger := FromContext(ctx)
return logger
}
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved
Loading