Skip to content

Commit

Permalink
add support for e2e latency for dynamic mode
Browse files Browse the repository at this point in the history
  • Loading branch information
minj131 committed Feb 19, 2024
1 parent 76772f4 commit 6768da7
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 0 deletions.
18 changes: 18 additions & 0 deletions pkg/fileutil/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fileutil

import (
"os"
"strconv"
"time"

"github.com/fsnotify/fsnotify"
Expand Down Expand Up @@ -104,3 +105,20 @@ func StartLoadDynamicFile(filename string, callBack FileChangeCallBack, stopCh <
}
}, time.Second, stopCh)
}

func CalculateTimeDeltaFromUnixInSeconds(from, to string) (float64, error) {
parsedFrom, err := strconv.ParseInt(from, 10, 64)
if err != nil {
return 0, err
}

parsedTo, err := strconv.ParseInt(to, 10, 64)
if err != nil {
return 0, err
}

timeFrom := time.Unix(parsedFrom, 0).UTC()
timeTo := time.Unix(parsedTo, 0).UTC()

return timeTo.Sub(timeFrom).Seconds(), nil
}
31 changes: 31 additions & 0 deletions pkg/fileutil/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,34 @@ func TestDeleteDynamicFile(t *testing.T) {
}
testA.mutex.Unlock()
}

func TestCalculateTimeDeltaFromUnixInSeconds(t *testing.T) {
type args struct {
from string
to string
}
cases := []struct {
input args
want float64
}{
{
args{"1706648530", "1706648539"},
9.0,
},
{
args{"1706648520", "1706648539"},
19.0,
},
}

for _, c := range cases {
out, err := CalculateTimeDeltaFromUnixInSeconds(c.input.from, c.input.to)
if err != nil {
t.Errorf("error is not expected")
}

if out != c.want {
t.Errorf("unexpected result: got %v but expected %v", out, c.want)
}
}
}
39 changes: 39 additions & 0 deletions pkg/mapper/dynamicfile/dynamicfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ package dynamicfile
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"sync"
"time"

"github.com/sirupsen/logrus"
"sigs.k8s.io/aws-iam-authenticator/pkg/arn"
"sigs.k8s.io/aws-iam-authenticator/pkg/config"
"sigs.k8s.io/aws-iam-authenticator/pkg/errutil"
"sigs.k8s.io/aws-iam-authenticator/pkg/fileutil"
"sigs.k8s.io/aws-iam-authenticator/pkg/metrics"
)

type DynamicFileMapStore struct {
Expand All @@ -21,9 +25,25 @@ type DynamicFileMapStore struct {
filename string
userIDStrict bool
usernamePrefixReserveList []string

skip bool
}

// Meta is the collection of fields which should be included in all top-level
// objects which are propagated for dynamic file mode
type Meta struct {
APIVersion string `json:"ApiVersion"`
// Time that the object takes from update time to load time
LastUpdatedDateTime string `json:"LastUpdatedDateTime"`
// Version is the version number of the update
Version string `json:"Version"`
// ClusterID is the id of the cluster
ClusterID string `json:"ClusterId"`
}

type DynamicFileData struct {
Meta

// RoleMappings is a list of mappings from AWS IAM Role to
// Kubernetes username + groups.
RoleMappings []config.RoleMapping `json:"mapRoles"`
Expand All @@ -48,6 +68,7 @@ func NewDynamicFileMapStore(cfg config.Config) (*DynamicFileMapStore, error) {
ms := DynamicFileMapStore{}
ms.filename = cfg.DynamicFilePath
ms.userIDStrict = cfg.DynamicFileUserIDStrict
ms.skip = true
return &ms, nil
}

Expand Down Expand Up @@ -165,6 +186,24 @@ func (ms *DynamicFileMapStore) CallBackForFileLoad(dynamicContent []byte) error
return err
}
ms.saveMap(userMappings, roleMappings, awsAccounts)

// emit load latency and if first time, skip emit and set to false to avoid instance bounce issue
if ms.skip {
ms.skip = false
} else {
latency, err := fileutil.CalculateTimeDeltaFromUnixInSeconds(dynamicFileData.LastUpdatedDateTime, strconv.FormatInt(time.Now().Unix(), 10))
if err != nil {
return fmt.Errorf("error parsing latency for dynamic file: %v", err)
}
metrics.Get().E2ELatency.WithLabelValues("dynamic_file").Observe(latency)
logrus.WithFields(logrus.Fields{
"ClusterId": dynamicFileData.ClusterID,
"Version": dynamicFileData.Version,
"Type": "dynamic_file",
"Latency": latency,
}).Infof("logging latency metric")
}

return nil
}

