Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal/ini: adding ini parser #2024

Closed
wants to merge 76 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
ac49d0e
Revert
xibz Apr 11, 2017
c6b043e
balh
xibz Jun 5, 2017
a371e0a
Merge branch 'master' of github.com:aws/aws-sdk-go into upstream
xibz Jul 3, 2018
4898aa7
Merge branch 'master' of github.com:aws/aws-sdk-go into upstream
xibz Jul 10, 2018
fceb94b
Merge branch 'master' of github.com:aws/aws-sdk-go into upstream
xibz Jul 18, 2018
7e4bed2
Merge branch 'master' of github.com:aws/aws-sdk-go into upstream
xibz Aug 10, 2018
8442157
Merge branch 'master' of github.com:aws/aws-sdk-go into upstream
xibz Aug 15, 2018
ecb64f2
Merge branch 'master' of github.com:aws/aws-sdk-go into upstream
xibz Aug 16, 2018
cf696ff
Merge branch 'master' of github.com:aws/aws-sdk-go into upstream
xibz Aug 23, 2018
3b3c2d9
Merge branch 'master' of github.com:aws/aws-sdk-go into upstream
xibz Sep 4, 2018
65a792e
Merge branch 'master' of github.com:aws/aws-sdk-go into upstream
xibz Sep 4, 2018
9372e5c
service/s3/s3manager: Fix Download Manager with iterator docs (#2131)
jasdel Sep 5, 2018
3238140
Add PR 2131 to pending changelog.
jasdel Sep 5, 2018
ce8b5cf
Add PR 2129 to pending changelog
jasdel Sep 5, 2018
7026443
Release v1.15.28
Sep 5, 2018
c3e789e
private/protocol: wrapping serialization errors (#2135)
xibz Sep 6, 2018
2cb1522
Update CHANGELOG_PENDING.md
xibz Sep 6, 2018
3862d54
Release v1.15.29
Sep 6, 2018
8fcba5c
removing feature (#2138)
xibz Sep 6, 2018
50009c8
Update version.go
xibz Sep 6, 2018
bc9a8db
private/protocol/json/jsonutil: Use json.Decoder to decrease memory a…
atsushi-ishibashi Sep 7, 2018
8b4cae3
Add PR 2115 to pending change log
jasdel Sep 7, 2018
bc077ca
Release v1.15.31
Sep 7, 2018
2176d7b
Release v1.15.32
Sep 10, 2018
438a0ac
Release v1.15.33
Sep 11, 2018
8d234f6
Release v1.15.34
Sep 12, 2018
508f32f
Release v1.15.35
Sep 13, 2018
c3671a0
Release v1.15.36
Sep 17, 2018
5930ca3
Release v1.15.37
Sep 18, 2018
c4dfd5e
Release v1.15.38
Sep 19, 2018
1bef87c
Release v1.15.39
Sep 20, 2018
5f1e913
Release v1.15.40
Sep 21, 2018
a7f81c7
aws/default: Add Authorization header support for endpoint credential…
jasdel Sep 24, 2018
f40c241
adding service ID to ec2metadata (#2160)
xibz Sep 24, 2018
ed55eab
Release v1.15.41
Sep 24, 2018
862f420
Release v1.15.42
Sep 25, 2018
5877d5b
private/protocol/restjson/restjson: Use json.Decoder to decrease memo…
atsushi-ishibashi Sep 25, 2018
11ece5b
private/protocol/jsonrpc/jsonrpc: Use json.Decoder to decrease memory…
atsushi-ishibashi Sep 25, 2018
9b653eb
Update pending change log for PR #2141 and #2142
jasdel Sep 25, 2018
b7bd6fe
Release v1.15.43
Sep 26, 2018
08b12eb
Release v1.15.44
Sep 27, 2018
d1f75d0
Release v1.15.45
Sep 28, 2018
e4e30a8
Release v1.15.46
Oct 1, 2018
5cf9777
aws/config: fix typo in Config struct documentation (#2169)
Oct 2, 2018
a02e36f
Adding region to api call metrics (#2175)
xibz Oct 2, 2018
d9ffbd4
service/cloudfront/sign: Do not Escape HTML when encode the cloudfron…
rogchap Oct 2, 2018
be01d38
private/model/api: Use modeled service signing version in code genera…
jasdel Oct 2, 2018
5253896
Add merged PRs to pending change log
jasdel Oct 2, 2018
74cd833
Release v1.15.47
Oct 2, 2018
7c7a2c9
aws/request: Add domain host label validation (#2186)
jasdel Oct 4, 2018
00299a0
service/s3: Refactor S3's RequestFailure error to be more generic (#2…
jasdel Oct 4, 2018
8283e06
Release v1.15.48
Oct 4, 2018
4dccf09
Release v1.15.49
Oct 5, 2018
2925dde
private/model/api: Correct spelling typo in service API (#2178)
jasdel Oct 8, 2018
c702bfc
Release v1.15.50
Oct 8, 2018
4d4d5d0
Release v1.15.51
Oct 9, 2018
eb23200
Adding ini parser
xibz Jul 3, 2018
10134a3
Adding support for base numbers
xibz Jul 4, 2018
d2b7af6
Adding support for negative values and scientific notation
xibz Jul 5, 2018
031dcd6
adding support for escaped strings
xibz Jul 5, 2018
5551bf0
renaming tables to sections and adding more docs
xibz Jul 5, 2018
f98254c
Adding number helper
xibz Jul 5, 2018
353740b
fixing parsing bugs
xibz Jul 6, 2018
bc91ff0
Adding const state names
xibz Jul 6, 2018
34eb206
removing nested section with '[ foo.bar ]' as it is not supported by …
xibz Jul 6, 2018
b66034b
Adding array value skipping
xibz Jul 6, 2018
12e729d
adding documentation
xibz Jul 6, 2018
e45e23c
add support for utf8 characters
xibz Jul 7, 2018
afbbc35
adding error code for unable to read files
xibz Jul 8, 2018
b4e9c25
Moving and updating based on feedback
xibz Jul 25, 2018
161d814
moving to root directory
xibz Jul 26, 2018
341b8d1
perf test
xibz Jul 31, 2018
c212294
Adding generic token
xibz Aug 1, 2018
e2d5de0
Generic AST
xibz Aug 7, 2018
0bda570
Updating based on feedback.
xibz Aug 7, 2018
ad96c19
syncing from tip
xibz Oct 15, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 22 additions & 25 deletions aws/session/shared_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package session

import (
"fmt"
"io/ioutil"

"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/go-ini/ini"

"github.com/aws/aws-sdk-go/internal/ini"
)

const (
Expand Down Expand Up @@ -66,7 +66,7 @@ type sharedConfig struct {

type sharedConfigFile struct {
Filename string
IniData *ini.File
IniData ini.Sections
}

// loadSharedConfig retrieves the configuration from the list of files
Expand Down Expand Up @@ -107,19 +107,16 @@ func loadSharedConfigIniFiles(filenames []string) ([]sharedConfigFile, error) {
files := make([]sharedConfigFile, 0, len(filenames))

for _, filename := range filenames {
b, err := ioutil.ReadFile(filename)
if err != nil {
sections, err := ini.OpenFile(filename)
if aerr, ok := err.(awserr.Error); ok && aerr.Code() == ini.ErrCodeUnableToReadFile {
// Skip files which can't be opened and read for whatever reason
continue
}

f, err := ini.Load(b)
if err != nil {
} else if err != nil {
return nil, SharedConfigLoadError{Filename: filename, Err: err}
}

files = append(files, sharedConfigFile{
Filename: filename, IniData: f,
Filename: filename, IniData: sections,
})
}

Expand Down Expand Up @@ -180,45 +177,45 @@ func (cfg *sharedConfig) setFromIniFiles(profile string, files []sharedConfigFil
// if a config file only includes aws_access_key_id but no aws_secret_access_key
// the aws_access_key_id will be ignored.
func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile) error {
section, err := file.IniData.GetSection(profile)
if err != nil {
section, ok := file.IniData.GetSection(profile)
if !ok {
// Fallback to to alternate profile name: profile <name>
section, err = file.IniData.GetSection(fmt.Sprintf("profile %s", profile))
if err != nil {
return SharedConfigProfileNotExistsError{Profile: profile, Err: err}
section, ok = file.IniData.GetSection(fmt.Sprintf("profile %s", profile))
if !ok {
return SharedConfigProfileNotExistsError{Profile: profile, Err: nil}
}
}

// Shared Credentials
akid := section.Key(accessKeyIDKey).String()
secret := section.Key(secretAccessKey).String()
akid := section.String(accessKeyIDKey)
secret := section.String(secretAccessKey)
if len(akid) > 0 && len(secret) > 0 {
cfg.Creds = credentials.Value{
AccessKeyID: akid,
SecretAccessKey: secret,
SessionToken: section.Key(sessionTokenKey).String(),
SessionToken: section.String(sessionTokenKey),
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", file.Filename),
}
}

// Assume Role
roleArn := section.Key(roleArnKey).String()
srcProfile := section.Key(sourceProfileKey).String()
credentialSource := section.Key(credentialSourceKey).String()
roleArn := section.String(roleArnKey)
srcProfile := section.String(sourceProfileKey)
credentialSource := section.String(credentialSourceKey)
hasSource := len(srcProfile) > 0 || len(credentialSource) > 0
if len(roleArn) > 0 && hasSource {
cfg.AssumeRole = assumeRoleConfig{
RoleARN: roleArn,
SourceProfile: srcProfile,
CredentialSource: credentialSource,
ExternalID: section.Key(externalIDKey).String(),
MFASerial: section.Key(mfaSerialKey).String(),
RoleSessionName: section.Key(roleSessionNameKey).String(),
ExternalID: section.String(externalIDKey),
MFASerial: section.String(mfaSerialKey),
RoleSessionName: section.String(roleSessionNameKey),
}
}

// Region
if v := section.Key(regionKey).String(); len(v) > 0 {
if v := section.String(regionKey); len(v) > 0 {
cfg.Region = v
}

Expand Down
4 changes: 2 additions & 2 deletions aws/session/shared_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"testing"

"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/go-ini/ini"
"github.com/aws/aws-sdk-go/internal/ini"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -140,7 +140,7 @@ func TestLoadSharedConfig(t *testing.T) {

func TestLoadSharedConfigFromFile(t *testing.T) {
filename := testConfigFilename
f, err := ini.Load(filename)
f, err := ini.OpenFile(filename)
if err != nil {
t.Fatalf("failed to load test config file, %s, %v", filename, err)
}
Expand Down
120 changes: 120 additions & 0 deletions internal/ini/ast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package ini

// ASTKind represents different states in the parse table
// and the type of AST that is being constructed
type ASTKind int

// ASTKind* is used in the parse table to transition between
// the different states
const (
ASTKindNone = ASTKind(iota)
ASTKindStart
ASTKindExpr
ASTKindEqualExpr
ASTKindStatement
ASTKindSkipStatement
ASTKindExprStatement
ASTKindSectionStatement
ASTKindNestedSectionStatement
ASTKindCompletedNestedSectionStatement
ASTKindCommentStatement
ASTKindCompletedSectionStatement
)

func (k ASTKind) String() string {
switch k {
case ASTKindNone:
return "none"
case ASTKindStart:
return "start"
case ASTKindExpr:
return "expr"
case ASTKindStatement:
return "stmt"
case ASTKindSectionStatement:
return "section_stmt"
case ASTKindExprStatement:
return "expr_stmt"
case ASTKindCommentStatement:
return "comment"
case ASTKindNestedSectionStatement:
return "nested_section_stmt"
case ASTKindCompletedSectionStatement:
return "completed_stmt"
case ASTKindSkipStatement:
return "skip"
default:
return ""
}
}

// AST interface allows us to determine what kind of node we
// are on and casting may not need to be necessary.
//
// The root is always the first node in Children
type AST struct {
Kind ASTKind
Root Token
RootToken bool
Children []AST
}

func newAST(kind ASTKind, root AST, children ...AST) AST {
return AST{
Kind: kind,
Children: append([]AST{root}, children...),
}
}

func newASTWithRootToken(kind ASTKind, root Token, children ...AST) AST {
return AST{
Kind: kind,
Root: root,
RootToken: true,
Children: children,
}
}

// AppendChild will append to the list of children an AST has.
func (a *AST) AppendChild(child AST) {
a.Children = append(a.Children, child)
}

// GetRoot will return the root AST which can be the first entry
// in the children list or a token.
func (a *AST) GetRoot() AST {
if a.RootToken {
return *a
}

if len(a.Children) == 0 {
return AST{}
}

return a.Children[0]
}

// GetChildren will return the current AST's list of children
func (a *AST) GetChildren() []AST {
if len(a.Children) == 0 {
return []AST{}
}

if a.RootToken {
return a.Children
}

return a.Children[1:]
}

// SetChildren will set and override all children of the AST.
func (a *AST) SetChildren(children []AST) {
if a.RootToken {
a.Children = children
} else {
a.Children = append(a.Children[:1], children...)
}
}

// Start is used to indicate the starting state of the parse table.
var Start = newAST(ASTKindStart, AST{})
40 changes: 40 additions & 0 deletions internal/ini/bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package ini

import (
oldini "github.com/go-ini/ini"
"testing"
)

const (
section = `[default]
region = us-west-2
credential_source = Ec2InstanceMetadata
s3 =
foo=bar
bar=baz
output = json

[assumerole]
output = json
region = us-west-2
`
)

func BenchmarkINIParser(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseBytes([]byte(section))
}
}

func BenchmarkGoINIParser(b *testing.B) {
for i := 0; i < b.N; i++ {
oldini.Load([]byte(section))
}
}

func BenchmarkTokenize(b *testing.B) {
lexer := iniLexer{}
for i := 0; i < b.N; i++ {
lexer.tokenize([]byte(section))
}
}
11 changes: 11 additions & 0 deletions internal/ini/comma_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ini

var commaRunes = []rune(",")

func isComma(b rune) bool {
return b == ','
}

func newCommaToken() Token {
return newToken(TokenComma, commaRunes, NoneType)
}
39 changes: 39 additions & 0 deletions internal/ini/comment_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ini

// isComment will return whether or not the next byte(s) is a
// comment.
func isComment(b []rune) bool {
if len(b) == 0 {
return false
}

switch b[0] {
case ';':
return true
case '#':
return true
case '/':
if len(b) > 1 {
return b[1] == '/'
}
}

return false
}

// newCommentToken will create a comment token and
// return how many bytes were read.
func newCommentToken(b []rune) (Token, int, error) {
i := 0
for ; i < len(b); i++ {
if b[i] == '\n' {
break
}

if len(b)-i > 2 && b[i] == '\r' && b[i+1] == '\n' {
break
}
}

return newToken(TokenComment, b[:i], NoneType), i, nil
}
30 changes: 30 additions & 0 deletions internal/ini/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Package ini is an LL(1) parser for configuration files.
//
// Example:
// sections, err := ini.OpenFile("/path/to/file")
// if err != nil {
// panic(err)
// }
//
// profile := "foo"
// section, ok := sections.GetSection(profile)
// if !ok {
// fmt.Printf("section %q could not be found", profile)
// }
//
// Below is the BNF that describes this parser
// Grammar:
// stmt -> value stmt'
// stmt' -> epsilon | op stmt
// value -> number | string | boolean | quoted_string
//
// section -> [ section'
// section' -> value section_close
// section_close -> ]
//
// SkipState will skip (NL WS)+
//
// comment -> # comment' | ; comment' | / comment_slash
// comment_slash -> / comment'
// comment' -> epsilon | value
package ini
4 changes: 4 additions & 0 deletions internal/ini/empty_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package ini

// emptyToken is used to satisfy the Token interface
var emptyToken = newToken(TokenNone, []rune{}, NoneType)
Loading