Skip to content

Commit

Permalink
Refactor needed methods to support gMSA linux (aws#3448)
Browse files Browse the repository at this point in the history
  • Loading branch information
saikiranakula-amzn committed Nov 4, 2022
1 parent 4aa977d commit 7ac5c2a
Show file tree
Hide file tree
Showing 24 changed files with 517 additions and 419 deletions.
40 changes: 40 additions & 0 deletions agent/api/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ package container

import (
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -1325,6 +1327,44 @@ func (c *Container) UpdateManagedAgentSentStatus(agentName string, status apicon
return false
}

// RequiresCredentialSpec checks if container needs a credentialspec resource
func (c *Container) RequiresCredentialSpec() bool {
credSpec, err := c.getCredentialSpec()
if err != nil || credSpec == "" {
return false
}

return true
}

// GetCredentialSpec is used to retrieve the current credentialspec resource
func (c *Container) GetCredentialSpec() (string, error) {
return c.getCredentialSpec()
}

func (c *Container) getCredentialSpec() (string, error) {
c.lock.RLock()
defer c.lock.RUnlock()

if c.DockerConfig.HostConfig == nil {
return "", errors.New("empty container hostConfig")
}

hostConfig := &dockercontainer.HostConfig{}
err := json.Unmarshal([]byte(*c.DockerConfig.HostConfig), hostConfig)
if err != nil || len(hostConfig.SecurityOpt) == 0 {
return "", errors.New("unable to obtain security options from container hostConfig")
}

for _, opt := range hostConfig.SecurityOpt {
if strings.HasPrefix(opt, "credentialspec") {
return opt, nil
}
}

return "", errors.New("unable to obtain credentialspec")
}

func (c *Container) GetManagedAgentStatus(agentName string) apicontainerstatus.ManagedAgentStatus {
c.lock.RLock()
defer c.lock.RUnlock()
Expand Down
110 changes: 110 additions & 0 deletions agent/api/container/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -970,3 +970,113 @@ func TestUpdateManagedAgentSentStatus(t *testing.T) {
})
}
}

func TestRequiresCredentialSpec(t *testing.T) {
testCases := []struct {
name string
container *Container
expectedOutput bool
}{
{
name: "hostconfig_nil",
container: &Container{},
expectedOutput: false,
},
{
name: "invalid_case",
container: getContainer("invalid"),
expectedOutput: false,
},
{
name: "empty_sec_opt",
container: getContainer("{\"NetworkMode\":\"bridge\"}"),
expectedOutput: false,
},
{
name: "missing_credentialspec",
container: getContainer("{\"SecurityOpt\": [\"invalid-sec-opt\"]}"),
expectedOutput: false,
},
{
name: "valid_credentialspec_file",
container: getContainer("{\"SecurityOpt\": [\"credentialspec:file://gmsa_gmsa-acct.json\"]}"),
expectedOutput: true,
},
{
name: "valid_credentialspec_s3",
container: getContainer("{\"SecurityOpt\": [\"credentialspec:arn:aws:s3:::${BucketName}/${ObjectName}\"]}"),
expectedOutput: true,
},
{
name: "valid_credentialspec_ssm",
container: getContainer("{\"SecurityOpt\": [\"credentialspec:arn:aws:ssm:region:aws_account_id:parameter/parameter_name\"]}"),
expectedOutput: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.expectedOutput, tc.container.RequiresCredentialSpec())
})
}
}

func TestGetCredentialSpecErr(t *testing.T) {
testCases := []struct {
name string
container *Container
expectedOutputString string
expectedErrorString string
}{
{
name: "hostconfig_nil",
container: &Container{},
expectedOutputString: "",
expectedErrorString: "empty container hostConfig",
},
{
name: "invalid_case",
container: getContainer("invalid"),
expectedOutputString: "",
expectedErrorString: "unable to obtain security options from container hostConfig",
},
{
name: "empty_sec_opt",
container: getContainer("{\"NetworkMode\":\"bridge\"}"),
expectedOutputString: "",
expectedErrorString: "unable to obtain security options from container hostConfig",
},
{
name: "missing_credentialspec",
container: getContainer("{\"SecurityOpt\": [\"invalid-sec-opt\"]}"),
expectedOutputString: "",
expectedErrorString: "unable to obtain credentialspec",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
expectedOutputStr, err := tc.container.GetCredentialSpec()
assert.Equal(t, tc.expectedOutputString, expectedOutputStr)
assert.EqualError(t, err, tc.expectedErrorString)
})
}
}

func TestGetCredentialSpecHappyPath(t *testing.T) {
c := getContainer("{\"SecurityOpt\": [\"credentialspec:file://gmsa_gmsa-acct.json\"]}")

expectedCredentialSpec := "credentialspec:file://gmsa_gmsa-acct.json"

credentialspec, err := c.GetCredentialSpec()
assert.NoError(t, err)
assert.EqualValues(t, expectedCredentialSpec, credentialspec)
}

func getContainer(hostConfig string) *Container {
c := &Container{
Name: "c",
}
c.DockerConfig.HostConfig = &hostConfig
return c
}
14 changes: 0 additions & 14 deletions agent/api/container/container_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,8 @@

package container

import (
"github.com/pkg/errors"
)

const (
// DockerContainerMinimumMemoryInBytes is the minimum amount of
// memory to be allocated to a docker container
DockerContainerMinimumMemoryInBytes = 4 * 1024 * 1024 // 4MB
)

// RequiresCredentialSpec checks if container needs a credentialspec resource
func (c *Container) RequiresCredentialSpec() bool {
return false
}

// GetCredentialSpec is used to retrieve the current credentialspec resource
func (c *Container) GetCredentialSpec() (string, error) {
return "", errors.New("unsupported platform")
}
46 changes: 0 additions & 46 deletions agent/api/container/container_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,54 +16,8 @@

package container

import (
"encoding/json"
"strings"

dockercontainer "github.com/docker/docker/api/types/container"
"github.com/pkg/errors"
)

const (
// DockerContainerMinimumMemoryInBytes is the minimum amount of
// memory to be allocated to a docker container
DockerContainerMinimumMemoryInBytes = 256 * 1024 * 1024 // 256MB
)

// RequiresCredentialSpec checks if container needs a credentialspec resource
func (c *Container) RequiresCredentialSpec() bool {
credSpec, err := c.getCredentialSpec()
if err != nil || credSpec == "" {
return false
}

return true
}

// GetCredentialSpec is used to retrieve the current credentialspec resource
func (c *Container) GetCredentialSpec() (string, error) {
return c.getCredentialSpec()
}

func (c *Container) getCredentialSpec() (string, error) {
c.lock.RLock()
defer c.lock.RUnlock()

if c.DockerConfig.HostConfig == nil {
return "", errors.New("empty container hostConfig")
}

hostConfig := &dockercontainer.HostConfig{}
err := json.Unmarshal([]byte(*c.DockerConfig.HostConfig), hostConfig)
if err != nil || len(hostConfig.SecurityOpt) == 0 {
return "", errors.New("unable to obtain security options from container hostConfig")
}

for _, opt := range hostConfig.SecurityOpt {
if strings.HasPrefix(opt, "credentialspec") {
return opt, nil
}
}

return "", errors.New("unable to obtain credentialspec")
}
133 changes: 0 additions & 133 deletions agent/api/container/container_windows_test.go

This file was deleted.

Loading

0 comments on commit 7ac5c2a

Please sign in to comment.