mirror of https://github.com/grafana/grafana
parent
80ccea3d85
commit
6fb76b7c9b
6
vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
6
vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
@ -0,0 +1,425 @@ |
||||
/* |
||||
Package processcreds is a credential Provider to retrieve `credential_process` |
||||
credentials. |
||||
|
||||
WARNING: The following describes a method of sourcing credentials from an external |
||||
process. This can potentially be dangerous, so proceed with caution. Other |
||||
credential providers should be preferred if at all possible. If using this |
||||
option, you should make sure that the config file is as locked down as possible |
||||
using security best practices for your operating system. |
||||
|
||||
You can use credentials from a `credential_process` in a variety of ways. |
||||
|
||||
One way is to setup your shared config file, located in the default |
||||
location, with the `credential_process` key and the command you want to be |
||||
called. You also need to set the AWS_SDK_LOAD_CONFIG environment variable |
||||
(e.g., `export AWS_SDK_LOAD_CONFIG=1`) to use the shared config file. |
||||
|
||||
[default] |
||||
credential_process = /command/to/call |
||||
|
||||
Creating a new session will use the credential process to retrieve credentials. |
||||
NOTE: If there are credentials in the profile you are using, the credential |
||||
process will not be used. |
||||
|
||||
// Initialize a session to load credentials.
|
||||
sess, _ := session.NewSession(&aws.Config{ |
||||
Region: aws.String("us-east-1")}, |
||||
) |
||||
|
||||
// Create S3 service client to use the credentials.
|
||||
svc := s3.New(sess) |
||||
|
||||
Another way to use the `credential_process` method is by using |
||||
`credentials.NewCredentials()` and providing a command to be executed to |
||||
retrieve credentials: |
||||
|
||||
// Create credentials using the ProcessProvider.
|
||||
creds := processcreds.NewCredentials("/path/to/command") |
||||
|
||||
// Create service client value configured for credentials.
|
||||
svc := s3.New(sess, &aws.Config{Credentials: creds}) |
||||
|
||||
You can set a non-default timeout for the `credential_process` with another |
||||
constructor, `credentials.NewCredentialsTimeout()`, providing the timeout. To |
||||
set a one minute timeout: |
||||
|
||||
// Create credentials using the ProcessProvider.
|
||||
creds := processcreds.NewCredentialsTimeout( |
||||
"/path/to/command", |
||||
time.Duration(500) * time.Millisecond) |
||||
|
||||
If you need more control, you can set any configurable options in the |
||||
credentials using one or more option functions. For example, you can set a two |
||||
minute timeout, a credential duration of 60 minutes, and a maximum stdout |
||||
buffer size of 2k. |
||||
|
||||
creds := processcreds.NewCredentials( |
||||
"/path/to/command", |
||||
func(opt *ProcessProvider) { |
||||
opt.Timeout = time.Duration(2) * time.Minute |
||||
opt.Duration = time.Duration(60) * time.Minute |
||||
opt.MaxBufSize = 2048 |
||||
}) |
||||
|
||||
You can also use your own `exec.Cmd`: |
||||
|
||||
// Create an exec.Cmd
|
||||
myCommand := exec.Command("/path/to/command") |
||||
|
||||
// Create credentials using your exec.Cmd and custom timeout
|
||||
creds := processcreds.NewCredentialsCommand( |
||||
myCommand, |
||||
func(opt *processcreds.ProcessProvider) { |
||||
opt.Timeout = time.Duration(1) * time.Second |
||||
}) |
||||
*/ |
||||
package processcreds |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/json" |
||||
"fmt" |
||||
"io" |
||||
"io/ioutil" |
||||
"os" |
||||
"os/exec" |
||||
"runtime" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr" |
||||
"github.com/aws/aws-sdk-go/aws/credentials" |
||||
) |
||||
|
||||
const ( |
||||
// ProviderName is the name this credentials provider will label any
|
||||
// returned credentials Value with.
|
||||
ProviderName = `ProcessProvider` |
||||
|
||||
// ErrCodeProcessProviderParse error parsing process output
|
||||
ErrCodeProcessProviderParse = "ProcessProviderParseError" |
||||
|
||||
// ErrCodeProcessProviderVersion version error in output
|
||||
ErrCodeProcessProviderVersion = "ProcessProviderVersionError" |
||||
|
||||
// ErrCodeProcessProviderRequired required attribute missing in output
|
||||
ErrCodeProcessProviderRequired = "ProcessProviderRequiredError" |
||||
|
||||
// ErrCodeProcessProviderExecution execution of command failed
|
||||
ErrCodeProcessProviderExecution = "ProcessProviderExecutionError" |
||||
|
||||
// errMsgProcessProviderTimeout process took longer than allowed
|
||||
errMsgProcessProviderTimeout = "credential process timed out" |
||||
|
||||
// errMsgProcessProviderProcess process error
|
||||
errMsgProcessProviderProcess = "error in credential_process" |
||||
|
||||
// errMsgProcessProviderParse problem parsing output
|
||||
errMsgProcessProviderParse = "parse failed of credential_process output" |
||||
|
||||
// errMsgProcessProviderVersion version error in output
|
||||
errMsgProcessProviderVersion = "wrong version in process output (not 1)" |
||||
|
||||
// errMsgProcessProviderMissKey missing access key id in output
|
||||
errMsgProcessProviderMissKey = "missing AccessKeyId in process output" |
||||
|
||||
// errMsgProcessProviderMissSecret missing secret acess key in output
|
||||
errMsgProcessProviderMissSecret = "missing SecretAccessKey in process output" |
||||
|
||||
// errMsgProcessProviderPrepareCmd prepare of command failed
|
||||
errMsgProcessProviderPrepareCmd = "failed to prepare command" |
||||
|
||||
// errMsgProcessProviderEmptyCmd command must not be empty
|
||||
errMsgProcessProviderEmptyCmd = "command must not be empty" |
||||
|
||||
// errMsgProcessProviderPipe failed to initialize pipe
|
||||
errMsgProcessProviderPipe = "failed to initialize pipe" |
||||
|
||||
// DefaultDuration is the default amount of time in minutes that the
|
||||
// credentials will be valid for.
|
||||
DefaultDuration = time.Duration(15) * time.Minute |
||||
|
||||
// DefaultBufSize limits buffer size from growing to an enormous
|
||||
// amount due to a faulty process.
|
||||
DefaultBufSize = 1024 |
||||
|
||||
// DefaultTimeout default limit on time a process can run.
|
||||
DefaultTimeout = time.Duration(1) * time.Minute |
||||
) |
||||
|
||||
// ProcessProvider satisfies the credentials.Provider interface, and is a
|
||||
// client to retrieve credentials from a process.
|
||||
type ProcessProvider struct { |
||||
staticCreds bool |
||||
credentials.Expiry |
||||
originalCommand []string |
||||
|
||||
// Expiry duration of the credentials. Defaults to 15 minutes if not set.
|
||||
Duration time.Duration |
||||
|
||||
// ExpiryWindow will allow the credentials to trigger refreshing prior to
|
||||
// the credentials actually expiring. This is beneficial so race conditions
|
||||
// with expiring credentials do not cause request to fail unexpectedly
|
||||
// due to ExpiredTokenException exceptions.
|
||||
//
|
||||
// So a ExpiryWindow of 10s would cause calls to IsExpired() to return true
|
||||
// 10 seconds before the credentials are actually expired.
|
||||
//
|
||||
// If ExpiryWindow is 0 or less it will be ignored.
|
||||
ExpiryWindow time.Duration |
||||
|
||||
// A string representing an os command that should return a JSON with
|
||||
// credential information.
|
||||
command *exec.Cmd |
||||
|
||||
// MaxBufSize limits memory usage from growing to an enormous
|
||||
// amount due to a faulty process.
|
||||
MaxBufSize int |
||||
|
||||
// Timeout limits the time a process can run.
|
||||
Timeout time.Duration |
||||
} |
||||
|
||||
// NewCredentials returns a pointer to a new Credentials object wrapping the
|
||||
// ProcessProvider. The credentials will expire every 15 minutes by default.
|
||||
func NewCredentials(command string, options ...func(*ProcessProvider)) *credentials.Credentials { |
||||
p := &ProcessProvider{ |
||||
command: exec.Command(command), |
||||
Duration: DefaultDuration, |
||||
Timeout: DefaultTimeout, |
||||
MaxBufSize: DefaultBufSize, |
||||
} |
||||
|
||||
for _, option := range options { |
||||
option(p) |
||||
} |
||||
|
||||
return credentials.NewCredentials(p) |
||||
} |
||||
|
||||
// NewCredentialsTimeout returns a pointer to a new Credentials object with
|
||||
// the specified command and timeout, and default duration and max buffer size.
|
||||
func NewCredentialsTimeout(command string, timeout time.Duration) *credentials.Credentials { |
||||
p := NewCredentials(command, func(opt *ProcessProvider) { |
||||
opt.Timeout = timeout |
||||
}) |
||||
|
||||
return p |
||||
} |
||||
|
||||
// NewCredentialsCommand returns a pointer to a new Credentials object with
|
||||
// the specified command, and default timeout, duration and max buffer size.
|
||||
func NewCredentialsCommand(command *exec.Cmd, options ...func(*ProcessProvider)) *credentials.Credentials { |
||||
p := &ProcessProvider{ |
||||
command: command, |
||||
Duration: DefaultDuration, |
||||
Timeout: DefaultTimeout, |
||||
MaxBufSize: DefaultBufSize, |
||||
} |
||||
|
||||
for _, option := range options { |
||||
option(p) |
||||
} |
||||
|
||||
return credentials.NewCredentials(p) |
||||
} |
||||
|
||||
type credentialProcessResponse struct { |
||||
Version int |
||||
AccessKeyID string `json:"AccessKeyId"` |
||||
SecretAccessKey string |
||||
SessionToken string |
||||
Expiration *time.Time |
||||
} |
||||
|
||||
// Retrieve executes the 'credential_process' and returns the credentials.
|
||||
func (p *ProcessProvider) Retrieve() (credentials.Value, error) { |
||||
out, err := p.executeCredentialProcess() |
||||
if err != nil { |
||||
return credentials.Value{ProviderName: ProviderName}, err |
||||
} |
||||
|
||||
// Serialize and validate response
|
||||
resp := &credentialProcessResponse{} |
||||
if err = json.Unmarshal(out, resp); err != nil { |
||||
return credentials.Value{ProviderName: ProviderName}, awserr.New( |
||||
ErrCodeProcessProviderParse, |
||||
fmt.Sprintf("%s: %s", errMsgProcessProviderParse, string(out)), |
||||
err) |
||||
} |
||||
|
||||
if resp.Version != 1 { |
||||
return credentials.Value{ProviderName: ProviderName}, awserr.New( |
||||
ErrCodeProcessProviderVersion, |
||||
errMsgProcessProviderVersion, |
||||
nil) |
||||
} |
||||
|
||||
if len(resp.AccessKeyID) == 0 { |
||||
return credentials.Value{ProviderName: ProviderName}, awserr.New( |
||||
ErrCodeProcessProviderRequired, |
||||
errMsgProcessProviderMissKey, |
||||
nil) |
||||
} |
||||
|
||||
if len(resp.SecretAccessKey) == 0 { |
||||
return credentials.Value{ProviderName: ProviderName}, awserr.New( |
||||
ErrCodeProcessProviderRequired, |
||||
errMsgProcessProviderMissSecret, |
||||
nil) |
||||
} |
||||
|
||||
// Handle expiration
|
||||
p.staticCreds = resp.Expiration == nil |
||||
if resp.Expiration != nil { |
||||
p.SetExpiration(*resp.Expiration, p.ExpiryWindow) |
||||
} |
||||
|
||||
return credentials.Value{ |
||||
ProviderName: ProviderName, |
||||
AccessKeyID: resp.AccessKeyID, |
||||
SecretAccessKey: resp.SecretAccessKey, |
||||
SessionToken: resp.SessionToken, |
||||
}, nil |
||||
} |
||||
|
||||
// IsExpired returns true if the credentials retrieved are expired, or not yet
|
||||
// retrieved.
|
||||
func (p *ProcessProvider) IsExpired() bool { |
||||
if p.staticCreds { |
||||
return false |
||||
} |
||||
return p.Expiry.IsExpired() |
||||
} |
||||
|
||||
// prepareCommand prepares the command to be executed.
|
||||
func (p *ProcessProvider) prepareCommand() error { |
||||
|
||||
var cmdArgs []string |
||||
if runtime.GOOS == "windows" { |
||||
cmdArgs = []string{"cmd.exe", "/C"} |
||||
} else { |
||||
cmdArgs = []string{"sh", "-c"} |
||||
} |
||||
|
||||
if len(p.originalCommand) == 0 { |
||||
p.originalCommand = make([]string, len(p.command.Args)) |
||||
copy(p.originalCommand, p.command.Args) |
||||
|
||||
// check for empty command because it succeeds
|
||||
if len(strings.TrimSpace(p.originalCommand[0])) < 1 { |
||||
return awserr.New( |
||||
ErrCodeProcessProviderExecution, |
||||
fmt.Sprintf( |
||||
"%s: %s", |
||||
errMsgProcessProviderPrepareCmd, |
||||
errMsgProcessProviderEmptyCmd), |
||||
nil) |
||||
} |
||||
} |
||||
|
||||
cmdArgs = append(cmdArgs, p.originalCommand...) |
||||
p.command = exec.Command(cmdArgs[0], cmdArgs[1:]...) |
||||
p.command.Env = os.Environ() |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// executeCredentialProcess starts the credential process on the OS and
|
||||
// returns the results or an error.
|
||||
func (p *ProcessProvider) executeCredentialProcess() ([]byte, error) { |
||||
|
||||
if err := p.prepareCommand(); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
// Setup the pipes
|
||||
outReadPipe, outWritePipe, err := os.Pipe() |
||||
if err != nil { |
||||
return nil, awserr.New( |
||||
ErrCodeProcessProviderExecution, |
||||
errMsgProcessProviderPipe, |
||||
err) |
||||
} |
||||
|
||||
p.command.Stderr = os.Stderr // display stderr on console for MFA
|
||||
p.command.Stdout = outWritePipe // get creds json on process's stdout
|
||||
p.command.Stdin = os.Stdin // enable stdin for MFA
|
||||
|
||||
output := bytes.NewBuffer(make([]byte, 0, p.MaxBufSize)) |
||||
|
||||
stdoutCh := make(chan error, 1) |
||||
go readInput( |
||||
io.LimitReader(outReadPipe, int64(p.MaxBufSize)), |
||||
output, |
||||
stdoutCh) |
||||
|
||||
execCh := make(chan error, 1) |
||||
go executeCommand(*p.command, execCh) |
||||
|
||||
finished := false |
||||
var errors []error |
||||
for !finished { |
||||
select { |
||||
case readError := <-stdoutCh: |
||||
errors = appendError(errors, readError) |
||||
finished = true |
||||
case execError := <-execCh: |
||||
err := outWritePipe.Close() |
||||
errors = appendError(errors, err) |
||||
errors = appendError(errors, execError) |
||||
if errors != nil { |
||||
return output.Bytes(), awserr.NewBatchError( |
||||
ErrCodeProcessProviderExecution, |
||||
errMsgProcessProviderProcess, |
||||
errors) |
||||
} |
||||
case <-time.After(p.Timeout): |
||||
finished = true |
||||
return output.Bytes(), awserr.NewBatchError( |
||||
ErrCodeProcessProviderExecution, |
||||
errMsgProcessProviderTimeout, |
||||
errors) // errors can be nil
|
||||
} |
||||
} |
||||
|
||||
out := output.Bytes() |
||||
|
||||
if runtime.GOOS == "windows" { |
||||
// windows adds slashes to quotes
|
||||
out = []byte(strings.Replace(string(out), `\"`, `"`, -1)) |
||||
} |
||||
|
||||
return out, nil |
||||
} |
||||
|
||||
// appendError conveniently checks for nil before appending slice
|
||||
func appendError(errors []error, err error) []error { |
||||
if err != nil { |
||||
return append(errors, err) |
||||
} |
||||
return errors |
||||
} |
||||
|
||||
func executeCommand(cmd exec.Cmd, exec chan error) { |
||||
// Start the command
|
||||
err := cmd.Start() |
||||
if err == nil { |
||||
err = cmd.Wait() |
||||
} |
||||
|
||||
exec <- err |
||||
} |
||||
|
||||
func readInput(r io.Reader, w io.Writer, read chan error) { |
||||
tee := io.TeeReader(r, w) |
||||
|
||||
_, err := ioutil.ReadAll(tee) |
||||
|
||||
if err == io.EOF { |
||||
err = nil |
||||
} |
||||
|
||||
read <- err // will only arrive here when write end of pipe is closed
|
||||
} |
||||
30
vendor/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider.go
generated
vendored
30
vendor/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider.go
generated
vendored
@ -0,0 +1,26 @@ |
||||
package csm |
||||
|
||||
type metricException interface { |
||||
Exception() string |
||||
Message() string |
||||
} |
||||
|
||||
type requestException struct { |
||||
exception string |
||||
message string |
||||
} |
||||
|
||||
func (e requestException) Exception() string { |
||||
return e.exception |
||||
} |
||||
func (e requestException) Message() string { |
||||
return e.message |
||||
} |
||||
|
||||
type awsException struct { |
||||
requestException |
||||
} |
||||
|
||||
type sdkException struct { |
||||
requestException |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,141 @@ |
||||
package endpoints |
||||
|
||||
// Service identifiers
|
||||
//
|
||||
// Deprecated: Use client package's EndpointID value instead of these
|
||||
// ServiceIDs. These IDs are not maintained, and are out of date.
|
||||
const ( |
||||
A4bServiceID = "a4b" // A4b.
|
||||
AcmServiceID = "acm" // Acm.
|
||||
AcmPcaServiceID = "acm-pca" // AcmPca.
|
||||
ApiMediatailorServiceID = "api.mediatailor" // ApiMediatailor.
|
||||
ApiPricingServiceID = "api.pricing" // ApiPricing.
|
||||
ApiSagemakerServiceID = "api.sagemaker" // ApiSagemaker.
|
||||
ApigatewayServiceID = "apigateway" // Apigateway.
|
||||
ApplicationAutoscalingServiceID = "application-autoscaling" // ApplicationAutoscaling.
|
||||
Appstream2ServiceID = "appstream2" // Appstream2.
|
||||
AppsyncServiceID = "appsync" // Appsync.
|
||||
AthenaServiceID = "athena" // Athena.
|
||||
AutoscalingServiceID = "autoscaling" // Autoscaling.
|
||||
AutoscalingPlansServiceID = "autoscaling-plans" // AutoscalingPlans.
|
||||
BatchServiceID = "batch" // Batch.
|
||||
BudgetsServiceID = "budgets" // Budgets.
|
||||
CeServiceID = "ce" // Ce.
|
||||
ChimeServiceID = "chime" // Chime.
|
||||
Cloud9ServiceID = "cloud9" // Cloud9.
|
||||
ClouddirectoryServiceID = "clouddirectory" // Clouddirectory.
|
||||
CloudformationServiceID = "cloudformation" // Cloudformation.
|
||||
CloudfrontServiceID = "cloudfront" // Cloudfront.
|
||||
CloudhsmServiceID = "cloudhsm" // Cloudhsm.
|
||||
Cloudhsmv2ServiceID = "cloudhsmv2" // Cloudhsmv2.
|
||||
CloudsearchServiceID = "cloudsearch" // Cloudsearch.
|
||||
CloudtrailServiceID = "cloudtrail" // Cloudtrail.
|
||||
CodebuildServiceID = "codebuild" // Codebuild.
|
||||
CodecommitServiceID = "codecommit" // Codecommit.
|
||||
CodedeployServiceID = "codedeploy" // Codedeploy.
|
||||
CodepipelineServiceID = "codepipeline" // Codepipeline.
|
||||
CodestarServiceID = "codestar" // Codestar.
|
||||
CognitoIdentityServiceID = "cognito-identity" // CognitoIdentity.
|
||||
CognitoIdpServiceID = "cognito-idp" // CognitoIdp.
|
||||
CognitoSyncServiceID = "cognito-sync" // CognitoSync.
|
||||
ComprehendServiceID = "comprehend" // Comprehend.
|
||||
ConfigServiceID = "config" // Config.
|
||||
CurServiceID = "cur" // Cur.
|
||||
DatapipelineServiceID = "datapipeline" // Datapipeline.
|
||||
DaxServiceID = "dax" // Dax.
|
||||
DevicefarmServiceID = "devicefarm" // Devicefarm.
|
||||
DirectconnectServiceID = "directconnect" // Directconnect.
|
||||
DiscoveryServiceID = "discovery" // Discovery.
|
||||
DmsServiceID = "dms" // Dms.
|
||||
DsServiceID = "ds" // Ds.
|
||||
DynamodbServiceID = "dynamodb" // Dynamodb.
|
||||
Ec2ServiceID = "ec2" // Ec2.
|
||||
Ec2metadataServiceID = "ec2metadata" // Ec2metadata.
|
||||
EcrServiceID = "ecr" // Ecr.
|
||||
EcsServiceID = "ecs" // Ecs.
|
||||
ElasticacheServiceID = "elasticache" // Elasticache.
|
||||
ElasticbeanstalkServiceID = "elasticbeanstalk" // Elasticbeanstalk.
|
||||
ElasticfilesystemServiceID = "elasticfilesystem" // Elasticfilesystem.
|
||||
ElasticloadbalancingServiceID = "elasticloadbalancing" // Elasticloadbalancing.
|
||||
ElasticmapreduceServiceID = "elasticmapreduce" // Elasticmapreduce.
|
||||
ElastictranscoderServiceID = "elastictranscoder" // Elastictranscoder.
|
||||
EmailServiceID = "email" // Email.
|
||||
EntitlementMarketplaceServiceID = "entitlement.marketplace" // EntitlementMarketplace.
|
||||
EsServiceID = "es" // Es.
|
||||
EventsServiceID = "events" // Events.
|
||||
FirehoseServiceID = "firehose" // Firehose.
|
||||
FmsServiceID = "fms" // Fms.
|
||||
GameliftServiceID = "gamelift" // Gamelift.
|
||||
GlacierServiceID = "glacier" // Glacier.
|
||||
GlueServiceID = "glue" // Glue.
|
||||
GreengrassServiceID = "greengrass" // Greengrass.
|
||||
GuarddutyServiceID = "guardduty" // Guardduty.
|
||||
HealthServiceID = "health" // Health.
|
||||
IamServiceID = "iam" // Iam.
|
||||
ImportexportServiceID = "importexport" // Importexport.
|
||||
InspectorServiceID = "inspector" // Inspector.
|
||||
IotServiceID = "iot" // Iot.
|
||||
IotanalyticsServiceID = "iotanalytics" // Iotanalytics.
|
||||
KinesisServiceID = "kinesis" // Kinesis.
|
||||
KinesisanalyticsServiceID = "kinesisanalytics" // Kinesisanalytics.
|
||||
KinesisvideoServiceID = "kinesisvideo" // Kinesisvideo.
|
||||
KmsServiceID = "kms" // Kms.
|
||||
LambdaServiceID = "lambda" // Lambda.
|
||||
LightsailServiceID = "lightsail" // Lightsail.
|
||||
LogsServiceID = "logs" // Logs.
|
||||
MachinelearningServiceID = "machinelearning" // Machinelearning.
|
||||
MarketplacecommerceanalyticsServiceID = "marketplacecommerceanalytics" // Marketplacecommerceanalytics.
|
||||
MediaconvertServiceID = "mediaconvert" // Mediaconvert.
|
||||
MedialiveServiceID = "medialive" // Medialive.
|
||||
MediapackageServiceID = "mediapackage" // Mediapackage.
|
||||
MediastoreServiceID = "mediastore" // Mediastore.
|
||||
MeteringMarketplaceServiceID = "metering.marketplace" // MeteringMarketplace.
|
||||
MghServiceID = "mgh" // Mgh.
|
||||
MobileanalyticsServiceID = "mobileanalytics" // Mobileanalytics.
|
||||
ModelsLexServiceID = "models.lex" // ModelsLex.
|
||||
MonitoringServiceID = "monitoring" // Monitoring.
|
||||
MturkRequesterServiceID = "mturk-requester" // MturkRequester.
|
||||
NeptuneServiceID = "neptune" // Neptune.
|
||||
OpsworksServiceID = "opsworks" // Opsworks.
|
||||
OpsworksCmServiceID = "opsworks-cm" // OpsworksCm.
|
||||
OrganizationsServiceID = "organizations" // Organizations.
|
||||
PinpointServiceID = "pinpoint" // Pinpoint.
|
||||
PollyServiceID = "polly" // Polly.
|
||||
RdsServiceID = "rds" // Rds.
|
||||
RedshiftServiceID = "redshift" // Redshift.
|
||||
RekognitionServiceID = "rekognition" // Rekognition.
|
||||
ResourceGroupsServiceID = "resource-groups" // ResourceGroups.
|
||||
Route53ServiceID = "route53" // Route53.
|
||||
Route53domainsServiceID = "route53domains" // Route53domains.
|
||||
RuntimeLexServiceID = "runtime.lex" // RuntimeLex.
|
||||
RuntimeSagemakerServiceID = "runtime.sagemaker" // RuntimeSagemaker.
|
||||
S3ServiceID = "s3" // S3.
|
||||
S3ControlServiceID = "s3-control" // S3Control.
|
||||
SagemakerServiceID = "api.sagemaker" // Sagemaker.
|
||||
SdbServiceID = "sdb" // Sdb.
|
||||
SecretsmanagerServiceID = "secretsmanager" // Secretsmanager.
|
||||
ServerlessrepoServiceID = "serverlessrepo" // Serverlessrepo.
|
||||
ServicecatalogServiceID = "servicecatalog" // Servicecatalog.
|
||||
ServicediscoveryServiceID = "servicediscovery" // Servicediscovery.
|
||||
ShieldServiceID = "shield" // Shield.
|
||||
SmsServiceID = "sms" // Sms.
|
||||
SnowballServiceID = "snowball" // Snowball.
|
||||
SnsServiceID = "sns" // Sns.
|
||||
SqsServiceID = "sqs" // Sqs.
|
||||
SsmServiceID = "ssm" // Ssm.
|
||||
StatesServiceID = "states" // States.
|
||||
StoragegatewayServiceID = "storagegateway" // Storagegateway.
|
||||
StreamsDynamodbServiceID = "streams.dynamodb" // StreamsDynamodb.
|
||||
StsServiceID = "sts" // Sts.
|
||||
SupportServiceID = "support" // Support.
|
||||
SwfServiceID = "swf" // Swf.
|
||||
TaggingServiceID = "tagging" // Tagging.
|
||||
TransferServiceID = "transfer" // Transfer.
|
||||
TranslateServiceID = "translate" // Translate.
|
||||
WafServiceID = "waf" // Waf.
|
||||
WafRegionalServiceID = "waf-regional" // WafRegional.
|
||||
WorkdocsServiceID = "workdocs" // Workdocs.
|
||||
WorkmailServiceID = "workmail" // Workmail.
|
||||
WorkspacesServiceID = "workspaces" // Workspaces.
|
||||
XrayServiceID = "xray" // Xray.
|
||||
) |
||||
@ -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{}) |
||||
@ -0,0 +1,11 @@ |
||||
package ini |
||||
|
||||
var commaRunes = []rune(",") |
||||
|
||||
func isComma(b rune) bool { |
||||
return b == ',' |
||||
} |
||||
|
||||
func newCommaToken() Token { |
||||
return newToken(TokenComma, commaRunes, NoneType) |
||||
} |
||||
@ -0,0 +1,35 @@ |
||||
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 |
||||
} |
||||
|
||||
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 |
||||
} |
||||
@ -0,0 +1,29 @@ |
||||
// 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' -> epsilon | value
|
||||
package ini |
||||
@ -0,0 +1,4 @@ |
||||
package ini |
||||
|
||||
// emptyToken is used to satisfy the Token interface
|
||||
var emptyToken = newToken(TokenNone, []rune{}, NoneType) |
||||
@ -0,0 +1,24 @@ |
||||
package ini |
||||
|
||||
// newExpression will return an expression AST.
|
||||
// Expr represents an expression
|
||||
//
|
||||
// grammar:
|
||||
// expr -> string | number
|
||||
func newExpression(tok Token) AST { |
||||
return newASTWithRootToken(ASTKindExpr, tok) |
||||
} |
||||
|
||||
func newEqualExpr(left AST, tok Token) AST { |
||||
return newASTWithRootToken(ASTKindEqualExpr, tok, left) |
||||
} |
||||
|
||||
// EqualExprKey will return a LHS value in the equal expr
|
||||
func EqualExprKey(ast AST) string { |
||||
children := ast.GetChildren() |
||||
if len(children) == 0 || ast.Kind != ASTKindEqualExpr { |
||||
return "" |
||||
} |
||||
|
||||
return string(children[0].Root.Raw()) |
||||
} |
||||
@ -0,0 +1,17 @@ |
||||
// +build gofuzz
|
||||
|
||||
package ini |
||||
|
||||
import ( |
||||
"bytes" |
||||
) |
||||
|
||||
func Fuzz(data []byte) int { |
||||
b := bytes.NewReader(data) |
||||
|
||||
if _, err := Parse(b); err != nil { |
||||
return 0 |
||||
} |
||||
|
||||
return 1 |
||||
} |
||||
@ -0,0 +1,51 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"io" |
||||
"os" |
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr" |
||||
) |
||||
|
||||
// OpenFile takes a path to a given file, and will open and parse
|
||||
// that file.
|
||||
func OpenFile(path string) (Sections, error) { |
||||
f, err := os.Open(path) |
||||
if err != nil { |
||||
return Sections{}, awserr.New(ErrCodeUnableToReadFile, "unable to open file", err) |
||||
} |
||||
defer f.Close() |
||||
|
||||
return Parse(f) |
||||
} |
||||
|
||||
// Parse will parse the given file using the shared config
|
||||
// visitor.
|
||||
func Parse(f io.Reader) (Sections, error) { |
||||
tree, err := ParseAST(f) |
||||
if err != nil { |
||||
return Sections{}, err |
||||
} |
||||
|
||||
v := NewDefaultVisitor() |
||||
if err = Walk(tree, v); err != nil { |
||||
return Sections{}, err |
||||
} |
||||
|
||||
return v.Sections, nil |
||||
} |
||||
|
||||
// ParseBytes will parse the given bytes and return the parsed sections.
|
||||
func ParseBytes(b []byte) (Sections, error) { |
||||
tree, err := ParseASTBytes(b) |
||||
if err != nil { |
||||
return Sections{}, err |
||||
} |
||||
|
||||
v := NewDefaultVisitor() |
||||
if err = Walk(tree, v); err != nil { |
||||
return Sections{}, err |
||||
} |
||||
|
||||
return v.Sections, nil |
||||
} |
||||
@ -0,0 +1,165 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"bytes" |
||||
"io" |
||||
"io/ioutil" |
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr" |
||||
) |
||||
|
||||
const ( |
||||
// ErrCodeUnableToReadFile is used when a file is failed to be
|
||||
// opened or read from.
|
||||
ErrCodeUnableToReadFile = "FailedRead" |
||||
) |
||||
|
||||
// TokenType represents the various different tokens types
|
||||
type TokenType int |
||||
|
||||
func (t TokenType) String() string { |
||||
switch t { |
||||
case TokenNone: |
||||
return "none" |
||||
case TokenLit: |
||||
return "literal" |
||||
case TokenSep: |
||||
return "sep" |
||||
case TokenOp: |
||||
return "op" |
||||
case TokenWS: |
||||
return "ws" |
||||
case TokenNL: |
||||
return "newline" |
||||
case TokenComment: |
||||
return "comment" |
||||
case TokenComma: |
||||
return "comma" |
||||
default: |
||||
return "" |
||||
} |
||||
} |
||||
|
||||
// TokenType enums
|
||||
const ( |
||||
TokenNone = TokenType(iota) |
||||
TokenLit |
||||
TokenSep |
||||
TokenComma |
||||
TokenOp |
||||
TokenWS |
||||
TokenNL |
||||
TokenComment |
||||
) |
||||
|
||||
type iniLexer struct{} |
||||
|
||||
// Tokenize will return a list of tokens during lexical analysis of the
|
||||
// io.Reader.
|
||||
func (l *iniLexer) Tokenize(r io.Reader) ([]Token, error) { |
||||
b, err := ioutil.ReadAll(r) |
||||
if err != nil { |
||||
return nil, awserr.New(ErrCodeUnableToReadFile, "unable to read file", err) |
||||
} |
||||
|
||||
return l.tokenize(b) |
||||
} |
||||
|
||||
func (l *iniLexer) tokenize(b []byte) ([]Token, error) { |
||||
runes := bytes.Runes(b) |
||||
var err error |
||||
n := 0 |
||||
tokenAmount := countTokens(runes) |
||||
tokens := make([]Token, tokenAmount) |
||||
count := 0 |
||||
|
||||
for len(runes) > 0 && count < tokenAmount { |
||||
switch { |
||||
case isWhitespace(runes[0]): |
||||
tokens[count], n, err = newWSToken(runes) |
||||
case isComma(runes[0]): |
||||
tokens[count], n = newCommaToken(), 1 |
||||
case isComment(runes): |
||||
tokens[count], n, err = newCommentToken(runes) |
||||
case isNewline(runes): |
||||
tokens[count], n, err = newNewlineToken(runes) |
||||
case isSep(runes): |
||||
tokens[count], n, err = newSepToken(runes) |
||||
case isOp(runes): |
||||
tokens[count], n, err = newOpToken(runes) |
||||
default: |
||||
tokens[count], n, err = newLitToken(runes) |
||||
} |
||||
|
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
count++ |
||||
|
||||
runes = runes[n:] |
||||
} |
||||
|
||||
return tokens[:count], nil |
||||
} |
||||
|
||||
func countTokens(runes []rune) int { |
||||
count, n := 0, 0 |
||||
var err error |
||||
|
||||
for len(runes) > 0 { |
||||
switch { |
||||
case isWhitespace(runes[0]): |
||||
_, n, err = newWSToken(runes) |
||||
case isComma(runes[0]): |
||||
_, n = newCommaToken(), 1 |
||||
case isComment(runes): |
||||
_, n, err = newCommentToken(runes) |
||||
case isNewline(runes): |
||||
_, n, err = newNewlineToken(runes) |
||||
case isSep(runes): |
||||
_, n, err = newSepToken(runes) |
||||
case isOp(runes): |
||||
_, n, err = newOpToken(runes) |
||||
default: |
||||
_, n, err = newLitToken(runes) |
||||
} |
||||
|
||||
if err != nil { |
||||
return 0 |
||||
} |
||||
|
||||
count++ |
||||
runes = runes[n:] |
||||
} |
||||
|
||||
return count + 1 |
||||
} |
||||
|
||||
// Token indicates a metadata about a given value.
|
||||
type Token struct { |
||||
t TokenType |
||||
ValueType ValueType |
||||
base int |
||||
raw []rune |
||||
} |
||||
|
||||
var emptyValue = Value{} |
||||
|
||||
func newToken(t TokenType, raw []rune, v ValueType) Token { |
||||
return Token{ |
||||
t: t, |
||||
raw: raw, |
||||
ValueType: v, |
||||
} |
||||
} |
||||
|
||||
// Raw return the raw runes that were consumed
|
||||
func (tok Token) Raw() []rune { |
||||
return tok.raw |
||||
} |
||||
|
||||
// Type returns the token type
|
||||
func (tok Token) Type() TokenType { |
||||
return tok.t |
||||
} |
||||
@ -0,0 +1,347 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"fmt" |
||||
"io" |
||||
) |
||||
|
||||
// State enums for the parse table
|
||||
const ( |
||||
InvalidState = iota |
||||
// stmt -> value stmt'
|
||||
StatementState |
||||
// stmt' -> MarkComplete | op stmt
|
||||
StatementPrimeState |
||||
// value -> number | string | boolean | quoted_string
|
||||
ValueState |
||||
// section -> [ section'
|
||||
OpenScopeState |
||||
// section' -> value section_close
|
||||
SectionState |
||||
// section_close -> ]
|
||||
CloseScopeState |
||||
// SkipState will skip (NL WS)+
|
||||
SkipState |
||||
// SkipTokenState will skip any token and push the previous
|
||||
// state onto the stack.
|
||||
SkipTokenState |
||||
// comment -> # comment' | ; comment'
|
||||
// comment' -> MarkComplete | value
|
||||
CommentState |
||||
// MarkComplete state will complete statements and move that
|
||||
// to the completed AST list
|
||||
MarkCompleteState |
||||
// TerminalState signifies that the tokens have been fully parsed
|
||||
TerminalState |
||||
) |
||||
|
||||
// parseTable is a state machine to dictate the grammar above.
|
||||
var parseTable = map[ASTKind]map[TokenType]int{ |
||||
ASTKindStart: map[TokenType]int{ |
||||
TokenLit: StatementState, |
||||
TokenSep: OpenScopeState, |
||||
TokenWS: SkipTokenState, |
||||
TokenNL: SkipTokenState, |
||||
TokenComment: CommentState, |
||||
TokenNone: TerminalState, |
||||
}, |
||||
ASTKindCommentStatement: map[TokenType]int{ |
||||
TokenLit: StatementState, |
||||
TokenSep: OpenScopeState, |
||||
TokenWS: SkipTokenState, |
||||
TokenNL: SkipTokenState, |
||||
TokenComment: CommentState, |
||||
TokenNone: MarkCompleteState, |
||||
}, |
||||
ASTKindExpr: map[TokenType]int{ |
||||
TokenOp: StatementPrimeState, |
||||
TokenLit: ValueState, |
||||
TokenSep: OpenScopeState, |
||||
TokenWS: ValueState, |
||||
TokenNL: SkipState, |
||||
TokenComment: CommentState, |
||||
TokenNone: MarkCompleteState, |
||||
}, |
||||
ASTKindEqualExpr: map[TokenType]int{ |
||||
TokenLit: ValueState, |
||||
TokenWS: SkipTokenState, |
||||
TokenNL: SkipState, |
||||
}, |
||||
ASTKindStatement: map[TokenType]int{ |
||||
TokenLit: SectionState, |
||||
TokenSep: CloseScopeState, |
||||
TokenWS: SkipTokenState, |
||||
TokenNL: SkipTokenState, |
||||
TokenComment: CommentState, |
||||
TokenNone: MarkCompleteState, |
||||
}, |
||||
ASTKindExprStatement: map[TokenType]int{ |
||||
TokenLit: ValueState, |
||||
TokenSep: OpenScopeState, |
||||
TokenOp: ValueState, |
||||
TokenWS: ValueState, |
||||
TokenNL: MarkCompleteState, |
||||
TokenComment: CommentState, |
||||
TokenNone: TerminalState, |
||||
TokenComma: SkipState, |
||||
}, |
||||
ASTKindSectionStatement: map[TokenType]int{ |
||||
TokenLit: SectionState, |
||||
TokenOp: SectionState, |
||||
TokenSep: CloseScopeState, |
||||
TokenWS: SectionState, |
||||
TokenNL: SkipTokenState, |
||||
}, |
||||
ASTKindCompletedSectionStatement: map[TokenType]int{ |
||||
TokenWS: SkipTokenState, |
||||
TokenNL: SkipTokenState, |
||||
TokenLit: StatementState, |
||||
TokenSep: OpenScopeState, |
||||
TokenComment: CommentState, |
||||
TokenNone: MarkCompleteState, |
||||
}, |
||||
ASTKindSkipStatement: map[TokenType]int{ |
||||
TokenLit: StatementState, |
||||
TokenSep: OpenScopeState, |
||||
TokenWS: SkipTokenState, |
||||
TokenNL: SkipTokenState, |
||||
TokenComment: CommentState, |
||||
TokenNone: TerminalState, |
||||
}, |
||||
} |
||||
|
||||
// ParseAST will parse input from an io.Reader using
|
||||
// an LL(1) parser.
|
||||
func ParseAST(r io.Reader) ([]AST, error) { |
||||
lexer := iniLexer{} |
||||
tokens, err := lexer.Tokenize(r) |
||||
if err != nil { |
||||
return []AST{}, err |
||||
} |
||||
|
||||
return parse(tokens) |
||||
} |
||||
|
||||
// ParseASTBytes will parse input from a byte slice using
|
||||
// an LL(1) parser.
|
||||
func ParseASTBytes(b []byte) ([]AST, error) { |
||||
lexer := iniLexer{} |
||||
tokens, err := lexer.tokenize(b) |
||||
if err != nil { |
||||
return []AST{}, err |
||||
} |
||||
|
||||
return parse(tokens) |
||||
} |
||||
|
||||
func parse(tokens []Token) ([]AST, error) { |
||||
start := Start |
||||
stack := newParseStack(3, len(tokens)) |
||||
|
||||
stack.Push(start) |
||||
s := newSkipper() |
||||
|
||||
loop: |
||||
for stack.Len() > 0 { |
||||
k := stack.Pop() |
||||
|
||||
var tok Token |
||||
if len(tokens) == 0 { |
||||
// this occurs when all the tokens have been processed
|
||||
// but reduction of what's left on the stack needs to
|
||||
// occur.
|
||||
tok = emptyToken |
||||
} else { |
||||
tok = tokens[0] |
||||
} |
||||
|
||||
step := parseTable[k.Kind][tok.Type()] |
||||
if s.ShouldSkip(tok) { |
||||
// being in a skip state with no tokens will break out of
|
||||
// the parse loop since there is nothing left to process.
|
||||
if len(tokens) == 0 { |
||||
break loop |
||||
} |
||||
|
||||
step = SkipTokenState |
||||
} |
||||
|
||||
switch step { |
||||
case TerminalState: |
||||
// Finished parsing. Push what should be the last
|
||||
// statement to the stack. If there is anything left
|
||||
// on the stack, an error in parsing has occurred.
|
||||
if k.Kind != ASTKindStart { |
||||
stack.MarkComplete(k) |
||||
} |
||||
break loop |
||||
case SkipTokenState: |
||||
// When skipping a token, the previous state was popped off the stack.
|
||||
// To maintain the correct state, the previous state will be pushed
|
||||
// onto the stack.
|
||||
stack.Push(k) |
||||
case StatementState: |
||||
if k.Kind != ASTKindStart { |
||||
stack.MarkComplete(k) |
||||
} |
||||
expr := newExpression(tok) |
||||
stack.Push(expr) |
||||
case StatementPrimeState: |
||||
if tok.Type() != TokenOp { |
||||
stack.MarkComplete(k) |
||||
continue |
||||
} |
||||
|
||||
if k.Kind != ASTKindExpr { |
||||
return nil, NewParseError( |
||||
fmt.Sprintf("invalid expression: expected Expr type, but found %T type", k), |
||||
) |
||||
} |
||||
|
||||
k = trimSpaces(k) |
||||
expr := newEqualExpr(k, tok) |
||||
stack.Push(expr) |
||||
case ValueState: |
||||
// ValueState requires the previous state to either be an equal expression
|
||||
// or an expression statement.
|
||||
//
|
||||
// This grammar occurs when the RHS is a number, word, or quoted string.
|
||||
// equal_expr -> lit op equal_expr'
|
||||
// equal_expr' -> number | string | quoted_string
|
||||
// quoted_string -> " quoted_string'
|
||||
// quoted_string' -> string quoted_string_end
|
||||
// quoted_string_end -> "
|
||||
//
|
||||
// otherwise
|
||||
// expr_stmt -> equal_expr (expr_stmt')*
|
||||
// expr_stmt' -> ws S | op S | MarkComplete
|
||||
// S -> equal_expr' expr_stmt'
|
||||
switch k.Kind { |
||||
case ASTKindEqualExpr: |
||||
// assiging a value to some key
|
||||
k.AppendChild(newExpression(tok)) |
||||
stack.Push(newExprStatement(k)) |
||||
case ASTKindExpr: |
||||
k.Root.raw = append(k.Root.raw, tok.Raw()...) |
||||
stack.Push(k) |
||||
case ASTKindExprStatement: |
||||
root := k.GetRoot() |
||||
children := root.GetChildren() |
||||
if len(children) == 0 { |
||||
return nil, NewParseError( |
||||
fmt.Sprintf("invalid expression: AST contains no children %s", k.Kind), |
||||
) |
||||
} |
||||
|
||||
rhs := children[len(children)-1] |
||||
|
||||
if rhs.Root.ValueType != QuotedStringType { |
||||
rhs.Root.ValueType = StringType |
||||
rhs.Root.raw = append(rhs.Root.raw, tok.Raw()...) |
||||
|
||||
} |
||||
|
||||
children[len(children)-1] = rhs |
||||
k.SetChildren(children) |
||||
|
||||
stack.Push(k) |
||||
} |
||||
case OpenScopeState: |
||||
if !runeCompare(tok.Raw(), openBrace) { |
||||
return nil, NewParseError("expected '['") |
||||
} |
||||
|
||||
stmt := newStatement() |
||||
stack.Push(stmt) |
||||
case CloseScopeState: |
||||
if !runeCompare(tok.Raw(), closeBrace) { |
||||
return nil, NewParseError("expected ']'") |
||||
} |
||||
|
||||
k = trimSpaces(k) |
||||
stack.Push(newCompletedSectionStatement(k)) |
||||
case SectionState: |
||||
var stmt AST |
||||
|
||||
switch k.Kind { |
||||
case ASTKindStatement: |
||||
// If there are multiple literals inside of a scope declaration,
|
||||
// then the current token's raw value will be appended to the Name.
|
||||
//
|
||||
// This handles cases like [ profile default ]
|
||||
//
|
||||
// k will represent a SectionStatement with the children representing
|
||||
// the label of the section
|
||||
stmt = newSectionStatement(tok) |
||||
case ASTKindSectionStatement: |
||||
k.Root.raw = append(k.Root.raw, tok.Raw()...) |
||||
stmt = k |
||||
default: |
||||
return nil, NewParseError( |
||||
fmt.Sprintf("invalid statement: expected statement: %v", k.Kind), |
||||
) |
||||
} |
||||
|
||||
stack.Push(stmt) |
||||
case MarkCompleteState: |
||||
if k.Kind != ASTKindStart { |
||||
stack.MarkComplete(k) |
||||
} |
||||
|
||||
if stack.Len() == 0 { |
||||
stack.Push(start) |
||||
} |
||||
case SkipState: |
||||
stack.Push(newSkipStatement(k)) |
||||
s.Skip() |
||||
case CommentState: |
||||
if k.Kind == ASTKindStart { |
||||
stack.Push(k) |
||||
} else { |
||||
stack.MarkComplete(k) |
||||
} |
||||
|
||||
stmt := newCommentStatement(tok) |
||||
stack.Push(stmt) |
||||
default: |
||||
return nil, NewParseError(fmt.Sprintf("invalid state with ASTKind %v and TokenType %v", k, tok)) |
||||
} |
||||
|
||||
if len(tokens) > 0 { |
||||
tokens = tokens[1:] |
||||
} |
||||
} |
||||
|
||||
// this occurs when a statement has not been completed
|
||||
if stack.top > 1 { |
||||
return nil, NewParseError(fmt.Sprintf("incomplete expression: %v", stack.container)) |
||||
} |
||||
|
||||
// returns a sublist which excludes the start symbol
|
||||
return stack.List(), nil |
||||
} |
||||
|
||||
// trimSpaces will trim spaces on the left and right hand side of
|
||||
// the literal.
|
||||
func trimSpaces(k AST) AST { |
||||
// trim left hand side of spaces
|
||||
for i := 0; i < len(k.Root.raw); i++ { |
||||
if !isWhitespace(k.Root.raw[i]) { |
||||
break |
||||
} |
||||
|
||||
k.Root.raw = k.Root.raw[1:] |
||||
i-- |
||||
} |
||||
|
||||
// trim right hand side of spaces
|
||||
for i := len(k.Root.raw) - 1; i >= 0; i-- { |
||||
if !isWhitespace(k.Root.raw[i]) { |
||||
break |
||||
} |
||||
|
||||
k.Root.raw = k.Root.raw[:len(k.Root.raw)-1] |
||||
} |
||||
|
||||
return k |
||||
} |
||||
@ -0,0 +1,324 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"fmt" |
||||
"strconv" |
||||
"strings" |
||||
) |
||||
|
||||
var ( |
||||
runesTrue = []rune("true") |
||||
runesFalse = []rune("false") |
||||
) |
||||
|
||||
var literalValues = [][]rune{ |
||||
runesTrue, |
||||
runesFalse, |
||||
} |
||||
|
||||
func isBoolValue(b []rune) bool { |
||||
for _, lv := range literalValues { |
||||
if isLitValue(lv, b) { |
||||
return true |
||||
} |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func isLitValue(want, have []rune) bool { |
||||
if len(have) < len(want) { |
||||
return false |
||||
} |
||||
|
||||
for i := 0; i < len(want); i++ { |
||||
if want[i] != have[i] { |
||||
return false |
||||
} |
||||
} |
||||
|
||||
return true |
||||
} |
||||
|
||||
// isNumberValue will return whether not the leading characters in
|
||||
// a byte slice is a number. A number is delimited by whitespace or
|
||||
// the newline token.
|
||||
//
|
||||
// A number is defined to be in a binary, octal, decimal (int | float), hex format,
|
||||
// or in scientific notation.
|
||||
func isNumberValue(b []rune) bool { |
||||
negativeIndex := 0 |
||||
helper := numberHelper{} |
||||
needDigit := false |
||||
|
||||
for i := 0; i < len(b); i++ { |
||||
negativeIndex++ |
||||
|
||||
switch b[i] { |
||||
case '-': |
||||
if helper.IsNegative() || negativeIndex != 1 { |
||||
return false |
||||
} |
||||
helper.Determine(b[i]) |
||||
needDigit = true |
||||
continue |
||||
case 'e', 'E': |
||||
if err := helper.Determine(b[i]); err != nil { |
||||
return false |
||||
} |
||||
negativeIndex = 0 |
||||
needDigit = true |
||||
continue |
||||
case 'b': |
||||
if helper.numberFormat == hex { |
||||
break |
||||
} |
||||
fallthrough |
||||
case 'o', 'x': |
||||
needDigit = true |
||||
if i == 0 { |
||||
return false |
||||
} |
||||
|
||||
fallthrough |
||||
case '.': |
||||
if err := helper.Determine(b[i]); err != nil { |
||||
return false |
||||
} |
||||
needDigit = true |
||||
continue |
||||
} |
||||
|
||||
if i > 0 && (isNewline(b[i:]) || isWhitespace(b[i])) { |
||||
return !needDigit |
||||
} |
||||
|
||||
if !helper.CorrectByte(b[i]) { |
||||
return false |
||||
} |
||||
needDigit = false |
||||
} |
||||
|
||||
return !needDigit |
||||
} |
||||
|
||||
func isValid(b []rune) (bool, int, error) { |
||||
if len(b) == 0 { |
||||
// TODO: should probably return an error
|
||||
return false, 0, nil |
||||
} |
||||
|
||||
return isValidRune(b[0]), 1, nil |
||||
} |
||||
|
||||
func isValidRune(r rune) bool { |
||||
return r != ':' && r != '=' && r != '[' && r != ']' && r != ' ' && r != '\n' |
||||
} |
||||
|
||||
// ValueType is an enum that will signify what type
|
||||
// the Value is
|
||||
type ValueType int |
||||
|
||||
func (v ValueType) String() string { |
||||
switch v { |
||||
case NoneType: |
||||
return "NONE" |
||||
case DecimalType: |
||||
return "FLOAT" |
||||
case IntegerType: |
||||
return "INT" |
||||
case StringType: |
||||
return "STRING" |
||||
case BoolType: |
||||
return "BOOL" |
||||
} |
||||
|
||||
return "" |
||||
} |
||||
|
||||
// ValueType enums
|
||||
const ( |
||||
NoneType = ValueType(iota) |
||||
DecimalType |
||||
IntegerType |
||||
StringType |
||||
QuotedStringType |
||||
BoolType |
||||
) |
||||
|
||||
// Value is a union container
|
||||
type Value struct { |
||||
Type ValueType |
||||
raw []rune |
||||
|
||||
integer int64 |
||||
decimal float64 |
||||
boolean bool |
||||
str string |
||||
} |
||||
|
||||
func newValue(t ValueType, base int, raw []rune) (Value, error) { |
||||
v := Value{ |
||||
Type: t, |
||||
raw: raw, |
||||
} |
||||
var err error |
||||
|
||||
switch t { |
||||
case DecimalType: |
||||
v.decimal, err = strconv.ParseFloat(string(raw), 64) |
||||
case IntegerType: |
||||
if base != 10 { |
||||
raw = raw[2:] |
||||
} |
||||
|
||||
v.integer, err = strconv.ParseInt(string(raw), base, 64) |
||||
case StringType: |
||||
v.str = string(raw) |
||||
case QuotedStringType: |
||||
v.str = string(raw[1 : len(raw)-1]) |
||||
case BoolType: |
||||
v.boolean = runeCompare(v.raw, runesTrue) |
||||
} |
||||
|
||||
// issue 2253
|
||||
//
|
||||
// if the value trying to be parsed is too large, then we will use
|
||||
// the 'StringType' and raw value instead.
|
||||
if nerr, ok := err.(*strconv.NumError); ok && nerr.Err == strconv.ErrRange { |
||||
v.Type = StringType |
||||
v.str = string(raw) |
||||
err = nil |
||||
} |
||||
|
||||
return v, err |
||||
} |
||||
|
||||
// Append will append values and change the type to a string
|
||||
// type.
|
||||
func (v *Value) Append(tok Token) { |
||||
r := tok.Raw() |
||||
if v.Type != QuotedStringType { |
||||
v.Type = StringType |
||||
r = tok.raw[1 : len(tok.raw)-1] |
||||
} |
||||
if tok.Type() != TokenLit { |
||||
v.raw = append(v.raw, tok.Raw()...) |
||||
} else { |
||||
v.raw = append(v.raw, r...) |
||||
} |
||||
} |
||||
|
||||
func (v Value) String() string { |
||||
switch v.Type { |
||||
case DecimalType: |
||||
return fmt.Sprintf("decimal: %f", v.decimal) |
||||
case IntegerType: |
||||
return fmt.Sprintf("integer: %d", v.integer) |
||||
case StringType: |
||||
return fmt.Sprintf("string: %s", string(v.raw)) |
||||
case QuotedStringType: |
||||
return fmt.Sprintf("quoted string: %s", string(v.raw)) |
||||
case BoolType: |
||||
return fmt.Sprintf("bool: %t", v.boolean) |
||||
default: |
||||
return "union not set" |
||||
} |
||||
} |
||||
|
||||
func newLitToken(b []rune) (Token, int, error) { |
||||
n := 0 |
||||
var err error |
||||
|
||||
token := Token{} |
||||
if b[0] == '"' { |
||||
n, err = getStringValue(b) |
||||
if err != nil { |
||||
return token, n, err |
||||
} |
||||
|
||||
token = newToken(TokenLit, b[:n], QuotedStringType) |
||||
} else if isNumberValue(b) { |
||||
var base int |
||||
base, n, err = getNumericalValue(b) |
||||
if err != nil { |
||||
return token, 0, err |
||||
} |
||||
|
||||
value := b[:n] |
||||
vType := IntegerType |
||||
if contains(value, '.') || hasExponent(value) { |
||||
vType = DecimalType |
||||
} |
||||
token = newToken(TokenLit, value, vType) |
||||
token.base = base |
||||
} else if isBoolValue(b) { |
||||
n, err = getBoolValue(b) |
||||
|
||||
token = newToken(TokenLit, b[:n], BoolType) |
||||
} else { |
||||
n, err = getValue(b) |
||||
token = newToken(TokenLit, b[:n], StringType) |
||||
} |
||||
|
||||
return token, n, err |
||||
} |
||||
|
||||
// IntValue returns an integer value
|
||||
func (v Value) IntValue() int64 { |
||||
return v.integer |
||||
} |
||||
|
||||
// FloatValue returns a float value
|
||||
func (v Value) FloatValue() float64 { |
||||
return v.decimal |
||||
} |
||||
|
||||
// BoolValue returns a bool value
|
||||
func (v Value) BoolValue() bool { |
||||
return v.boolean |
||||
} |
||||
|
||||
func isTrimmable(r rune) bool { |
||||
switch r { |
||||
case '\n', ' ': |
||||
return true |
||||
} |
||||
return false |
||||
} |
||||
|
||||
// StringValue returns the string value
|
||||
func (v Value) StringValue() string { |
||||
switch v.Type { |
||||
case StringType: |
||||
return strings.TrimFunc(string(v.raw), isTrimmable) |
||||
case QuotedStringType: |
||||
// preserve all characters in the quotes
|
||||
return string(removeEscapedCharacters(v.raw[1 : len(v.raw)-1])) |
||||
default: |
||||
return strings.TrimFunc(string(v.raw), isTrimmable) |
||||
} |
||||
} |
||||
|
||||
func contains(runes []rune, c rune) bool { |
||||
for i := 0; i < len(runes); i++ { |
||||
if runes[i] == c { |
||||
return true |
||||
} |
||||
} |
||||
|
||||
return false |
||||
} |
||||
|
||||
func runeCompare(v1 []rune, v2 []rune) bool { |
||||
if len(v1) != len(v2) { |
||||
return false |
||||
} |
||||
|
||||
for i := 0; i < len(v1); i++ { |
||||
if v1[i] != v2[i] { |
||||
return false |
||||
} |
||||
} |
||||
|
||||
return true |
||||
} |
||||
@ -0,0 +1,30 @@ |
||||
package ini |
||||
|
||||
func isNewline(b []rune) bool { |
||||
if len(b) == 0 { |
||||
return false |
||||
} |
||||
|
||||
if b[0] == '\n' { |
||||
return true |
||||
} |
||||
|
||||
if len(b) < 2 { |
||||
return false |
||||
} |
||||
|
||||
return b[0] == '\r' && b[1] == '\n' |
||||
} |
||||
|
||||
func newNewlineToken(b []rune) (Token, int, error) { |
||||
i := 1 |
||||
if b[0] == '\r' && isNewline(b[1:]) { |
||||
i++ |
||||
} |
||||
|
||||
if !isNewline([]rune(b[:i])) { |
||||
return emptyToken, 0, NewParseError("invalid new line token") |
||||
} |
||||
|
||||
return newToken(TokenNL, b[:i], NoneType), i, nil |
||||
} |
||||
@ -0,0 +1,152 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"strconv" |
||||
) |
||||
|
||||
const ( |
||||
none = numberFormat(iota) |
||||
binary |
||||
octal |
||||
decimal |
||||
hex |
||||
exponent |
||||
) |
||||
|
||||
type numberFormat int |
||||
|
||||
// numberHelper is used to dictate what format a number is in
|
||||
// and what to do for negative values. Since -1e-4 is a valid
|
||||
// number, we cannot just simply check for duplicate negatives.
|
||||
type numberHelper struct { |
||||
numberFormat numberFormat |
||||
|
||||
negative bool |
||||
negativeExponent bool |
||||
} |
||||
|
||||
func (b numberHelper) Exists() bool { |
||||
return b.numberFormat != none |
||||
} |
||||
|
||||
func (b numberHelper) IsNegative() bool { |
||||
return b.negative || b.negativeExponent |
||||
} |
||||
|
||||
func (b *numberHelper) Determine(c rune) error { |
||||
if b.Exists() { |
||||
return NewParseError(fmt.Sprintf("multiple number formats: 0%v", string(c))) |
||||
} |
||||
|
||||
switch c { |
||||
case 'b': |
||||
b.numberFormat = binary |
||||
case 'o': |
||||
b.numberFormat = octal |
||||
case 'x': |
||||
b.numberFormat = hex |
||||
case 'e', 'E': |
||||
b.numberFormat = exponent |
||||
case '-': |
||||
if b.numberFormat != exponent { |
||||
b.negative = true |
||||
} else { |
||||
b.negativeExponent = true |
||||
} |
||||
case '.': |
||||
b.numberFormat = decimal |
||||
default: |
||||
return NewParseError(fmt.Sprintf("invalid number character: %v", string(c))) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (b numberHelper) CorrectByte(c rune) bool { |
||||
switch { |
||||
case b.numberFormat == binary: |
||||
if !isBinaryByte(c) { |
||||
return false |
||||
} |
||||
case b.numberFormat == octal: |
||||
if !isOctalByte(c) { |
||||
return false |
||||
} |
||||
case b.numberFormat == hex: |
||||
if !isHexByte(c) { |
||||
return false |
||||
} |
||||
case b.numberFormat == decimal: |
||||
if !isDigit(c) { |
||||
return false |
||||
} |
||||
case b.numberFormat == exponent: |
||||
if !isDigit(c) { |
||||
return false |
||||
} |
||||
case b.negativeExponent: |
||||
if !isDigit(c) { |
||||
return false |
||||
} |
||||
case b.negative: |
||||
if !isDigit(c) { |
||||
return false |
||||
} |
||||
default: |
||||
if !isDigit(c) { |
||||
return false |
||||
} |
||||
} |
||||
|
||||
return true |
||||
} |
||||
|
||||
func (b numberHelper) Base() int { |
||||
switch b.numberFormat { |
||||
case binary: |
||||
return 2 |
||||
case octal: |
||||
return 8 |
||||
case hex: |
||||
return 16 |
||||
default: |
||||
return 10 |
||||
} |
||||
} |
||||
|
||||
func (b numberHelper) String() string { |
||||
buf := bytes.Buffer{} |
||||
i := 0 |
||||
|
||||
switch b.numberFormat { |
||||
case binary: |
||||
i++ |
||||
buf.WriteString(strconv.Itoa(i) + ": binary format\n") |
||||
case octal: |
||||
i++ |
||||
buf.WriteString(strconv.Itoa(i) + ": octal format\n") |
||||
case hex: |
||||
i++ |
||||
buf.WriteString(strconv.Itoa(i) + ": hex format\n") |
||||
case exponent: |
||||
i++ |
||||
buf.WriteString(strconv.Itoa(i) + ": exponent format\n") |
||||
default: |
||||
i++ |
||||
buf.WriteString(strconv.Itoa(i) + ": integer format\n") |
||||
} |
||||
|
||||
if b.negative { |
||||
i++ |
||||
buf.WriteString(strconv.Itoa(i) + ": negative format\n") |
||||
} |
||||
|
||||
if b.negativeExponent { |
||||
i++ |
||||
buf.WriteString(strconv.Itoa(i) + ": negative exponent format\n") |
||||
} |
||||
|
||||
return buf.String() |
||||
} |
||||
@ -0,0 +1,39 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"fmt" |
||||
) |
||||
|
||||
var ( |
||||
equalOp = []rune("=") |
||||
equalColonOp = []rune(":") |
||||
) |
||||
|
||||
func isOp(b []rune) bool { |
||||
if len(b) == 0 { |
||||
return false |
||||
} |
||||
|
||||
switch b[0] { |
||||
case '=': |
||||
return true |
||||
case ':': |
||||
return true |
||||
default: |
||||
return false |
||||
} |
||||
} |
||||
|
||||
func newOpToken(b []rune) (Token, int, error) { |
||||
tok := Token{} |
||||
|
||||
switch b[0] { |
||||
case '=': |
||||
tok = newToken(TokenOp, equalOp, NoneType) |
||||
case ':': |
||||
tok = newToken(TokenOp, equalColonOp, NoneType) |
||||
default: |
||||
return tok, 0, NewParseError(fmt.Sprintf("unexpected op type, %v", b[0])) |
||||
} |
||||
return tok, 1, nil |
||||
} |
||||
@ -0,0 +1,43 @@ |
||||
package ini |
||||
|
||||
import "fmt" |
||||
|
||||
const ( |
||||
// ErrCodeParseError is returned when a parsing error
|
||||
// has occurred.
|
||||
ErrCodeParseError = "INIParseError" |
||||
) |
||||
|
||||
// ParseError is an error which is returned during any part of
|
||||
// the parsing process.
|
||||
type ParseError struct { |
||||
msg string |
||||
} |
||||
|
||||
// NewParseError will return a new ParseError where message
|
||||
// is the description of the error.
|
||||
func NewParseError(message string) *ParseError { |
||||
return &ParseError{ |
||||
msg: message, |
||||
} |
||||
} |
||||
|
||||
// Code will return the ErrCodeParseError
|
||||
func (err *ParseError) Code() string { |
||||
return ErrCodeParseError |
||||
} |
||||
|
||||
// Message returns the error's message
|
||||
func (err *ParseError) Message() string { |
||||
return err.msg |
||||
} |
||||
|
||||
// OrigError return nothing since there will never be any
|
||||
// original error.
|
||||
func (err *ParseError) OrigError() error { |
||||
return nil |
||||
} |
||||
|
||||
func (err *ParseError) Error() string { |
||||
return fmt.Sprintf("%s: %s", err.Code(), err.Message()) |
||||
} |
||||
@ -0,0 +1,60 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
) |
||||
|
||||
// ParseStack is a stack that contains a container, the stack portion,
|
||||
// and the list which is the list of ASTs that have been successfully
|
||||
// parsed.
|
||||
type ParseStack struct { |
||||
top int |
||||
container []AST |
||||
list []AST |
||||
index int |
||||
} |
||||
|
||||
func newParseStack(sizeContainer, sizeList int) ParseStack { |
||||
return ParseStack{ |
||||
container: make([]AST, sizeContainer), |
||||
list: make([]AST, sizeList), |
||||
} |
||||
} |
||||
|
||||
// Pop will return and truncate the last container element.
|
||||
func (s *ParseStack) Pop() AST { |
||||
s.top-- |
||||
return s.container[s.top] |
||||
} |
||||
|
||||
// Push will add the new AST to the container
|
||||
func (s *ParseStack) Push(ast AST) { |
||||
s.container[s.top] = ast |
||||
s.top++ |
||||
} |
||||
|
||||
// MarkComplete will append the AST to the list of completed statements
|
||||
func (s *ParseStack) MarkComplete(ast AST) { |
||||
s.list[s.index] = ast |
||||
s.index++ |
||||
} |
||||
|
||||
// List will return the completed statements
|
||||
func (s ParseStack) List() []AST { |
||||
return s.list[:s.index] |
||||
} |
||||
|
||||
// Len will return the length of the container
|
||||
func (s *ParseStack) Len() int { |
||||
return s.top |
||||
} |
||||
|
||||
func (s ParseStack) String() string { |
||||
buf := bytes.Buffer{} |
||||
for i, node := range s.list { |
||||
buf.WriteString(fmt.Sprintf("%d: %v\n", i+1, node)) |
||||
} |
||||
|
||||
return buf.String() |
||||
} |
||||
@ -0,0 +1,41 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"fmt" |
||||
) |
||||
|
||||
var ( |
||||
emptyRunes = []rune{} |
||||
) |
||||
|
||||
func isSep(b []rune) bool { |
||||
if len(b) == 0 { |
||||
return false |
||||
} |
||||
|
||||
switch b[0] { |
||||
case '[', ']': |
||||
return true |
||||
default: |
||||
return false |
||||
} |
||||
} |
||||
|
||||
var ( |
||||
openBrace = []rune("[") |
||||
closeBrace = []rune("]") |
||||
) |
||||
|
||||
func newSepToken(b []rune) (Token, int, error) { |
||||
tok := Token{} |
||||
|
||||
switch b[0] { |
||||
case '[': |
||||
tok = newToken(TokenSep, openBrace, NoneType) |
||||
case ']': |
||||
tok = newToken(TokenSep, closeBrace, NoneType) |
||||
default: |
||||
return tok, 0, NewParseError(fmt.Sprintf("unexpected sep type, %v", b[0])) |
||||
} |
||||
return tok, 1, nil |
||||
} |
||||
@ -0,0 +1,45 @@ |
||||
package ini |
||||
|
||||
// skipper is used to skip certain blocks of an ini file.
|
||||
// Currently skipper is used to skip nested blocks of ini
|
||||
// files. See example below
|
||||
//
|
||||
// [ foo ]
|
||||
// nested = ; this section will be skipped
|
||||
// a=b
|
||||
// c=d
|
||||
// bar=baz ; this will be included
|
||||
type skipper struct { |
||||
shouldSkip bool |
||||
TokenSet bool |
||||
prevTok Token |
||||
} |
||||
|
||||
func newSkipper() skipper { |
||||
return skipper{ |
||||
prevTok: emptyToken, |
||||
} |
||||
} |
||||
|
||||
func (s *skipper) ShouldSkip(tok Token) bool { |
||||
if s.shouldSkip && |
||||
s.prevTok.Type() == TokenNL && |
||||
tok.Type() != TokenWS { |
||||
|
||||
s.Continue() |
||||
return false |
||||
} |
||||
s.prevTok = tok |
||||
|
||||
return s.shouldSkip |
||||
} |
||||
|
||||
func (s *skipper) Skip() { |
||||
s.shouldSkip = true |
||||
s.prevTok = emptyToken |
||||
} |
||||
|
||||
func (s *skipper) Continue() { |
||||
s.shouldSkip = false |
||||
s.prevTok = emptyToken |
||||
} |
||||
@ -0,0 +1,35 @@ |
||||
package ini |
||||
|
||||
// Statement is an empty AST mostly used for transitioning states.
|
||||
func newStatement() AST { |
||||
return newAST(ASTKindStatement, AST{}) |
||||
} |
||||
|
||||
// SectionStatement represents a section AST
|
||||
func newSectionStatement(tok Token) AST { |
||||
return newASTWithRootToken(ASTKindSectionStatement, tok) |
||||
} |
||||
|
||||
// ExprStatement represents a completed expression AST
|
||||
func newExprStatement(ast AST) AST { |
||||
return newAST(ASTKindExprStatement, ast) |
||||
} |
||||
|
||||
// CommentStatement represents a comment in the ini definition.
|
||||
//
|
||||
// grammar:
|
||||
// comment -> #comment' | ;comment'
|
||||
// comment' -> epsilon | value
|
||||
func newCommentStatement(tok Token) AST { |
||||
return newAST(ASTKindCommentStatement, newExpression(tok)) |
||||
} |
||||
|
||||
// CompletedSectionStatement represents a completed section
|
||||
func newCompletedSectionStatement(ast AST) AST { |
||||
return newAST(ASTKindCompletedSectionStatement, ast) |
||||
} |
||||
|
||||
// SkipStatement is used to skip whole statements
|
||||
func newSkipStatement(ast AST) AST { |
||||
return newAST(ASTKindSkipStatement, ast) |
||||
} |
||||
@ -0,0 +1,284 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"fmt" |
||||
) |
||||
|
||||
// getStringValue will return a quoted string and the amount
|
||||
// of bytes read
|
||||
//
|
||||
// an error will be returned if the string is not properly formatted
|
||||
func getStringValue(b []rune) (int, error) { |
||||
if b[0] != '"' { |
||||
return 0, NewParseError("strings must start with '\"'") |
||||
} |
||||
|
||||
endQuote := false |
||||
i := 1 |
||||
|
||||
for ; i < len(b) && !endQuote; i++ { |
||||
if escaped := isEscaped(b[:i], b[i]); b[i] == '"' && !escaped { |
||||
endQuote = true |
||||
break |
||||
} else if escaped { |
||||
/*c, err := getEscapedByte(b[i]) |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
|
||||
b[i-1] = c |
||||
b = append(b[:i], b[i+1:]...) |
||||
i--*/ |
||||
|
||||
continue |
||||
} |
||||
} |
||||
|
||||
if !endQuote { |
||||
return 0, NewParseError("missing '\"' in string value") |
||||
} |
||||
|
||||
return i + 1, nil |
||||
} |
||||
|
||||
// getBoolValue will return a boolean and the amount
|
||||
// of bytes read
|
||||
//
|
||||
// an error will be returned if the boolean is not of a correct
|
||||
// value
|
||||
func getBoolValue(b []rune) (int, error) { |
||||
if len(b) < 4 { |
||||
return 0, NewParseError("invalid boolean value") |
||||
} |
||||
|
||||
n := 0 |
||||
for _, lv := range literalValues { |
||||
if len(lv) > len(b) { |
||||
continue |
||||
} |
||||
|
||||
if isLitValue(lv, b) { |
||||
n = len(lv) |
||||
} |
||||
} |
||||
|
||||
if n == 0 { |
||||
return 0, NewParseError("invalid boolean value") |
||||
} |
||||
|
||||
return n, nil |
||||
} |
||||
|
||||
// getNumericalValue will return a numerical string, the amount
|
||||
// of bytes read, and the base of the number
|
||||
//
|
||||
// an error will be returned if the number is not of a correct
|
||||
// value
|
||||
func getNumericalValue(b []rune) (int, int, error) { |
||||
if !isDigit(b[0]) { |
||||
return 0, 0, NewParseError("invalid digit value") |
||||
} |
||||
|
||||
i := 0 |
||||
helper := numberHelper{} |
||||
|
||||
loop: |
||||
for negativeIndex := 0; i < len(b); i++ { |
||||
negativeIndex++ |
||||
|
||||
if !isDigit(b[i]) { |
||||
switch b[i] { |
||||
case '-': |
||||
if helper.IsNegative() || negativeIndex != 1 { |
||||
return 0, 0, NewParseError("parse error '-'") |
||||
} |
||||
|
||||
n := getNegativeNumber(b[i:]) |
||||
i += (n - 1) |
||||
helper.Determine(b[i]) |
||||
continue |
||||
case '.': |
||||
if err := helper.Determine(b[i]); err != nil { |
||||
return 0, 0, err |
||||
} |
||||
case 'e', 'E': |
||||
if err := helper.Determine(b[i]); err != nil { |
||||
return 0, 0, err |
||||
} |
||||
|
||||
negativeIndex = 0 |
||||
case 'b': |
||||
if helper.numberFormat == hex { |
||||
break |
||||
} |
||||
fallthrough |
||||
case 'o', 'x': |
||||
if i == 0 && b[i] != '0' { |
||||
return 0, 0, NewParseError("incorrect base format, expected leading '0'") |
||||
} |
||||
|
||||
if i != 1 { |
||||
return 0, 0, NewParseError(fmt.Sprintf("incorrect base format found %s at %d index", string(b[i]), i)) |
||||
} |
||||
|
||||
if err := helper.Determine(b[i]); err != nil { |
||||
return 0, 0, err |
||||
} |
||||
default: |
||||
if isWhitespace(b[i]) { |
||||
break loop |
||||
} |
||||
|
||||
if isNewline(b[i:]) { |
||||
break loop |
||||
} |
||||
|
||||
if !(helper.numberFormat == hex && isHexByte(b[i])) { |
||||
if i+2 < len(b) && !isNewline(b[i:i+2]) { |
||||
return 0, 0, NewParseError("invalid numerical character") |
||||
} else if !isNewline([]rune{b[i]}) { |
||||
return 0, 0, NewParseError("invalid numerical character") |
||||
} |
||||
|
||||
break loop |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return helper.Base(), i, nil |
||||
} |
||||
|
||||
// isDigit will return whether or not something is an integer
|
||||
func isDigit(b rune) bool { |
||||
return b >= '0' && b <= '9' |
||||
} |
||||
|
||||
func hasExponent(v []rune) bool { |
||||
return contains(v, 'e') || contains(v, 'E') |
||||
} |
||||
|
||||
func isBinaryByte(b rune) bool { |
||||
switch b { |
||||
case '0', '1': |
||||
return true |
||||
default: |
||||
return false |
||||
} |
||||
} |
||||
|
||||
func isOctalByte(b rune) bool { |
||||
switch b { |
||||
case '0', '1', '2', '3', '4', '5', '6', '7': |
||||
return true |
||||
default: |
||||
return false |
||||
} |
||||
} |
||||
|
||||
func isHexByte(b rune) bool { |
||||
if isDigit(b) { |
||||
return true |
||||
} |
||||
return (b >= 'A' && b <= 'F') || |
||||
(b >= 'a' && b <= 'f') |
||||
} |
||||
|
||||
func getValue(b []rune) (int, error) { |
||||
i := 0 |
||||
|
||||
for i < len(b) { |
||||
if isNewline(b[i:]) { |
||||
break |
||||
} |
||||
|
||||
if isOp(b[i:]) { |
||||
break |
||||
} |
||||
|
||||
valid, n, err := isValid(b[i:]) |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
|
||||
if !valid { |
||||
break |
||||
} |
||||
|
||||
i += n |
||||
} |
||||
|
||||
return i, nil |
||||
} |
||||
|
||||
// getNegativeNumber will return a negative number from a
|
||||
// byte slice. This will iterate through all characters until
|
||||
// a non-digit has been found.
|
||||
func getNegativeNumber(b []rune) int { |
||||
if b[0] != '-' { |
||||
return 0 |
||||
} |
||||
|
||||
i := 1 |
||||
for ; i < len(b); i++ { |
||||
if !isDigit(b[i]) { |
||||
return i |
||||
} |
||||
} |
||||
|
||||
return i |
||||
} |
||||
|
||||
// isEscaped will return whether or not the character is an escaped
|
||||
// character.
|
||||
func isEscaped(value []rune, b rune) bool { |
||||
if len(value) == 0 { |
||||
return false |
||||
} |
||||
|
||||
switch b { |
||||
case '\'': // single quote
|
||||
case '"': // quote
|
||||
case 'n': // newline
|
||||
case 't': // tab
|
||||
case '\\': // backslash
|
||||
default: |
||||
return false |
||||
} |
||||
|
||||
return value[len(value)-1] == '\\' |
||||
} |
||||
|
||||
func getEscapedByte(b rune) (rune, error) { |
||||
switch b { |
||||
case '\'': // single quote
|
||||
return '\'', nil |
||||
case '"': // quote
|
||||
return '"', nil |
||||
case 'n': // newline
|
||||
return '\n', nil |
||||
case 't': // table
|
||||
return '\t', nil |
||||
case '\\': // backslash
|
||||
return '\\', nil |
||||
default: |
||||
return b, NewParseError(fmt.Sprintf("invalid escaped character %c", b)) |
||||
} |
||||
} |
||||
|
||||
func removeEscapedCharacters(b []rune) []rune { |
||||
for i := 0; i < len(b); i++ { |
||||
if isEscaped(b[:i], b[i]) { |
||||
c, err := getEscapedByte(b[i]) |
||||
if err != nil { |
||||
return b |
||||
} |
||||
|
||||
b[i-1] = c |
||||
b = append(b[:i], b[i+1:]...) |
||||
i-- |
||||
} |
||||
} |
||||
|
||||
return b |
||||
} |
||||
@ -0,0 +1,166 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"fmt" |
||||
"sort" |
||||
) |
||||
|
||||
// Visitor is an interface used by walkers that will
|
||||
// traverse an array of ASTs.
|
||||
type Visitor interface { |
||||
VisitExpr(AST) error |
||||
VisitStatement(AST) error |
||||
} |
||||
|
||||
// DefaultVisitor is used to visit statements and expressions
|
||||
// and ensure that they are both of the correct format.
|
||||
// In addition, upon visiting this will build sections and populate
|
||||
// the Sections field which can be used to retrieve profile
|
||||
// configuration.
|
||||
type DefaultVisitor struct { |
||||
scope string |
||||
Sections Sections |
||||
} |
||||
|
||||
// NewDefaultVisitor return a DefaultVisitor
|
||||
func NewDefaultVisitor() *DefaultVisitor { |
||||
return &DefaultVisitor{ |
||||
Sections: Sections{ |
||||
container: map[string]Section{}, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// VisitExpr visits expressions...
|
||||
func (v *DefaultVisitor) VisitExpr(expr AST) error { |
||||
t := v.Sections.container[v.scope] |
||||
if t.values == nil { |
||||
t.values = values{} |
||||
} |
||||
|
||||
switch expr.Kind { |
||||
case ASTKindExprStatement: |
||||
opExpr := expr.GetRoot() |
||||
switch opExpr.Kind { |
||||
case ASTKindEqualExpr: |
||||
children := opExpr.GetChildren() |
||||
if len(children) <= 1 { |
||||
return NewParseError("unexpected token type") |
||||
} |
||||
|
||||
rhs := children[1] |
||||
|
||||
if rhs.Root.Type() != TokenLit { |
||||
return NewParseError("unexpected token type") |
||||
} |
||||
|
||||
key := EqualExprKey(opExpr) |
||||
v, err := newValue(rhs.Root.ValueType, rhs.Root.base, rhs.Root.Raw()) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
t.values[key] = v |
||||
default: |
||||
return NewParseError(fmt.Sprintf("unsupported expression %v", expr)) |
||||
} |
||||
default: |
||||
return NewParseError(fmt.Sprintf("unsupported expression %v", expr)) |
||||
} |
||||
|
||||
v.Sections.container[v.scope] = t |
||||
return nil |
||||
} |
||||
|
||||
// VisitStatement visits statements...
|
||||
func (v *DefaultVisitor) VisitStatement(stmt AST) error { |
||||
switch stmt.Kind { |
||||
case ASTKindCompletedSectionStatement: |
||||
child := stmt.GetRoot() |
||||
if child.Kind != ASTKindSectionStatement { |
||||
return NewParseError(fmt.Sprintf("unsupported child statement: %T", child)) |
||||
} |
||||
|
||||
name := string(child.Root.Raw()) |
||||
v.Sections.container[name] = Section{} |
||||
v.scope = name |
||||
default: |
||||
return NewParseError(fmt.Sprintf("unsupported statement: %s", stmt.Kind)) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// Sections is a map of Section structures that represent
|
||||
// a configuration.
|
||||
type Sections struct { |
||||
container map[string]Section |
||||
} |
||||
|
||||
// GetSection will return section p. If section p does not exist,
|
||||
// false will be returned in the second parameter.
|
||||
func (t Sections) GetSection(p string) (Section, bool) { |
||||
v, ok := t.container[p] |
||||
return v, ok |
||||
} |
||||
|
||||
// values represents a map of union values.
|
||||
type values map[string]Value |
||||
|
||||
// List will return a list of all sections that were successfully
|
||||
// parsed.
|
||||
func (t Sections) List() []string { |
||||
keys := make([]string, len(t.container)) |
||||
i := 0 |
||||
for k := range t.container { |
||||
keys[i] = k |
||||
i++ |
||||
} |
||||
|
||||
sort.Strings(keys) |
||||
return keys |
||||
} |
||||
|
||||
// Section contains a name and values. This represent
|
||||
// a sectioned entry in a configuration file.
|
||||
type Section struct { |
||||
Name string |
||||
values values |
||||
} |
||||
|
||||
// Has will return whether or not an entry exists in a given section
|
||||
func (t Section) Has(k string) bool { |
||||
_, ok := t.values[k] |
||||
return ok |
||||
} |
||||
|
||||
// ValueType will returned what type the union is set to. If
|
||||
// k was not found, the NoneType will be returned.
|
||||
func (t Section) ValueType(k string) (ValueType, bool) { |
||||
v, ok := t.values[k] |
||||
return v.Type, ok |
||||
} |
||||
|
||||
// Bool returns a bool value at k
|
||||
func (t Section) Bool(k string) bool { |
||||
return t.values[k].BoolValue() |
||||
} |
||||
|
||||
// Int returns an integer value at k
|
||||
func (t Section) Int(k string) int64 { |
||||
return t.values[k].IntValue() |
||||
} |
||||
|
||||
// Float64 returns a float value at k
|
||||
func (t Section) Float64(k string) float64 { |
||||
return t.values[k].FloatValue() |
||||
} |
||||
|
||||
// String returns the string value at k
|
||||
func (t Section) String(k string) string { |
||||
_, ok := t.values[k] |
||||
if !ok { |
||||
return "" |
||||
} |
||||
return t.values[k].StringValue() |
||||
} |
||||
@ -0,0 +1,25 @@ |
||||
package ini |
||||
|
||||
// Walk will traverse the AST using the v, the Visitor.
|
||||
func Walk(tree []AST, v Visitor) error { |
||||
for _, node := range tree { |
||||
switch node.Kind { |
||||
case ASTKindExpr, |
||||
ASTKindExprStatement: |
||||
|
||||
if err := v.VisitExpr(node); err != nil { |
||||
return err |
||||
} |
||||
case ASTKindStatement, |
||||
ASTKindCompletedSectionStatement, |
||||
ASTKindNestedSectionStatement, |
||||
ASTKindCompletedNestedSectionStatement: |
||||
|
||||
if err := v.VisitStatement(node); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
@ -0,0 +1,24 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"unicode" |
||||
) |
||||
|
||||
// isWhitespace will return whether or not the character is
|
||||
// a whitespace character.
|
||||
//
|
||||
// Whitespace is defined as a space or tab.
|
||||
func isWhitespace(c rune) bool { |
||||
return unicode.IsSpace(c) && c != '\n' && c != '\r' |
||||
} |
||||
|
||||
func newWSToken(b []rune) (Token, int, error) { |
||||
i := 0 |
||||
for ; i < len(b); i++ { |
||||
if !isWhitespace(b[i]) { |
||||
break |
||||
} |
||||
} |
||||
|
||||
return newToken(TokenWS, b[:i], NoneType), i, nil |
||||
} |
||||
@ -0,0 +1,57 @@ |
||||
package s3err |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr" |
||||
"github.com/aws/aws-sdk-go/aws/request" |
||||
) |
||||
|
||||
// RequestFailure provides additional S3 specific metadata for the request
|
||||
// failure.
|
||||
type RequestFailure struct { |
||||
awserr.RequestFailure |
||||
|
||||
hostID string |
||||
} |
||||
|
||||
// NewRequestFailure returns a request failure error decordated with S3
|
||||
// specific metadata.
|
||||
func NewRequestFailure(err awserr.RequestFailure, hostID string) *RequestFailure { |
||||
return &RequestFailure{RequestFailure: err, hostID: hostID} |
||||
} |
||||
|
||||
func (r RequestFailure) Error() string { |
||||
extra := fmt.Sprintf("status code: %d, request id: %s, host id: %s", |
||||
r.StatusCode(), r.RequestID(), r.hostID) |
||||
return awserr.SprintError(r.Code(), r.Message(), extra, r.OrigErr()) |
||||
} |
||||
func (r RequestFailure) String() string { |
||||
return r.Error() |
||||
} |
||||
|
||||
// HostID returns the HostID request response value.
|
||||
func (r RequestFailure) HostID() string { |
||||
return r.hostID |
||||
} |
||||
|
||||
// RequestFailureWrapperHandler returns a handler to rap an
|
||||
// awserr.RequestFailure with the S3 request ID 2 from the response.
|
||||
func RequestFailureWrapperHandler() request.NamedHandler { |
||||
return request.NamedHandler{ |
||||
Name: "awssdk.s3.errorHandler", |
||||
Fn: func(req *request.Request) { |
||||
reqErr, ok := req.Error.(awserr.RequestFailure) |
||||
if !ok || reqErr == nil { |
||||
return |
||||
} |
||||
|
||||
hostID := req.HTTPResponse.Header.Get("X-Amz-Id-2") |
||||
if req.Error == nil { |
||||
return |
||||
} |
||||
|
||||
req.Error = NewRequestFailure(reqErr, hostID) |
||||
}, |
||||
} |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
package sdkuri |
||||
|
||||
import ( |
||||
"path" |
||||
"strings" |
||||
) |
||||
|
||||
// PathJoin will join the elements of the path delimited by the "/"
|
||||
// character. Similar to path.Join with the exception the trailing "/"
|
||||
// character is preserved if present.
|
||||
func PathJoin(elems ...string) string { |
||||
if len(elems) == 0 { |
||||
return "" |
||||
} |
||||
|
||||
hasTrailing := strings.HasSuffix(elems[len(elems)-1], "/") |
||||
str := path.Join(elems...) |
||||
if hasTrailing && str != "/" { |
||||
str += "/" |
||||
} |
||||
|
||||
return str |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
package shareddefaults |
||||
|
||||
const ( |
||||
// ECSCredsProviderEnvVar is an environmental variable key used to
|
||||
// determine which path needs to be hit.
|
||||
ECSCredsProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" |
||||
) |
||||
|
||||
// ECSContainerCredentialsURI is the endpoint to retrieve container
|
||||
// credentials. This can be overridden to test to ensure the credential process
|
||||
// is behaving correctly.
|
||||
var ECSContainerCredentialsURI = "http://169.254.170.2" |
||||
36
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamapi/api.go
generated
vendored
36
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamapi/api.go
generated
vendored
@ -0,0 +1,68 @@ |
||||
package protocol |
||||
|
||||
import ( |
||||
"strings" |
||||
|
||||
"github.com/aws/aws-sdk-go/aws/request" |
||||
) |
||||
|
||||
// ValidateEndpointHostHandler is a request handler that will validate the
|
||||
// request endpoint's hosts is a valid RFC 3986 host.
|
||||
var ValidateEndpointHostHandler = request.NamedHandler{ |
||||
Name: "awssdk.protocol.ValidateEndpointHostHandler", |
||||
Fn: func(r *request.Request) { |
||||
err := ValidateEndpointHost(r.Operation.Name, r.HTTPRequest.URL.Host) |
||||
if err != nil { |
||||
r.Error = err |
||||
} |
||||
}, |
||||
} |
||||
|
||||
// ValidateEndpointHost validates that the host string passed in is a valid RFC
|
||||
// 3986 host. Returns error if the host is not valid.
|
||||
func ValidateEndpointHost(opName, host string) error { |
||||
paramErrs := request.ErrInvalidParams{Context: opName} |
||||
labels := strings.Split(host, ".") |
||||
|
||||
for i, label := range labels { |
||||
if i == len(labels)-1 && len(label) == 0 { |
||||
// Allow trailing dot for FQDN hosts.
|
||||
continue |
||||
} |
||||
|
||||
if !ValidHostLabel(label) { |
||||
paramErrs.Add(request.NewErrParamFormat( |
||||
"endpoint host label", "[a-zA-Z0-9-]{1,63}", label)) |
||||
} |
||||
} |
||||
|
||||
if len(host) > 255 { |
||||
paramErrs.Add(request.NewErrParamMaxLen( |
||||
"endpoint host", 255, host, |
||||
)) |
||||
} |
||||
|
||||
if paramErrs.Len() > 0 { |
||||
return paramErrs |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// ValidHostLabel returns if the label is a valid RFC 3986 host label.
|
||||
func ValidHostLabel(label string) bool { |
||||
if l := len(label); l == 0 || l > 63 { |
||||
return false |
||||
} |
||||
for _, r := range label { |
||||
switch { |
||||
case r >= '0' && r <= '9': |
||||
case r >= 'A' && r <= 'Z': |
||||
case r >= 'a' && r <= 'z': |
||||
case r == '-': |
||||
default: |
||||
return false |
||||
} |
||||
} |
||||
|
||||
return true |
||||
} |
||||
@ -0,0 +1,54 @@ |
||||
package protocol |
||||
|
||||
import ( |
||||
"strings" |
||||
|
||||
"github.com/aws/aws-sdk-go/aws" |
||||
"github.com/aws/aws-sdk-go/aws/request" |
||||
) |
||||
|
||||
// HostPrefixHandlerName is the handler name for the host prefix request
|
||||
// handler.
|
||||
const HostPrefixHandlerName = "awssdk.endpoint.HostPrefixHandler" |
||||
|
||||
// NewHostPrefixHandler constructs a build handler
|
||||
func NewHostPrefixHandler(prefix string, labelsFn func() map[string]string) request.NamedHandler { |
||||
builder := HostPrefixBuilder{ |
||||
Prefix: prefix, |
||||
LabelsFn: labelsFn, |
||||
} |
||||
|
||||
return request.NamedHandler{ |
||||
Name: HostPrefixHandlerName, |
||||
Fn: builder.Build, |
||||
} |
||||
} |
||||
|
||||
// HostPrefixBuilder provides the request handler to expand and prepend
|
||||
// the host prefix into the operation's request endpoint host.
|
||||
type HostPrefixBuilder struct { |
||||
Prefix string |
||||
LabelsFn func() map[string]string |
||||
} |
||||
|
||||
// Build updates the passed in Request with the HostPrefix template expanded.
|
||||
func (h HostPrefixBuilder) Build(r *request.Request) { |
||||
if aws.BoolValue(r.Config.DisableEndpointHostPrefix) { |
||||
return |
||||
} |
||||
|
||||
var labels map[string]string |
||||
if h.LabelsFn != nil { |
||||
labels = h.LabelsFn() |
||||
} |
||||
|
||||
prefix := h.Prefix |
||||
for name, value := range labels { |
||||
prefix = strings.Replace(prefix, "{"+name+"}", value, -1) |
||||
} |
||||
|
||||
r.HTTPRequest.URL.Host = prefix + r.HTTPRequest.URL.Host |
||||
if len(r.HTTPRequest.Host) > 0 { |
||||
r.HTTPRequest.Host = prefix + r.HTTPRequest.Host |
||||
} |
||||
} |
||||
@ -0,0 +1,296 @@ |
||||
// Package jsonutil provides JSON serialization of AWS requests and responses.
|
||||
package jsonutil |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/base64" |
||||
"encoding/json" |
||||
"fmt" |
||||
"math" |
||||
"reflect" |
||||
"sort" |
||||
"strconv" |
||||
"time" |
||||
|
||||
"github.com/aws/aws-sdk-go/aws" |
||||
"github.com/aws/aws-sdk-go/private/protocol" |
||||
) |
||||
|
||||
var timeType = reflect.ValueOf(time.Time{}).Type() |
||||
var byteSliceType = reflect.ValueOf([]byte{}).Type() |
||||
|
||||
// BuildJSON builds a JSON string for a given object v.
|
||||
func BuildJSON(v interface{}) ([]byte, error) { |
||||
var buf bytes.Buffer |
||||
|
||||
err := buildAny(reflect.ValueOf(v), &buf, "") |
||||
return buf.Bytes(), err |
||||
} |
||||
|
||||
func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { |
||||
origVal := value |
||||
value = reflect.Indirect(value) |
||||
if !value.IsValid() { |
||||
return nil |
||||
} |
||||
|
||||
vtype := value.Type() |
||||
|
||||
t := tag.Get("type") |
||||
if t == "" { |
||||
switch vtype.Kind() { |
||||
case reflect.Struct: |
||||
// also it can't be a time object
|
||||
if value.Type() != timeType { |
||||
t = "structure" |
||||
} |
||||
case reflect.Slice: |
||||
// also it can't be a byte slice
|
||||
if _, ok := value.Interface().([]byte); !ok { |
||||
t = "list" |
||||
} |
||||
case reflect.Map: |
||||
// cannot be a JSONValue map
|
||||
if _, ok := value.Interface().(aws.JSONValue); !ok { |
||||
t = "map" |
||||
} |
||||
} |
||||
} |
||||
|
||||
switch t { |
||||
case "structure": |
||||
if field, ok := vtype.FieldByName("_"); ok { |
||||
tag = field.Tag |
||||
} |
||||
return buildStruct(value, buf, tag) |
||||
case "list": |
||||
return buildList(value, buf, tag) |
||||
case "map": |
||||
return buildMap(value, buf, tag) |
||||
default: |
||||
return buildScalar(origVal, buf, tag) |
||||
} |
||||
} |
||||
|
||||
func buildStruct(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { |
||||
if !value.IsValid() { |
||||
return nil |
||||
} |
||||
|
||||
// unwrap payloads
|
||||
if payload := tag.Get("payload"); payload != "" { |
||||
field, _ := value.Type().FieldByName(payload) |
||||
tag = field.Tag |
||||
value = elemOf(value.FieldByName(payload)) |
||||
|
||||
if !value.IsValid() { |
||||
return nil |
||||
} |
||||
} |
||||
|
||||
buf.WriteByte('{') |
||||
|
||||
t := value.Type() |
||||
first := true |
||||
for i := 0; i < t.NumField(); i++ { |
||||
member := value.Field(i) |
||||
|
||||
// This allocates the most memory.
|
||||
// Additionally, we cannot skip nil fields due to
|
||||
// idempotency auto filling.
|
||||
field := t.Field(i) |
||||
|
||||
if field.PkgPath != "" { |
||||
continue // ignore unexported fields
|
||||
} |
||||
if field.Tag.Get("json") == "-" { |
||||
continue |
||||
} |
||||
if field.Tag.Get("location") != "" { |
||||
continue // ignore non-body elements
|
||||
} |
||||
if field.Tag.Get("ignore") != "" { |
||||
continue |
||||
} |
||||
|
||||
if protocol.CanSetIdempotencyToken(member, field) { |
||||
token := protocol.GetIdempotencyToken() |
||||
member = reflect.ValueOf(&token) |
||||
} |
||||
|
||||
if (member.Kind() == reflect.Ptr || member.Kind() == reflect.Slice || member.Kind() == reflect.Map) && member.IsNil() { |
||||
continue // ignore unset fields
|
||||
} |
||||
|
||||
if first { |
||||
first = false |
||||
} else { |
||||
buf.WriteByte(',') |
||||
} |
||||
|
||||
// figure out what this field is called
|
||||
name := field.Name |
||||
if locName := field.Tag.Get("locationName"); locName != "" { |
||||
name = locName |
||||
} |
||||
|
||||
writeString(name, buf) |
||||
buf.WriteString(`:`) |
||||
|
||||
err := buildAny(member, buf, field.Tag) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
} |
||||
|
||||
buf.WriteString("}") |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func buildList(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { |
||||
buf.WriteString("[") |
||||
|
||||
for i := 0; i < value.Len(); i++ { |
||||
buildAny(value.Index(i), buf, "") |
||||
|
||||
if i < value.Len()-1 { |
||||
buf.WriteString(",") |
||||
} |
||||
} |
||||
|
||||
buf.WriteString("]") |
||||
|
||||
return nil |
||||
} |
||||
|
||||
type sortedValues []reflect.Value |
||||
|
||||
func (sv sortedValues) Len() int { return len(sv) } |
||||
func (sv sortedValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } |
||||
func (sv sortedValues) Less(i, j int) bool { return sv[i].String() < sv[j].String() } |
||||
|
||||
func buildMap(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { |
||||
buf.WriteString("{") |
||||
|
||||
sv := sortedValues(value.MapKeys()) |
||||
sort.Sort(sv) |
||||
|
||||
for i, k := range sv { |
||||
if i > 0 { |
||||
buf.WriteByte(',') |
||||
} |
||||
|
||||
writeString(k.String(), buf) |
||||
buf.WriteString(`:`) |
||||
|
||||
buildAny(value.MapIndex(k), buf, "") |
||||
} |
||||
|
||||
buf.WriteString("}") |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func buildScalar(v reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { |
||||
// prevents allocation on the heap.
|
||||
scratch := [64]byte{} |
||||
switch value := reflect.Indirect(v); value.Kind() { |
||||
case reflect.String: |
||||
writeString(value.String(), buf) |
||||
case reflect.Bool: |
||||
if value.Bool() { |
||||
buf.WriteString("true") |
||||
} else { |
||||
buf.WriteString("false") |
||||
} |
||||
case reflect.Int64: |
||||
buf.Write(strconv.AppendInt(scratch[:0], value.Int(), 10)) |
||||
case reflect.Float64: |
||||
f := value.Float() |
||||
if math.IsInf(f, 0) || math.IsNaN(f) { |
||||
return &json.UnsupportedValueError{Value: v, Str: strconv.FormatFloat(f, 'f', -1, 64)} |
||||
} |
||||
buf.Write(strconv.AppendFloat(scratch[:0], f, 'f', -1, 64)) |
||||
default: |
||||
switch converted := value.Interface().(type) { |
||||
case time.Time: |
||||
format := tag.Get("timestampFormat") |
||||
if len(format) == 0 { |
||||
format = protocol.UnixTimeFormatName |
||||
} |
||||
|
||||
ts := protocol.FormatTime(format, converted) |
||||
if format != protocol.UnixTimeFormatName { |
||||
ts = `"` + ts + `"` |
||||
} |
||||
|
||||
buf.WriteString(ts) |
||||
case []byte: |
||||
if !value.IsNil() { |
||||
buf.WriteByte('"') |
||||
if len(converted) < 1024 { |
||||
// for small buffers, using Encode directly is much faster.
|
||||
dst := make([]byte, base64.StdEncoding.EncodedLen(len(converted))) |
||||
base64.StdEncoding.Encode(dst, converted) |
||||
buf.Write(dst) |
||||
} else { |
||||
// for large buffers, avoid unnecessary extra temporary
|
||||
// buffer space.
|
||||
enc := base64.NewEncoder(base64.StdEncoding, buf) |
||||
enc.Write(converted) |
||||
enc.Close() |
||||
} |
||||
buf.WriteByte('"') |
||||
} |
||||
case aws.JSONValue: |
||||
str, err := protocol.EncodeJSONValue(converted, protocol.QuotedEscape) |
||||
if err != nil { |
||||
return fmt.Errorf("unable to encode JSONValue, %v", err) |
||||
} |
||||
buf.WriteString(str) |
||||
default: |
||||
return fmt.Errorf("unsupported JSON value %v (%s)", value.Interface(), value.Type()) |
||||
} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
var hex = "0123456789abcdef" |
||||
|
||||
func writeString(s string, buf *bytes.Buffer) { |
||||
buf.WriteByte('"') |
||||
for i := 0; i < len(s); i++ { |
||||
if s[i] == '"' { |
||||
buf.WriteString(`\"`) |
||||
} else if s[i] == '\\' { |
||||
buf.WriteString(`\\`) |
||||
} else if s[i] == '\b' { |
||||
buf.WriteString(`\b`) |
||||
} else if s[i] == '\f' { |
||||
buf.WriteString(`\f`) |
||||
} else if s[i] == '\r' { |
||||
buf.WriteString(`\r`) |
||||
} else if s[i] == '\t' { |
||||
buf.WriteString(`\t`) |
||||
} else if s[i] == '\n' { |
||||
buf.WriteString(`\n`) |
||||
} else if s[i] < 32 { |
||||
buf.WriteString("\\u00") |
||||
buf.WriteByte(hex[s[i]>>4]) |
||||
buf.WriteByte(hex[s[i]&0xF]) |
||||
} else { |
||||
buf.WriteByte(s[i]) |
||||
} |
||||
} |
||||
buf.WriteByte('"') |
||||
} |
||||
|
||||
// Returns the reflection element of a value, if it is a pointer.
|
||||
func elemOf(value reflect.Value) reflect.Value { |
||||
for value.Kind() == reflect.Ptr { |
||||
value = value.Elem() |
||||
} |
||||
return value |
||||
} |
||||
@ -0,0 +1,228 @@ |
||||
package jsonutil |
||||
|
||||
import ( |
||||
"encoding/base64" |
||||
"encoding/json" |
||||
"fmt" |
||||
"io" |
||||
"reflect" |
||||
"time" |
||||
|
||||
"github.com/aws/aws-sdk-go/aws" |
||||
"github.com/aws/aws-sdk-go/private/protocol" |
||||
) |
||||
|
||||
// UnmarshalJSON reads a stream and unmarshals the results in object v.
|
||||
func UnmarshalJSON(v interface{}, stream io.Reader) error { |
||||
var out interface{} |
||||
|
||||
err := json.NewDecoder(stream).Decode(&out) |
||||
if err == io.EOF { |
||||
return nil |
||||
} else if err != nil { |
||||
return err |
||||
} |
||||
|
||||
return unmarshalAny(reflect.ValueOf(v), out, "") |
||||
} |
||||
|
||||
func unmarshalAny(value reflect.Value, data interface{}, tag reflect.StructTag) error { |
||||
vtype := value.Type() |
||||
if vtype.Kind() == reflect.Ptr { |
||||
vtype = vtype.Elem() // check kind of actual element type
|
||||
} |
||||
|
||||
t := tag.Get("type") |
||||
if t == "" { |
||||
switch vtype.Kind() { |
||||
case reflect.Struct: |
||||
// also it can't be a time object
|
||||
if _, ok := value.Interface().(*time.Time); !ok { |
||||
t = "structure" |
||||
} |
||||
case reflect.Slice: |
||||
// also it can't be a byte slice
|
||||
if _, ok := value.Interface().([]byte); !ok { |
||||
t = "list" |
||||
} |
||||
case reflect.Map: |
||||
// cannot be a JSONValue map
|
||||
if _, ok := value.Interface().(aws.JSONValue); !ok { |
||||
t = "map" |
||||
} |
||||
} |
||||
} |
||||
|
||||
switch t { |
||||
case "structure": |
||||
if field, ok := vtype.FieldByName("_"); ok { |
||||
tag = field.Tag |
||||
} |
||||
return unmarshalStruct(value, data, tag) |
||||
case "list": |
||||
return unmarshalList(value, data, tag) |
||||
case "map": |
||||
return unmarshalMap(value, data, tag) |
||||
default: |
||||
return unmarshalScalar(value, data, tag) |
||||
} |
||||
} |
||||
|
||||
func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTag) error { |
||||
if data == nil { |
||||
return nil |
||||
} |
||||
mapData, ok := data.(map[string]interface{}) |
||||
if !ok { |
||||
return fmt.Errorf("JSON value is not a structure (%#v)", data) |
||||
} |
||||
|
||||
t := value.Type() |
||||
if value.Kind() == reflect.Ptr { |
||||
if value.IsNil() { // create the structure if it's nil
|
||||
s := reflect.New(value.Type().Elem()) |
||||
value.Set(s) |
||||
value = s |
||||
} |
||||
|
||||
value = value.Elem() |
||||
t = t.Elem() |
||||
} |
||||
|
||||
// unwrap any payloads
|
||||
if payload := tag.Get("payload"); payload != "" { |
||||
field, _ := t.FieldByName(payload) |
||||
return unmarshalAny(value.FieldByName(payload), data, field.Tag) |
||||
} |
||||
|
||||
for i := 0; i < t.NumField(); i++ { |
||||
field := t.Field(i) |
||||
if field.PkgPath != "" { |
||||
continue // ignore unexported fields
|
||||
} |
||||
|
||||
// figure out what this field is called
|
||||
name := field.Name |
||||
if locName := field.Tag.Get("locationName"); locName != "" { |
||||
name = locName |
||||
} |
||||
|
||||
member := value.FieldByIndex(field.Index) |
||||
err := unmarshalAny(member, mapData[name], field.Tag) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) error { |
||||
if data == nil { |
||||
return nil |
||||
} |
||||
listData, ok := data.([]interface{}) |
||||
if !ok { |
||||
return fmt.Errorf("JSON value is not a list (%#v)", data) |
||||
} |
||||
|
||||
if value.IsNil() { |
||||
l := len(listData) |
||||
value.Set(reflect.MakeSlice(value.Type(), l, l)) |
||||
} |
||||
|
||||
for i, c := range listData { |
||||
err := unmarshalAny(value.Index(i), c, "") |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag) error { |
||||
if data == nil { |
||||
return nil |
||||
} |
||||
mapData, ok := data.(map[string]interface{}) |
||||
if !ok { |
||||
return fmt.Errorf("JSON value is not a map (%#v)", data) |
||||
} |
||||
|
||||
if value.IsNil() { |
||||
value.Set(reflect.MakeMap(value.Type())) |
||||
} |
||||
|
||||
for k, v := range mapData { |
||||
kvalue := reflect.ValueOf(k) |
||||
vvalue := reflect.New(value.Type().Elem()).Elem() |
||||
|
||||
unmarshalAny(vvalue, v, "") |
||||
value.SetMapIndex(kvalue, vvalue) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTag) error { |
||||
|
||||
switch d := data.(type) { |
||||
case nil: |
||||
return nil // nothing to do here
|
||||
case string: |
||||
switch value.Interface().(type) { |
||||
case *string: |
||||
value.Set(reflect.ValueOf(&d)) |
||||
case []byte: |
||||
b, err := base64.StdEncoding.DecodeString(d) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
value.Set(reflect.ValueOf(b)) |
||||
case *time.Time: |
||||
format := tag.Get("timestampFormat") |
||||
if len(format) == 0 { |
||||
format = protocol.ISO8601TimeFormatName |
||||
} |
||||
|
||||
t, err := protocol.ParseTime(format, d) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
value.Set(reflect.ValueOf(&t)) |
||||
case aws.JSONValue: |
||||
// No need to use escaping as the value is a non-quoted string.
|
||||
v, err := protocol.DecodeJSONValue(d, protocol.NoEscape) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
value.Set(reflect.ValueOf(v)) |
||||
default: |
||||
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) |
||||
} |
||||
case float64: |
||||
switch value.Interface().(type) { |
||||
case *int64: |
||||
di := int64(d) |
||||
value.Set(reflect.ValueOf(&di)) |
||||
case *float64: |
||||
value.Set(reflect.ValueOf(&d)) |
||||
case *time.Time: |
||||
// Time unmarshaled from a float64 can only be epoch seconds
|
||||
t := time.Unix(int64(d), 0).UTC() |
||||
value.Set(reflect.ValueOf(&t)) |
||||
default: |
||||
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) |
||||
} |
||||
case bool: |
||||
switch value.Interface().(type) { |
||||
case *bool: |
||||
value.Set(reflect.ValueOf(&d)) |
||||
default: |
||||
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) |
||||
} |
||||
default: |
||||
return fmt.Errorf("unsupported JSON value (%v)", data) |
||||
} |
||||
return nil |
||||
} |
||||
@ -0,0 +1,115 @@ |
||||
// Package jsonrpc provides JSON RPC utilities for serialization of AWS
|
||||
// requests and responses.
|
||||
package jsonrpc |
||||
|
||||
//go:generate go run -tags codegen ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/input/json.json build_test.go
|
||||
//go:generate go run -tags codegen ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/output/json.json unmarshal_test.go
|
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"io" |
||||
"strings" |
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr" |
||||
"github.com/aws/aws-sdk-go/aws/request" |
||||
"github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" |
||||
"github.com/aws/aws-sdk-go/private/protocol/rest" |
||||
) |
||||
|
||||
var emptyJSON = []byte("{}") |
||||
|
||||
// BuildHandler is a named request handler for building jsonrpc protocol requests
|
||||
var BuildHandler = request.NamedHandler{Name: "awssdk.jsonrpc.Build", Fn: Build} |
||||
|
||||
// UnmarshalHandler is a named request handler for unmarshaling jsonrpc protocol requests
|
||||
var UnmarshalHandler = request.NamedHandler{Name: "awssdk.jsonrpc.Unmarshal", Fn: Unmarshal} |
||||
|
||||
// UnmarshalMetaHandler is a named request handler for unmarshaling jsonrpc protocol request metadata
|
||||
var UnmarshalMetaHandler = request.NamedHandler{Name: "awssdk.jsonrpc.UnmarshalMeta", Fn: UnmarshalMeta} |
||||
|
||||
// UnmarshalErrorHandler is a named request handler for unmarshaling jsonrpc protocol request errors
|
||||
var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.jsonrpc.UnmarshalError", Fn: UnmarshalError} |
||||
|
||||
// Build builds a JSON payload for a JSON RPC request.
|
||||
func Build(req *request.Request) { |
||||
var buf []byte |
||||
var err error |
||||
if req.ParamsFilled() { |
||||
buf, err = jsonutil.BuildJSON(req.Params) |
||||
if err != nil { |
||||
req.Error = awserr.New("SerializationError", "failed encoding JSON RPC request", err) |
||||
return |
||||
} |
||||
} else { |
||||
buf = emptyJSON |
||||
} |
||||
|
||||
if req.ClientInfo.TargetPrefix != "" || string(buf) != "{}" { |
||||
req.SetBufferBody(buf) |
||||
} |
||||
|
||||
if req.ClientInfo.TargetPrefix != "" { |
||||
target := req.ClientInfo.TargetPrefix + "." + req.Operation.Name |
||||
req.HTTPRequest.Header.Add("X-Amz-Target", target) |
||||
} |
||||
if req.ClientInfo.JSONVersion != "" { |
||||
jsonVersion := req.ClientInfo.JSONVersion |
||||
req.HTTPRequest.Header.Add("Content-Type", "application/x-amz-json-"+jsonVersion) |
||||
} |
||||
} |
||||
|
||||
// Unmarshal unmarshals a response for a JSON RPC service.
|
||||
func Unmarshal(req *request.Request) { |
||||
defer req.HTTPResponse.Body.Close() |
||||
if req.DataFilled() { |
||||
err := jsonutil.UnmarshalJSON(req.Data, req.HTTPResponse.Body) |
||||
if err != nil { |
||||
req.Error = awserr.NewRequestFailure( |
||||
awserr.New("SerializationError", "failed decoding JSON RPC response", err), |
||||
req.HTTPResponse.StatusCode, |
||||
req.RequestID, |
||||
) |
||||
} |
||||
} |
||||
return |
||||
} |
||||
|
||||
// UnmarshalMeta unmarshals headers from a response for a JSON RPC service.
|
||||
func UnmarshalMeta(req *request.Request) { |
||||
rest.UnmarshalMeta(req) |
||||
} |
||||
|
||||
// UnmarshalError unmarshals an error response for a JSON RPC service.
|
||||
func UnmarshalError(req *request.Request) { |
||||
defer req.HTTPResponse.Body.Close() |
||||
|
||||
var jsonErr jsonErrorResponse |
||||
err := json.NewDecoder(req.HTTPResponse.Body).Decode(&jsonErr) |
||||
if err == io.EOF { |
||||
req.Error = awserr.NewRequestFailure( |
||||
awserr.New("SerializationError", req.HTTPResponse.Status, nil), |
||||
req.HTTPResponse.StatusCode, |
||||
req.RequestID, |
||||
) |
||||
return |
||||
} else if err != nil { |
||||
req.Error = awserr.NewRequestFailure( |
||||
awserr.New("SerializationError", "failed decoding JSON RPC error response", err), |
||||
req.HTTPResponse.StatusCode, |
||||
req.RequestID, |
||||
) |
||||
return |
||||
} |
||||
|
||||
codes := strings.SplitN(jsonErr.Code, "#", 2) |
||||
req.Error = awserr.NewRequestFailure( |
||||
awserr.New(codes[len(codes)-1], jsonErr.Message, nil), |
||||
req.HTTPResponse.StatusCode, |
||||
req.RequestID, |
||||
) |
||||
} |
||||
|
||||
type jsonErrorResponse struct { |
||||
Code string `json:"__type"` |
||||
Message string `json:"message"` |
||||
} |
||||
@ -0,0 +1,72 @@ |
||||
package protocol |
||||
|
||||
import ( |
||||
"strconv" |
||||
"time" |
||||
) |
||||
|
||||
// Names of time formats supported by the SDK
|
||||
const ( |
||||
RFC822TimeFormatName = "rfc822" |
||||
ISO8601TimeFormatName = "iso8601" |
||||
UnixTimeFormatName = "unixTimestamp" |
||||
) |
||||
|
||||
// Time formats supported by the SDK
|
||||
const ( |
||||
// RFC 7231#section-7.1.1.1 timetamp format. e.g Tue, 29 Apr 2014 18:30:38 GMT
|
||||
RFC822TimeFormat = "Mon, 2 Jan 2006 15:04:05 GMT" |
||||
|
||||
// RFC3339 a subset of the ISO8601 timestamp format. e.g 2014-04-29T18:30:38Z
|
||||
ISO8601TimeFormat = "2006-01-02T15:04:05Z" |
||||
) |
||||
|
||||
// IsKnownTimestampFormat returns if the timestamp format name
|
||||
// is know to the SDK's protocols.
|
||||
func IsKnownTimestampFormat(name string) bool { |
||||
switch name { |
||||
case RFC822TimeFormatName: |
||||
fallthrough |
||||
case ISO8601TimeFormatName: |
||||
fallthrough |
||||
case UnixTimeFormatName: |
||||
return true |
||||
default: |
||||
return false |
||||
} |
||||
} |
||||
|
||||
// FormatTime returns a string value of the time.
|
||||
func FormatTime(name string, t time.Time) string { |
||||
t = t.UTC() |
||||
|
||||
switch name { |
||||
case RFC822TimeFormatName: |
||||
return t.Format(RFC822TimeFormat) |
||||
case ISO8601TimeFormatName: |
||||
return t.Format(ISO8601TimeFormat) |
||||
case UnixTimeFormatName: |
||||
return strconv.FormatInt(t.Unix(), 10) |
||||
default: |
||||
panic("unknown timestamp format name, " + name) |
||||
} |
||||
} |
||||
|
||||
// ParseTime attempts to parse the time given the format. Returns
|
||||
// the time if it was able to be parsed, and fails otherwise.
|
||||
func ParseTime(formatName, value string) (time.Time, error) { |
||||
switch formatName { |
||||
case RFC822TimeFormatName: |
||||
return time.Parse(RFC822TimeFormat, value) |
||||
case ISO8601TimeFormatName: |
||||
return time.Parse(ISO8601TimeFormat, value) |
||||
case UnixTimeFormatName: |
||||
v, err := strconv.ParseFloat(value, 64) |
||||
if err != nil { |
||||
return time.Time{}, err |
||||
} |
||||
return time.Unix(int64(v), 0), nil |
||||
default: |
||||
panic("unknown timestamp format name, " + formatName) |
||||
} |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,64 @@ |
||||
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
|
||||
|
||||
// Package resourcegroupstaggingapi provides the client and types for making API
|
||||
// requests to AWS Resource Groups Tagging API.
|
||||
//
|
||||
// This guide describes the API operations for the resource groups tagging.
|
||||
//
|
||||
// A tag is a label that you assign to an AWS resource. A tag consists of a
|
||||
// key and a value, both of which you define. For example, if you have two Amazon
|
||||
// EC2 instances, you might assign both a tag key of "Stack." But the value
|
||||
// of "Stack" might be "Testing" for one and "Production" for the other.
|
||||
//
|
||||
// Tagging can help you organize your resources and enables you to simplify
|
||||
// resource management, access management and cost allocation. For more information
|
||||
// about tagging, see Working with Tag Editor (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/tag-editor.html)
|
||||
// and Working with Resource Groups (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/resource-groups.html).
|
||||
// For more information about permissions you need to use the resource groups
|
||||
// tagging APIs, see Obtaining Permissions for Resource Groups (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/obtaining-permissions-for-resource-groups.html)
|
||||
// and Obtaining Permissions for Tagging (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/obtaining-permissions-for-tagging.html).
|
||||
//
|
||||
// You can use the resource groups tagging APIs to complete the following tasks:
|
||||
//
|
||||
// * Tag and untag supported resources located in the specified region for
|
||||
// the AWS account
|
||||
//
|
||||
// * Use tag-based filters to search for resources located in the specified
|
||||
// region for the AWS account
|
||||
//
|
||||
// * List all existing tag keys in the specified region for the AWS account
|
||||
//
|
||||
// * List all existing values for the specified key in the specified region
|
||||
// for the AWS account
|
||||
//
|
||||
// Not all resources can have tags. For a lists of resources that you can tag,
|
||||
// see Supported Resources (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/supported-resources.html)
|
||||
// in the AWS Resource Groups and Tag Editor User Guide.
|
||||
//
|
||||
// To make full use of the resource groups tagging APIs, you might need additional
|
||||
// IAM permissions, including permission to access the resources of individual
|
||||
// services as well as permission to view and apply tags to those resources.
|
||||
// For more information, see Obtaining Permissions for Tagging (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/obtaining-permissions-for-tagging.html)
|
||||
// in the AWS Resource Groups and Tag Editor User Guide.
|
||||
//
|
||||
// See https://docs.aws.amazon.com/goto/WebAPI/resourcegroupstaggingapi-2017-01-26 for more information on this service.
|
||||
//
|
||||
// See resourcegroupstaggingapi package documentation for more information.
|
||||
// https://docs.aws.amazon.com/sdk-for-go/api/service/resourcegroupstaggingapi/
|
||||
//
|
||||
// Using the Client
|
||||
//
|
||||
// To contact AWS Resource Groups Tagging API with the SDK use the New function to create
|
||||
// a new service client. With that client you can make API requests to the service.
|
||||
// These clients are safe to use concurrently.
|
||||
//
|
||||
// See the SDK's documentation for more information on how to use the SDK.
|
||||
// https://docs.aws.amazon.com/sdk-for-go/api/
|
||||
//
|
||||
// See aws.Config documentation for more information on configuring SDK clients.
|
||||
// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
|
||||
//
|
||||
// See the AWS Resource Groups Tagging API client ResourceGroupsTaggingAPI for more
|
||||
// information on creating client for this service.
|
||||
// https://docs.aws.amazon.com/sdk-for-go/api/service/resourcegroupstaggingapi/#New
|
||||
package resourcegroupstaggingapi |
||||
@ -0,0 +1,33 @@ |
||||
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
|
||||
|
||||
package resourcegroupstaggingapi |
||||
|
||||
const ( |
||||
|
||||
// ErrCodeInternalServiceException for service response error code
|
||||
// "InternalServiceException".
|
||||
//
|
||||
// The request processing failed because of an unknown error, exception, or
|
||||
// failure. You can retry the request.
|
||||
ErrCodeInternalServiceException = "InternalServiceException" |
||||
|
||||
// ErrCodeInvalidParameterException for service response error code
|
||||
// "InvalidParameterException".
|
||||
//
|
||||
// A parameter is missing or a malformed string or invalid or out-of-range value
|
||||
// was supplied for the request parameter.
|
||||
ErrCodeInvalidParameterException = "InvalidParameterException" |
||||
|
||||
// ErrCodePaginationTokenExpiredException for service response error code
|
||||
// "PaginationTokenExpiredException".
|
||||
//
|
||||
// A PaginationToken is valid for a maximum of 15 minutes. Your request was
|
||||
// denied because the specified PaginationToken has expired.
|
||||
ErrCodePaginationTokenExpiredException = "PaginationTokenExpiredException" |
||||
|
||||
// ErrCodeThrottledException for service response error code
|
||||
// "ThrottledException".
|
||||
//
|
||||
// The request was denied to limit the frequency of submitted requests.
|
||||
ErrCodeThrottledException = "ThrottledException" |
||||
) |
||||
@ -0,0 +1,93 @@ |
||||
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
|
||||
|
||||
// Package resourcegroupstaggingapiiface provides an interface to enable mocking the AWS Resource Groups Tagging API service client
|
||||
// for testing your code.
|
||||
//
|
||||
// It is important to note that this interface will have breaking changes
|
||||
// when the service model is updated and adds new API operations, paginators,
|
||||
// and waiters.
|
||||
package resourcegroupstaggingapiiface |
||||
|
||||
import ( |
||||
"github.com/aws/aws-sdk-go/aws" |
||||
"github.com/aws/aws-sdk-go/aws/request" |
||||
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" |
||||
) |
||||
|
||||
// ResourceGroupsTaggingAPIAPI provides an interface to enable mocking the
|
||||
// resourcegroupstaggingapi.ResourceGroupsTaggingAPI service client's API operation,
|
||||
// paginators, and waiters. This make unit testing your code that calls out
|
||||
// to the SDK's service client's calls easier.
|
||||
//
|
||||
// The best way to use this interface is so the SDK's service client's calls
|
||||
// can be stubbed out for unit testing your code with the SDK without needing
|
||||
// to inject custom request handlers into the SDK's request pipeline.
|
||||
//
|
||||
// // myFunc uses an SDK service client to make a request to
|
||||
// // AWS Resource Groups Tagging API.
|
||||
// func myFunc(svc resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI) bool {
|
||||
// // Make svc.GetResources request
|
||||
// }
|
||||
//
|
||||
// func main() {
|
||||
// sess := session.New()
|
||||
// svc := resourcegroupstaggingapi.New(sess)
|
||||
//
|
||||
// myFunc(svc)
|
||||
// }
|
||||
//
|
||||
// In your _test.go file:
|
||||
//
|
||||
// // Define a mock struct to be used in your unit tests of myFunc.
|
||||
// type mockResourceGroupsTaggingAPIClient struct {
|
||||
// resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI
|
||||
// }
|
||||
// func (m *mockResourceGroupsTaggingAPIClient) GetResources(input *resourcegroupstaggingapi.GetResourcesInput) (*resourcegroupstaggingapi.GetResourcesOutput, error) {
|
||||
// // mock response/functionality
|
||||
// }
|
||||
//
|
||||
// func TestMyFunc(t *testing.T) {
|
||||
// // Setup Test
|
||||
// mockSvc := &mockResourceGroupsTaggingAPIClient{}
|
||||
//
|
||||
// myfunc(mockSvc)
|
||||
//
|
||||
// // Verify myFunc's functionality
|
||||
// }
|
||||
//
|
||||
// It is important to note that this interface will have breaking changes
|
||||
// when the service model is updated and adds new API operations, paginators,
|
||||
// and waiters. Its suggested to use the pattern above for testing, or using
|
||||
// tooling to generate mocks to satisfy the interfaces.
|
||||
type ResourceGroupsTaggingAPIAPI interface { |
||||
GetResources(*resourcegroupstaggingapi.GetResourcesInput) (*resourcegroupstaggingapi.GetResourcesOutput, error) |
||||
GetResourcesWithContext(aws.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error) |
||||
GetResourcesRequest(*resourcegroupstaggingapi.GetResourcesInput) (*request.Request, *resourcegroupstaggingapi.GetResourcesOutput) |
||||
|
||||
GetResourcesPages(*resourcegroupstaggingapi.GetResourcesInput, func(*resourcegroupstaggingapi.GetResourcesOutput, bool) bool) error |
||||
GetResourcesPagesWithContext(aws.Context, *resourcegroupstaggingapi.GetResourcesInput, func(*resourcegroupstaggingapi.GetResourcesOutput, bool) bool, ...request.Option) error |
||||
|
||||
GetTagKeys(*resourcegroupstaggingapi.GetTagKeysInput) (*resourcegroupstaggingapi.GetTagKeysOutput, error) |
||||
GetTagKeysWithContext(aws.Context, *resourcegroupstaggingapi.GetTagKeysInput, ...request.Option) (*resourcegroupstaggingapi.GetTagKeysOutput, error) |
||||
GetTagKeysRequest(*resourcegroupstaggingapi.GetTagKeysInput) (*request.Request, *resourcegroupstaggingapi.GetTagKeysOutput) |
||||
|
||||
GetTagKeysPages(*resourcegroupstaggingapi.GetTagKeysInput, func(*resourcegroupstaggingapi.GetTagKeysOutput, bool) bool) error |
||||
GetTagKeysPagesWithContext(aws.Context, *resourcegroupstaggingapi.GetTagKeysInput, func(*resourcegroupstaggingapi.GetTagKeysOutput, bool) bool, ...request.Option) error |
||||
|
||||
GetTagValues(*resourcegroupstaggingapi.GetTagValuesInput) (*resourcegroupstaggingapi.GetTagValuesOutput, error) |
||||
GetTagValuesWithContext(aws.Context, *resourcegroupstaggingapi.GetTagValuesInput, ...request.Option) (*resourcegroupstaggingapi.GetTagValuesOutput, error) |
||||
GetTagValuesRequest(*resourcegroupstaggingapi.GetTagValuesInput) (*request.Request, *resourcegroupstaggingapi.GetTagValuesOutput) |
||||
|
||||
GetTagValuesPages(*resourcegroupstaggingapi.GetTagValuesInput, func(*resourcegroupstaggingapi.GetTagValuesOutput, bool) bool) error |
||||
GetTagValuesPagesWithContext(aws.Context, *resourcegroupstaggingapi.GetTagValuesInput, func(*resourcegroupstaggingapi.GetTagValuesOutput, bool) bool, ...request.Option) error |
||||
|
||||
TagResources(*resourcegroupstaggingapi.TagResourcesInput) (*resourcegroupstaggingapi.TagResourcesOutput, error) |
||||
TagResourcesWithContext(aws.Context, *resourcegroupstaggingapi.TagResourcesInput, ...request.Option) (*resourcegroupstaggingapi.TagResourcesOutput, error) |
||||
TagResourcesRequest(*resourcegroupstaggingapi.TagResourcesInput) (*request.Request, *resourcegroupstaggingapi.TagResourcesOutput) |
||||
|
||||
UntagResources(*resourcegroupstaggingapi.UntagResourcesInput) (*resourcegroupstaggingapi.UntagResourcesOutput, error) |
||||
UntagResourcesWithContext(aws.Context, *resourcegroupstaggingapi.UntagResourcesInput, ...request.Option) (*resourcegroupstaggingapi.UntagResourcesOutput, error) |
||||
UntagResourcesRequest(*resourcegroupstaggingapi.UntagResourcesInput) (*request.Request, *resourcegroupstaggingapi.UntagResourcesOutput) |
||||
} |
||||
|
||||
var _ ResourceGroupsTaggingAPIAPI = (*resourcegroupstaggingapi.ResourceGroupsTaggingAPI)(nil) |
||||
@ -0,0 +1,97 @@ |
||||
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
|
||||
|
||||
package resourcegroupstaggingapi |
||||
|
||||
import ( |
||||
"github.com/aws/aws-sdk-go/aws" |
||||
"github.com/aws/aws-sdk-go/aws/client" |
||||
"github.com/aws/aws-sdk-go/aws/client/metadata" |
||||
"github.com/aws/aws-sdk-go/aws/request" |
||||
"github.com/aws/aws-sdk-go/aws/signer/v4" |
||||
"github.com/aws/aws-sdk-go/private/protocol/jsonrpc" |
||||
) |
||||
|
||||
// ResourceGroupsTaggingAPI provides the API operation methods for making requests to
|
||||
// AWS Resource Groups Tagging API. See this package's package overview docs
|
||||
// for details on the service.
|
||||
//
|
||||
// ResourceGroupsTaggingAPI methods are safe to use concurrently. It is not safe to
|
||||
// modify mutate any of the struct's properties though.
|
||||
type ResourceGroupsTaggingAPI struct { |
||||
*client.Client |
||||
} |
||||
|
||||
// Used for custom client initialization logic
|
||||
var initClient func(*client.Client) |
||||
|
||||
// Used for custom request initialization logic
|
||||
var initRequest func(*request.Request) |
||||
|
||||
// Service information constants
|
||||
const ( |
||||
ServiceName = "tagging" // Name of service.
|
||||
EndpointsID = ServiceName // ID to lookup a service endpoint with.
|
||||
ServiceID = "Resource Groups Tagging API" // ServiceID is a unique identifer of a specific service.
|
||||
) |
||||
|
||||
// New creates a new instance of the ResourceGroupsTaggingAPI client with a session.
|
||||
// If additional configuration is needed for the client instance use the optional
|
||||
// aws.Config parameter to add your extra config.
|
||||
//
|
||||
// Example:
|
||||
// // Create a ResourceGroupsTaggingAPI client from just a session.
|
||||
// svc := resourcegroupstaggingapi.New(mySession)
|
||||
//
|
||||
// // Create a ResourceGroupsTaggingAPI client with additional configuration
|
||||
// svc := resourcegroupstaggingapi.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
|
||||
func New(p client.ConfigProvider, cfgs ...*aws.Config) *ResourceGroupsTaggingAPI { |
||||
c := p.ClientConfig(EndpointsID, cfgs...) |
||||
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName) |
||||
} |
||||
|
||||
// newClient creates, initializes and returns a new service client instance.
|
||||
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion, signingName string) *ResourceGroupsTaggingAPI { |
||||
svc := &ResourceGroupsTaggingAPI{ |
||||
Client: client.New( |
||||
cfg, |
||||
metadata.ClientInfo{ |
||||
ServiceName: ServiceName, |
||||
ServiceID: ServiceID, |
||||
SigningName: signingName, |
||||
SigningRegion: signingRegion, |
||||
Endpoint: endpoint, |
||||
APIVersion: "2017-01-26", |
||||
JSONVersion: "1.1", |
||||
TargetPrefix: "ResourceGroupsTaggingAPI_20170126", |
||||
}, |
||||
handlers, |
||||
), |
||||
} |
||||
|
||||
// Handlers
|
||||
svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler) |
||||
svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler) |
||||
svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler) |
||||
svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler) |
||||
svc.Handlers.UnmarshalError.PushBackNamed(jsonrpc.UnmarshalErrorHandler) |
||||
|
||||
// Run custom client initialization if present
|
||||
if initClient != nil { |
||||
initClient(svc.Client) |
||||
} |
||||
|
||||
return svc |
||||
} |
||||
|
||||
// newRequest creates a new request for a ResourceGroupsTaggingAPI operation and runs any
|
||||
// custom request initialization.
|
||||
func (c *ResourceGroupsTaggingAPI) newRequest(op *request.Operation, params, data interface{}) *request.Request { |
||||
req := c.NewRequest(op, params, data) |
||||
|
||||
// Run custom request initialization if present
|
||||
if initRequest != nil { |
||||
initRequest(req) |
||||
} |
||||
|
||||
return req |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue