-
-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
321 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
package models | ||
|
||
import ( | ||
"context" | ||
"crypto/rsa" | ||
"errors" | ||
"fmt" | ||
"net/url" | ||
"time" | ||
|
||
"github.com/redis/go-redis/v9" | ||
"github.com/sirupsen/logrus" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
type ServerConfig struct { | ||
Domain *url.URL | ||
Bind string | ||
PrivateKey *rsa.PrivateKey | ||
} | ||
|
||
type ServiceConfig struct { | ||
Name string | ||
Summary string | ||
IconURL *url.URL | ||
ImageURL *url.URL | ||
} | ||
|
||
type RelayConfigV2 struct { | ||
serverConfig *ServerConfig | ||
serviceConfig *ServiceConfig | ||
redisOptions *redis.Options | ||
jobConcurrency int | ||
} | ||
|
||
type RelayConfigV2BuilderOptions struct { | ||
WithServerConfig bool | ||
WithJobConcurrency bool | ||
} | ||
|
||
func buildServerConfig() (*ServerConfig, error) { | ||
domain, err := url.ParseRequestURI("https://" + viper.GetString("RELAY_DOMAIN")) | ||
if err != nil { | ||
return nil, errors.New("RELAY_DOMAIN: " + err.Error()) | ||
} | ||
|
||
privateKey, err := readPrivateKeyRSA(viper.GetString("ACTOR_PEM")) | ||
if err != nil { | ||
return nil, errors.New("ACTOR_PEM: " + err.Error()) | ||
} | ||
|
||
return &ServerConfig{ | ||
Domain: domain, | ||
Bind: viper.GetString("RELAY_BIND"), | ||
PrivateKey: privateKey, | ||
}, nil | ||
} | ||
|
||
func buildServiceConfig() (*ServiceConfig, error) { | ||
// Required fields | ||
name := viper.GetString("RELAY_SERVICENAME") | ||
summary := viper.GetString("RELAY_SUMMARY") | ||
|
||
// Optional fields | ||
iconURL, err := url.ParseRequestURI(viper.GetString("RELAY_ICON")) | ||
if err != nil { | ||
logrus.Warn("RELAY_ICON: INVALID OR EMPTY. THIS COLUMN IS DISABLED.") | ||
iconURL = nil | ||
} | ||
imageURL, err := url.ParseRequestURI(viper.GetString("RELAY_IMAGE")) | ||
if err != nil { | ||
logrus.Warn("RELAY_IMAGE: INVALID OR EMPTY. THIS COLUMN IS DISABLED.") | ||
imageURL = nil | ||
} | ||
|
||
return &ServiceConfig{ | ||
Name: name, | ||
Summary: summary, | ||
IconURL: iconURL, | ||
ImageURL: imageURL, | ||
}, nil | ||
} | ||
|
||
func NewRelayConfigV2(options RelayConfigV2BuilderOptions) (*RelayConfigV2, error) { | ||
result := RelayConfigV2{} | ||
|
||
serviceOptions, err := buildServiceConfig() | ||
if err != nil { | ||
return nil, err | ||
} | ||
result.serviceConfig = serviceOptions | ||
|
||
redisOptions, err := redis.ParseURL(viper.GetString("REDIS_URL")) | ||
if err != nil { | ||
return nil, errors.New("REDIS_URL: " + err.Error()) | ||
} | ||
result.redisOptions = redisOptions | ||
|
||
// Works with API Server | ||
if options.WithServerConfig { | ||
serverOptions, err := buildServerConfig() | ||
if err != nil { | ||
return nil, err | ||
} | ||
result.serverConfig = serverOptions | ||
} else { | ||
result.serverConfig = nil | ||
} | ||
|
||
// Works with Job Worker | ||
if options.WithJobConcurrency { | ||
viper.SetDefault("JOB_CONCURRENCY", 10) | ||
jobConcurrency := viper.GetInt("JOB_CONCURRENCY") | ||
if jobConcurrency < 1 { | ||
return nil, errors.New("JOB_CONCURRENCY: Invalid Value") | ||
} | ||
result.jobConcurrency = jobConcurrency | ||
} else { | ||
result.jobConcurrency = 0 | ||
} | ||
|
||
return &result, nil | ||
} | ||
|
||
// ServerConfig is API Server options. | ||
func (config *RelayConfigV2) ServerConfig() (*ServerConfig, error) { | ||
if config.serverConfig != nil { | ||
return config.serverConfig, nil | ||
} | ||
return nil, errors.New("this configuration does not have ServerConfig") | ||
} | ||
|
||
// ServiceConfig is Relay Service options. | ||
func (config *RelayConfigV2) ServiceConfig() *ServiceConfig { | ||
return config.serviceConfig | ||
} | ||
|
||
// RedisOptions is Redis options. | ||
func (config *RelayConfigV2) RedisOptions() *redis.Options { | ||
return config.redisOptions | ||
} | ||
|
||
// JobConcurrency is Job concurrency. | ||
func (config *RelayConfigV2) JobConcurrency() (int, error) { | ||
if config.jobConcurrency == 0 { | ||
return 0, errors.New("this configuration does not have JobConcurrency") | ||
} | ||
return config.jobConcurrency, nil | ||
} | ||
|
||
func (config *RelayConfigV2) DumpWelcomeMessage(moduleName string, version string) string { | ||
message := fmt.Sprintf(`Welcome to Activity-Relay %s - %s\n`, version, moduleName) | ||
message += fmt.Sprintf(`- Configuration\n`) | ||
message += fmt.Sprintf(`RELAY NAME : %s\n`, config.serviceConfig.Name) | ||
message += fmt.Sprintf(`REDIS URL : %s\n`, config.redisOptions.Addr) | ||
if config.serverConfig != nil { | ||
message += fmt.Sprintf(`RELAY DOMAIN : %s\n`, config.serverConfig.Domain.Host) | ||
message += fmt.Sprintf(`BIND ADDRESS : %s\n`, config.serverConfig.Bind) | ||
} | ||
if config.jobConcurrency != 0 { | ||
message += fmt.Sprintf(`JOB_CONCURRENCY : %d\n`, config.jobConcurrency) | ||
} | ||
|
||
return message | ||
} | ||
|
||
func (config *RelayConfigV2) NewRedisClient(ctx context.Context) (*redis.Client, error) { | ||
redisClient := redis.NewClient(config.redisOptions) | ||
|
||
pCtx, cancel := context.WithTimeout(ctx, 5*time.Second) | ||
defer cancel() | ||
|
||
_, err := redisClient.Ping(pCtx).Result() | ||
if err != nil { | ||
return nil, err | ||
} | ||
return redisClient, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package models | ||
|
||
import ( | ||
"context" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/spf13/viper" | ||
) | ||
|
||
func TestNewRelayConfigV2(t *testing.T) { | ||
t.Run("success generate valid options", func(t *testing.T) { | ||
relayConfig, err := NewRelayConfigV2(RelayConfigV2BuilderOptions{ | ||
WithServerConfig: true, | ||
WithJobConcurrency: true, | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
// ServerConfig | ||
if relayConfig.serverConfig.Domain.Host != "relay.toot.yukimochi.jp" { | ||
t.Error("fail - parse: RelayConfig.serverConfig.Domain") | ||
} | ||
if relayConfig.serverConfig.Bind != "0.0.0.0:8080" { | ||
t.Error("fail - parse: RelayConfig.serverConfig.Bind") | ||
} | ||
if relayConfig.serverConfig.PrivateKey == nil { | ||
t.Error("fail - parse: RelayConfig.serverConfig.PrivateKey") | ||
} | ||
|
||
// ServiceConfig | ||
if relayConfig.serviceConfig.Name != "YUKIMOCHI Toot Relay Service" { | ||
t.Error("fail - parse: RelayConfig.serviceConfig.Name") | ||
} | ||
if relayConfig.serviceConfig.Summary != "YUKIMOCHI Toot Relay Service is Running by Activity-Relay" { | ||
t.Error("fail - parse: RelayConfig.serviceConfig.Summary") | ||
} | ||
if relayConfig.serviceConfig.IconURL.String() != "https://example.com/example_icon.png" { | ||
t.Error("fail - parse: RelayConfig.serviceConfig.IconURL") | ||
} | ||
if relayConfig.serviceConfig.ImageURL.String() != "https://example.com/example_image.png" { | ||
t.Error("fail - parse: RelayConfig.serviceConfig.ImageURL") | ||
} | ||
|
||
// RedisOptions | ||
if relayConfig.redisOptions == nil { | ||
t.Error("fail - parse: RelayConfig.redisOptions") | ||
} | ||
|
||
// JobConcurrency | ||
if relayConfig.jobConcurrency != 50 { | ||
t.Error("fail - parse: RelayConfig.jobConcurrency") | ||
} | ||
}) | ||
|
||
t.Run("success generate valid options without serverConfig", func(t *testing.T) { | ||
relayConfig, err := NewRelayConfigV2(RelayConfigV2BuilderOptions{ | ||
WithServerConfig: false, | ||
WithJobConcurrency: true, | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
// ServerConfig | ||
if relayConfig.serverConfig != nil { | ||
t.Error("fail - parse: RelayConfig.serverConfig") | ||
} | ||
}) | ||
|
||
t.Run("success generate valid options without jobConcurrency", func(t *testing.T) { | ||
relayConfig, err := NewRelayConfigV2(RelayConfigV2BuilderOptions{ | ||
WithServerConfig: true, | ||
WithJobConcurrency: false, | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
// JobConcurrency | ||
if relayConfig.jobConcurrency != 0 { | ||
t.Error("fail - parse: RelayConfig.jobConcurrency") | ||
} | ||
}) | ||
|
||
t.Run("fail invalid options", func(t *testing.T) { | ||
invalidExamples := map[string]string{ | ||
"ACTOR_PEM@notFound": "../misc/test/notfound.pem", | ||
"REDIS_URL@invalidURL": "", | ||
} | ||
|
||
for key, invalidValue := range invalidExamples { | ||
viperKey := strings.Split(key, "@")[0] | ||
validValue := viper.GetString(viperKey) | ||
|
||
viper.Set(viperKey, invalidValue) | ||
_, err := NewRelayConfig() | ||
if err == nil { | ||
t.Error("fail - invalid value should be raise error : " + key) | ||
} | ||
|
||
viper.Set(viperKey, validValue) | ||
} | ||
}) | ||
} | ||
|
||
func TestNewRedisClient(t *testing.T) { | ||
t.Run("success create client for reachable redis serer", func(t *testing.T) { | ||
relayConfig, err := NewRelayConfigV2(RelayConfigV2BuilderOptions{ | ||
WithServerConfig: false, | ||
WithJobConcurrency: false, | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
_, err = relayConfig.NewRedisClient(context.Background()) | ||
if err != nil { | ||
t.Error("fail - create client for reachable redis serer") | ||
} | ||
}) | ||
|
||
t.Run("fail create client for unreachable redis serer", func(t *testing.T) { | ||
validURL := viper.GetString("REDIS_URL") | ||
viper.Set("REDIS_URL", "redis://localhost:6380") | ||
|
||
relayConfig, err := NewRelayConfigV2(RelayConfigV2BuilderOptions{ | ||
WithServerConfig: false, | ||
WithJobConcurrency: false, | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
_, err = relayConfig.NewRedisClient(context.Background()) | ||
if err == nil { | ||
t.Error("fail - create client for unreachable redis serer") | ||
} | ||
|
||
viper.Set("REDIS_URL", validURL) | ||
}) | ||
} |