Expand Down
15 changes: 15 additions & 0 deletions pkg/mapper/dynamicfile/dynamicfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (
"time"

"github.com/google/go-cmp/cmp"
"github.com/prometheus/client_golang/prometheus"
"sigs.k8s.io/aws-iam-authenticator/pkg/config"
"sigs.k8s.io/aws-iam-authenticator/pkg/errutil"
"sigs.k8s.io/aws-iam-authenticator/pkg/fileutil"
"sigs.k8s.io/aws-iam-authenticator/pkg/metrics"
"sigs.k8s.io/aws-iam-authenticator/pkg/token"
)

Expand All @@ -18,6 +20,11 @@ var (
testRole = config.RoleMapping{RoleARN: "arn:aws:iam::012345678912:role/computer", Username: "computer", Groups: []string{"system:nodes"}}
)

func TestMain(m *testing.M) {
metrics.InitMetrics(prometheus.NewRegistry())
m.Run()
}

func makeStore(users map[string]config.UserMapping, roles map[string]config.RoleMapping, filename string, userIDStrict bool) DynamicFileMapStore {
ms := DynamicFileMapStore{
users: users,
Expand Down Expand Up @@ -96,6 +103,10 @@ func TestAWSAccount(t *testing.T) {

var origFileContent = `
{
"ApiVersion": "1",
"Version": "1",
"LastUpdatedDateTime": "12345678",
"ClusterId": "000000000098",
"mapRoles": [
{
"rolearn": "arn:aws:iam::000000000098:role/KubernetesAdmin",
Expand Down Expand Up @@ -133,6 +144,10 @@ var origFileContent = `

var updatedFileContent = `
{
"ApiVersion": "1",
"Version": "1",
"LastUpdatedDateTime": "12345678",
"ClusterId": "000000000098",
"mapRoles": [
{
"rolearn": "arn:aws:iam::000000000098:role/KubernetesAdmin",
Expand Down
10 changes: 10 additions & 0 deletions pkg/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type Metrics struct {
StsResponses *prometheus.CounterVec
DynamicFileFailures prometheus.Counter
StsThrottling prometheus.Counter
E2ELatency *prometheus.HistogramVec
}

func createMetrics(reg prometheus.Registerer) Metrics {
Expand Down Expand Up @@ -98,5 +99,14 @@ func createMetrics(reg prometheus.Registerer) Metrics {
Help: "Number of EC2 describe instances calls.",
},
),
E2ELatency: factory.NewHistogramVec(
prometheus.HistogramOpts{
Name: "dynamic_e2e_latency_seconds",
Namespace: Namespace,
Help: "End to end latency in seconds partitioned by type.",
Buckets: prometheus.ExponentialBuckets(2, 2, 8),
},
[]string{"type"},
),
}
}
21 changes: 21 additions & 0 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"log"
"net/http"
"regexp"
"strconv"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -75,6 +76,7 @@ type handler struct {
ec2Provider ec2provider.EC2Provider
clusterID string
backendMapper BackendMapper
skip bool
scrubbedAccounts []string
cfg config.Config
}
Expand Down Expand Up @@ -211,6 +213,7 @@ func (c *Server) getHandler(backendMapper BackendMapper, ec2DescribeQps int, ec2
backendMapper: backendMapper,
scrubbedAccounts: c.Config.ScrubbedAWSAccounts,
cfg: c.Config,
skip: true,
}

h.HandleFunc("/authenticate", h.authenticateEndpoint)
Expand Down Expand Up @@ -513,6 +516,24 @@ func (h *handler) CallBackForFileLoad(dynamicContent []byte) error {
} else {
logrus.Infof("BackendMode dynamic file got changed, but same with current mode, skip rebuild mapper")
}

// emit load latency and if first time, skip emit and set to false to avoid instance bounce issue
if h.skip {
h.skip = false
} else {
latency, err := fileutil.CalculateTimeDeltaFromUnixInSeconds(backendModes.LastUpdatedDateTime, strconv.FormatInt(time.Now().Unix(), 10))
if err != nil {
return fmt.Errorf("error parsing latency for dynamic backend mode file: %v", err)
}
metrics.Get().E2ELatency.WithLabelValues("dynamic_backend_mode").Observe(latency)
logrus.WithFields(logrus.Fields{
"ClusterId": backendModes.ClusterID,
"Version": backendModes.Version,
"Type": "dynamic_backend_mode",
"Latency": latency,
}).Infof("logging latency metric")
}

return nil
}

Expand Down
13 changes: 13 additions & 0 deletions pkg/server/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,20 @@ type BackendMapper struct {
currentModes string
}

// Meta is the collection of fields which should be included in all top-level
// objects which are propagated for dynamic file mode
type Meta struct {
APIVersion string `json:"ApiVersion"`
// Time that the object takes from update time to load time
LastUpdatedDateTime string `json:"LastUpdatedDateTime"`
// Version is the version number of the update
Version string `json:"Version"`
// ClusterID is the id of the cluster
ClusterID string `json:"ClusterId"`
}

// AccessConfig represents the configuration format for cluster access config via backend mode.
type BackendModeConfig struct {
Meta
BackendMode string `json:"backendMode"`
}

0 comments on commit 6768da7

Please sign in to comment.