Skip to content

Commit

Permalink
Added code for getting information of instances of aws or gcp.
Browse files Browse the repository at this point in the history
  • Loading branch information
gjbae1212 committed Jun 18, 2019
1 parent 668ab06 commit 95a6d47
Show file tree
Hide file tree
Showing 2 changed files with 299 additions and 0 deletions.
181 changes: 181 additions & 0 deletions server/store.go
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
}
118 changes: 118 additions & 0 deletions server/store_test.go
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"))
}
}
}

0 comments on commit 95a6d47

Please sign in to comment.