-
Notifications
You must be signed in to change notification settings - Fork 0
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
6 changed files
with
234 additions
and
19 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
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,31 @@ | ||
// Copyright 2024 HUMAN Security. | ||
// Use of this source code is governed by a MIT style | ||
// license that can be found in the LICENSE file. | ||
|
||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"github.com/perimeterx/envite" | ||
"github.com/perimeterx/envite/seed/redis" | ||
) | ||
|
||
// buildRedisSeed is a builder function that constructs a new Redis seed component. | ||
// It takes a byte slice of JSON data as input. | ||
// The function attempts to parse the JSON data into a mongo.SeedConfig struct, which defines the configuration | ||
// for a Redis seed component. If the JSON data is successfully parsed, it then uses this configuration | ||
// to instantiate and return a new MongoDB seed component via the redis.NewSeedComponent function. | ||
// | ||
// Returns: | ||
// - An envite.Component which is the redis.SeedComponent initialized with the provided configuration. | ||
// - An error if the JSON data cannot be parsed into a redis.SeedConfig struct. | ||
func buildRedisSeed(data []byte, _ flagValues, _ string) (envite.Component, error) { | ||
var config redis.SeedConfig | ||
err := json.Unmarshal(data, &config) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not parse config: %w", err) | ||
} | ||
|
||
return redis.NewSeedComponent(config), 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
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
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,131 @@ | ||
// Copyright 2024 HUMAN Security. | ||
// Use of this source code is governed by a MIT style | ||
// license that can be found in the LICENSE file. | ||
|
||
package redis | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/go-redis/redis/v8" | ||
"github.com/perimeterx/envite" | ||
"strconv" | ||
"sync" | ||
"sync/atomic" | ||
"time" | ||
) | ||
|
||
// ComponentType represents the type of the redis seed component. | ||
const ComponentType = "redis seed" | ||
|
||
// SeedComponent is a component for seeding redis with data. | ||
type SeedComponent struct { | ||
lock sync.Mutex | ||
config SeedConfig | ||
status atomic.Value | ||
writer *envite.Writer | ||
} | ||
|
||
// NewSeedComponent creates a new SeedComponent instance. | ||
func NewSeedComponent(config SeedConfig) *SeedComponent { | ||
r := &SeedComponent{config: config} | ||
r.status.Store(envite.ComponentStatusStopped) | ||
return r | ||
} | ||
|
||
func (r *SeedComponent) Type() string { | ||
return ComponentType | ||
} | ||
|
||
func (r *SeedComponent) AttachEnvironment(_ context.Context, _ *envite.Environment, writer *envite.Writer) error { | ||
r.writer = writer | ||
return nil | ||
} | ||
|
||
func (r *SeedComponent) Prepare(context.Context) error { | ||
return nil | ||
} | ||
|
||
func (r *SeedComponent) Start(ctx context.Context) error { | ||
r.lock.Lock() | ||
defer r.lock.Unlock() | ||
|
||
r.status.Store(envite.ComponentStatusStarting) | ||
|
||
err := r.seed(ctx) | ||
if err != nil { | ||
r.status.Store(envite.ComponentStatusFailed) | ||
return err | ||
} | ||
|
||
r.status.Store(envite.ComponentStatusFinished) | ||
|
||
return nil | ||
} | ||
|
||
func (r *SeedComponent) seed(ctx context.Context) error { | ||
r.writer.WriteString("starting redis seed") | ||
client, err := r.clientProvider() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = client.FlushAll(ctx).Err() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, hashData := range r.config.Data { | ||
var count int | ||
err = client.HSet(ctx, hashData.Key, hashData.Fields).Err() | ||
|
||
if err != nil { | ||
return err | ||
} | ||
if hashData.TTL > 0 { | ||
err = client.Expire(ctx, hashData.Key, time.Duration(hashData.TTL)*time.Second).Err() | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
count = len(hashData.Fields) | ||
|
||
r.writer.WriteString(fmt.Sprintf( | ||
"inserted %s fields to %s", | ||
r.writer.Color.Green(strconv.Itoa(count)), | ||
r.writer.Color.Green(hashData.Key), | ||
)) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (r *SeedComponent) clientProvider() (*redis.Client, error) { | ||
if r.config.ClientProvider != nil { | ||
return r.config.ClientProvider() | ||
} | ||
|
||
options, err := redis.ParseURL(r.config.Address) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return redis.NewClient(options), nil | ||
} | ||
|
||
func (r *SeedComponent) Stop(context.Context) error { | ||
r.status.Store(envite.ComponentStatusStopped) | ||
return nil | ||
} | ||
|
||
func (r *SeedComponent) Cleanup(context.Context) error { | ||
return nil | ||
} | ||
|
||
func (r *SeedComponent) Status(context.Context) (envite.ComponentStatus, error) { | ||
return r.status.Load().(envite.ComponentStatus), nil | ||
} | ||
|
||
func (r *SeedComponent) Config() any { | ||
return r.config | ||
} |
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,33 @@ | ||
// Copyright 2024 HUMAN Security. | ||
// Use of this source code is governed by a MIT style | ||
// license that can be found in the LICENSE file. | ||
|
||
package redis | ||
|
||
import "github.com/go-redis/redis/v8" | ||
|
||
// SeedConfig represents the configuration for the redis seed component. | ||
type SeedConfig struct { | ||
// Address - a valid redis server address to connect to | ||
Address string `json:"address,omitempty"` | ||
|
||
// ClientProvider - can be used as an alternative to Address, provides a redis client to use. | ||
// available only via code, not available in config files. | ||
// if both ClientProvider and Address are provided, ClientProvider is used. | ||
ClientProvider func() (*redis.Client, error) `json:"-"` | ||
|
||
// Data - a list of objects, each represents a redis key and its data | ||
Data []*SeedData `json:"data,omitempty"` | ||
} | ||
|
||
// SeedData represents data for a redis hash. | ||
type SeedData struct { | ||
// Key - the name of the redis key | ||
Key string `json:"key,omitempty"` | ||
|
||
// Fields - a map of field names and their values to insert using the redis HSet function: | ||
Fields []string `json:"fields,omitempty"` | ||
|
||
// TTL - the time to live for the key in seconds | ||
TTL int `json:"ttl,omitempty"` | ||
} |