-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added code for getting information of instances of aws or gcp.
- Loading branch information
Showing
2 changed files
with
299 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,181 @@ | ||
package server | ||
|
||
import ( | ||
"fmt" | ||
"github.com/logrusorgru/aurora" | ||
"log" | ||
"net" | ||
"strconv" | ||
"strings" | ||
"sync" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/ec2" | ||
) | ||
|
||
type CloudVendor string | ||
|
||
type LookupTable map[string][]*Record | ||
|
||
const ( | ||
TTL = 300 * time.Second | ||
) | ||
|
||
const ( | ||
CacheName = "CLOUD-NAME-SERVER" | ||
AWS CloudVendor = "AWS" | ||
GCP CloudVendor = "GCP" | ||
) | ||
|
||
type Store struct { | ||
cache *sync.Map | ||
awsconf *AwsConfig | ||
gcpconf *GcpConfig | ||
} | ||
|
||
type Record struct { | ||
Vendor CloudVendor | ||
ZoneOrRegion string | ||
PublicIP net.IP | ||
PrivateIP net.IP | ||
ExpiredAt time.Time | ||
} | ||
|
||
func (s *Store) Lookup(key string) ([]*Record, error) { | ||
m, ok := s.cache.Load(CacheName) | ||
if !ok { | ||
return nil, fmt.Errorf("[err] unknown error<not found store table>") | ||
} | ||
records, ok := m.(LookupTable)[key] | ||
if !ok { | ||
return []*Record{}, nil | ||
} | ||
return records, nil | ||
} | ||
|
||
func (s *Store) renewal() error { | ||
now := time.Now() | ||
|
||
table := make(LookupTable) | ||
count := 0 | ||
// aws renewal | ||
if s.awsconf != nil { | ||
// get running instances | ||
input := &ec2.DescribeInstancesInput{ | ||
Filters: []*ec2.Filter{ | ||
{Name: aws.String("instance-state-name"), Values: []*string{aws.String("running")}}, | ||
}, | ||
} | ||
|
||
for region, client := range s.awsconf.clients { | ||
output, err := client.DescribeInstances(input) | ||
if err != nil { | ||
return err | ||
} else { | ||
for _, rv := range output.Reservations { | ||
for _, inst := range rv.Instances { | ||
record := &Record{Vendor: AWS, ExpiredAt: now.Add(TTL), ZoneOrRegion: region} | ||
|
||
// insert public ip | ||
if inst.PublicIpAddress != nil { | ||
if value := net.ParseIP(*inst.PublicIpAddress); value != nil { | ||
record.PublicIP = value | ||
} | ||
} | ||
|
||
// insert private ip | ||
if inst.PrivateIpAddress != nil { | ||
if value := net.ParseIP(*inst.PrivateIpAddress); value != nil { | ||
record.PrivateIP = value | ||
} | ||
} | ||
|
||
count += 1 | ||
// register instance-id | ||
table[strings.ToLower(*inst.InstanceId)] = append(table[strings.ToLower(*inst.InstanceId)], record) | ||
|
||
// register name | ||
for _, tag := range inst.Tags { | ||
if *tag.Key == "Name" { | ||
table[strings.ToLower(*tag.Value)] = append(table[strings.ToLower(*tag.Value)], record) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
// gcp renewal | ||
if s.gcpconf != nil { | ||
for _, zone := range s.gcpconf.zones { | ||
gcpListCall := s.gcpconf.client.Instances.List(s.gcpconf.projectId, zone) | ||
gcpListCall.Filter("status = RUNNING") | ||
instances, err := gcpListCall.Do() | ||
if err != nil { | ||
return err | ||
} | ||
for _, instance := range instances.Items { | ||
if len(instance.NetworkInterfaces) > 0 { | ||
record := &Record{Vendor: GCP, ExpiredAt: now.Add(TTL), ZoneOrRegion: zone} | ||
// insert public ip | ||
if len(instance.NetworkInterfaces[0].AccessConfigs) > 0 { | ||
if value := net.ParseIP(instance.NetworkInterfaces[0].AccessConfigs[0].NatIP); value != nil { | ||
record.PublicIP = value | ||
} | ||
} | ||
// insert private ip | ||
if value := net.ParseIP(instance.NetworkInterfaces[0].NetworkIP); value != nil { | ||
record.PrivateIP = value | ||
} | ||
|
||
count += 1 | ||
// register instance-id | ||
table[strconv.FormatInt(int64(instance.Id), 10)] = append(table[strconv.FormatInt(int64(instance.Id), 10)], record) | ||
|
||
// register name | ||
table[strings.ToLower(instance.Name)] = append(table[strings.ToLower(instance.Name)], record) | ||
} | ||
} | ||
} | ||
} | ||
s.cache.Store(CacheName, table) | ||
log.Printf("[%s][%d] cache table %s\n", aurora.Yellow("update"), count, time.Now().String()) | ||
return nil | ||
} | ||
|
||
func (r *Record) TTL() time.Duration { | ||
now := time.Now() | ||
duration := r.ExpiredAt.Sub(now) | ||
if duration < 0 { | ||
return 5 * time.Second | ||
} | ||
return duration | ||
} | ||
|
||
func NewStore(awsconf *AwsConfig, gcpconf *GcpConfig) (*Store, error) { | ||
store := &Store{} | ||
store.cache = &sync.Map{} | ||
store.awsconf = awsconf | ||
store.gcpconf = gcpconf | ||
|
||
// a first renewal must be success | ||
if err := store.renewal(); err != nil { | ||
return nil, err | ||
} | ||
|
||
// periodic renewal | ||
go func() { | ||
tick := time.NewTicker(1 * time.Minute) | ||
for { | ||
select { | ||
case <-tick.C: | ||
if err := store.renewal(); err != nil { | ||
log.Printf("[err] renewal %+v\n", err) | ||
} | ||
} | ||
} | ||
}() | ||
return store, 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,118 @@ | ||
package server | ||
|
||
import ( | ||
"gopkg.in/yaml.v2" | ||
"io/ioutil" | ||
"os" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestNewStore(t *testing.T) { | ||
assert := assert.New(t) | ||
|
||
yamlPath := os.Getenv("TEST_YAML_PATH") | ||
if yamlPath != "" { | ||
config := make(map[interface{}]interface{}) | ||
bys, err := ioutil.ReadFile(yamlPath) | ||
assert.NoError(err) | ||
yaml.Unmarshal(bys, &config) | ||
assert.NoError(err) | ||
_, _, awsconfig, gcpconfig, err := ParseConfig(config) | ||
assert.NoError(err) | ||
|
||
store, err := NewStore(awsconfig, gcpconfig) | ||
assert.NoError(err) | ||
_ = store | ||
} | ||
|
||
} | ||
|
||
func TestStore_Lookup(t *testing.T) { | ||
assert := assert.New(t) | ||
|
||
yamlPath := os.Getenv("TEST_YAML_PATH") | ||
if yamlPath != "" { | ||
config := make(map[interface{}]interface{}) | ||
bys, err := ioutil.ReadFile(yamlPath) | ||
assert.NoError(err) | ||
yaml.Unmarshal(bys, &config) | ||
assert.NoError(err) | ||
_, _, awsconfig, gcpconfig, err := ParseConfig(config) | ||
assert.NoError(err) | ||
|
||
store, err := NewStore(awsconfig, gcpconfig) | ||
assert.NoError(err) | ||
|
||
// empty | ||
records, err := store.Lookup("empty") | ||
assert.NoError(err) | ||
assert.Len(records, 0) | ||
|
||
if os.Getenv("TEST_AWS_1") != "" { | ||
records, err := store.Lookup(os.Getenv("TEST_AWS_1")) | ||
assert.NoError(err) | ||
for _, record := range records { | ||
assert.NotEmpty(record.PublicIP.String()) | ||
assert.NotEmpty(record.PrivateIP.String()) | ||
assert.NotEmpty(record.ZoneOrRegion) | ||
assert.Equal(AWS, record.Vendor) | ||
} | ||
} | ||
|
||
if os.Getenv("TEST_GCP_1") != "" { | ||
records, err := store.Lookup(os.Getenv("TEST_GCP_1")) | ||
assert.NoError(err) | ||
for _, record := range records { | ||
assert.NotEmpty(record.PublicIP.String()) | ||
assert.NotEmpty(record.PrivateIP.String()) | ||
assert.NotEmpty(record.ZoneOrRegion) | ||
assert.Equal(GCP, record.Vendor) | ||
} | ||
} | ||
} | ||
|
||
} | ||
|
||
func TestRecord_TTL(t *testing.T) { | ||
assert := assert.New(t) | ||
|
||
yamlPath := os.Getenv("TEST_YAML_PATH") | ||
if yamlPath != "" { | ||
config := make(map[interface{}]interface{}) | ||
bys, err := ioutil.ReadFile(yamlPath) | ||
assert.NoError(err) | ||
yaml.Unmarshal(bys, &config) | ||
assert.NoError(err) | ||
_, _, awsconfig, gcpconfig, err := ParseConfig(config) | ||
assert.NoError(err) | ||
|
||
store, err := NewStore(awsconfig, gcpconfig) | ||
assert.NoError(err) | ||
|
||
table, ok := store.cache.Load(CacheName) | ||
assert.True(ok) | ||
for _, v := range table.(LookupTable) { | ||
for _, re := range v { | ||
assert.True(re.TTL() > (5 * time.Second)) | ||
} | ||
} | ||
} | ||
} | ||
|
||
//go test github.com/gjbae1212/cloud-name-server/server -bench=. | ||
func BenchmarkStore_Lookup(b *testing.B) { | ||
yamlPath := os.Getenv("TEST_YAML_PATH") | ||
if yamlPath != "" { | ||
config := make(map[interface{}]interface{}) | ||
bys, _ := ioutil.ReadFile(yamlPath) | ||
yaml.Unmarshal(bys, &config) | ||
_, _, awsconfig, gcpconfig, _ := ParseConfig(config) | ||
store, _ := NewStore(awsconfig, gcpconfig) | ||
for i := 0; i < b.N; i++ { | ||
store.Lookup(os.Getenv("TEST_AWS_1")) | ||
} | ||
} | ||
} |