mirror of
https://github.com/yannh/kubeconform.git
synced 2026-02-20 18:37:01 +00:00
Merge pull request #8 from yannh/minor-fixes-and-tests
Minor fixes and tests
This commit is contained in:
commit
98b8de29ba
12 changed files with 555 additions and 39 deletions
|
|
@ -12,16 +12,28 @@
|
||||||
[ "$output" = "Summary: 7 resources found in 2 files - Valid: 7, Invalid: 0, Errors: 0 Skipped: 0" ]
|
[ "$output" = "Summary: 7 resources found in 2 files - Valid: 7, Invalid: 0, Errors: 0 Skipped: 0" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "Pass when parsing a valid Kubernetes config file with int_to_string vars" {
|
||||||
|
run bin/kubeconform -verbose fixtures/int_or_string.yaml
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" = "fixtures/int_or_string.yaml - Service heapster is valid" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Pass when parsing a valid Kubernetes config YAML file with generate name" {
|
||||||
|
run bin/kubeconform -verbose fixtures/generate_name.yaml
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" = "fixtures/generate_name.yaml - Job pi-{{ generateName }} is valid" ]
|
||||||
|
}
|
||||||
|
|
||||||
@test "Pass when parsing a Kubernetes file with string and integer quantities" {
|
@test "Pass when parsing a Kubernetes file with string and integer quantities" {
|
||||||
run bin/kubeconform -verbose fixtures/quantity.yaml
|
run bin/kubeconform -verbose fixtures/quantity.yaml
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[ "$output" = "fixtures/quantity.yaml - LimitRange is valid" ]
|
[ "$output" = "fixtures/quantity.yaml - LimitRange mem-limit-range is valid" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "Pass when parsing a valid Kubernetes config file with null arrays" {
|
@test "Pass when parsing a valid Kubernetes config file with null arrays" {
|
||||||
run bin/kubeconform -verbose fixtures/null_string.yaml
|
run bin/kubeconform -verbose fixtures/null_string.yaml
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[ "$output" = "fixtures/null_string.yaml - Service is valid" ]
|
[ "$output" = "fixtures/null_string.yaml - Service frontend is valid" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "Pass when parsing a multi-document config file" {
|
@test "Pass when parsing a multi-document config file" {
|
||||||
|
|
@ -43,7 +55,19 @@
|
||||||
@test "Return relevant error for non-existent file" {
|
@test "Return relevant error for non-existent file" {
|
||||||
run bin/kubeconform fixtures/not-here
|
run bin/kubeconform fixtures/not-here
|
||||||
[ "$status" -eq 1 ]
|
[ "$status" -eq 1 ]
|
||||||
[ "$output" = "fixtures/not-here - failed validation: open fixtures/not-here: no such file or directory" ]
|
[ "$output" = "fixtures/not-here - failed validation: open fixtures/not-here: no such file or directory" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Pass when parsing a blank config file" {
|
||||||
|
run bin/kubeconform -summary fixtures/blank.yaml
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" = "Summary: 0 resource found in 1 file - Valid: 0, Invalid: 0, Errors: 0 Skipped: 0" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Pass when parsing a blank config file with a comment" {
|
||||||
|
run bin/kubeconform -summary fixtures/comment.yaml
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" = "Summary: 0 resource found in 1 file - Valid: 0, Invalid: 0, Errors: 0 Skipped: 0" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "Fail when parsing a config with additional properties and strict set" {
|
@test "Fail when parsing a config with additional properties and strict set" {
|
||||||
|
|
@ -65,3 +89,29 @@
|
||||||
run bin/kubeconform -registry './fixtures/registry/{{ .ResourceKind }}{{ .KindSuffix }}.json' fixtures/test_crd.yaml
|
run bin/kubeconform -registry './fixtures/registry/{{ .ResourceKind }}{{ .KindSuffix }}.json' fixtures/test_crd.yaml
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "Pass when parsing a config with additional properties" {
|
||||||
|
run bin/kubeconform -summary fixtures/extra_property.yaml
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" = "Summary: 1 resource found in 1 file - Valid: 1, Invalid: 0, Errors: 0 Skipped: 0" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Pass when using a valid, preset --registry" {
|
||||||
|
run bin/kubeconform --registry kubernetesjsonschema.dev fixtures/valid.yaml
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Pass when using a valid HTTP --registry" {
|
||||||
|
run bin/kubeconform --registry 'https://kubernetesjsonschema.dev/{{ .NormalizedVersion }}-standalone{{ .StrictSuffix }}/{{ .ResourceKind }}{{ .KindSuffix }}.json' fixtures/valid.yaml
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Fail when using an invalid HTTP --registry" {
|
||||||
|
run bin/kubeconform --registry 'http://foo' fixtures/valid.yaml
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Fail when using an invalid non-HTTP --registry" {
|
||||||
|
run bin/kubeconform --registry 'foo' fixtures/valid.yaml
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type validationResult struct {
|
type validationResult struct {
|
||||||
filename, kind, version string
|
filename, kind, version, Name string
|
||||||
err error
|
err error
|
||||||
skipped bool
|
skipped bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourcesFromReader(r io.Reader) ([][]byte, error) {
|
func resourcesFromReader(r io.Reader) ([][]byte, error) {
|
||||||
|
|
@ -65,21 +65,27 @@ func ValidateStream(r io.Reader, regs []registry.Registry, k8sVersion string, c
|
||||||
return []validationResult{{err: fmt.Errorf("failed reading file: %s", err)}}
|
return []validationResult{{err: fmt.Errorf("failed reading file: %s", err)}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
validationResults := []validationResult{}
|
validationResults := []validationResult{}
|
||||||
|
if len(rawResources) == 0 {
|
||||||
|
// In case a file has no resources, we want to capture that the file was parsed - and therefore send a message with an empty resource and no error
|
||||||
|
validationResults = append(validationResults, validationResult{kind: "", version: "", Name: "", err: nil, skipped: false})
|
||||||
|
}
|
||||||
|
|
||||||
for _, rawResource := range rawResources {
|
for _, rawResource := range rawResources {
|
||||||
var sig resource.Signature
|
var sig resource.Signature
|
||||||
if sig, err = resource.SignatureFromBytes(rawResource); err != nil {
|
if sig, err = resource.SignatureFromBytes(rawResource); err != nil {
|
||||||
validationResults = append(validationResults, validationResult{kind: sig.Kind, version: sig.Version, err: fmt.Errorf("error while parsing: %s", err)})
|
validationResults = append(validationResults, validationResult{kind: sig.Kind, version: sig.Version, Name: sig.Name, err: fmt.Errorf("error while parsing: %s", err)})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if sig.Kind == "" {
|
if sig.Kind == "" {
|
||||||
|
validationResults = append(validationResults, validationResult{kind: "", version: "", Name: "", err: nil, skipped: false})
|
||||||
continue // We skip resoures that don't have a Kind defined
|
continue // We skip resoures that don't have a Kind defined
|
||||||
}
|
}
|
||||||
|
|
||||||
if skip(sig) {
|
if skip(sig) {
|
||||||
validationResults = append(validationResults, validationResult{kind: sig.Kind, version: sig.Version, err: nil, skipped: true})
|
validationResults = append(validationResults, validationResult{kind: sig.Kind, version: sig.Version, Name: sig.Name, err: nil, skipped: true})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,7 +101,7 @@ func ValidateStream(r io.Reader, regs []registry.Registry, k8sVersion string, c
|
||||||
if !ok {
|
if !ok {
|
||||||
schema, err = downloadSchema(regs, sig.Kind, sig.Version, k8sVersion)
|
schema, err = downloadSchema(regs, sig.Kind, sig.Version, k8sVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
validationResults = append(validationResults, validationResult{kind: sig.Kind, version: sig.Version, err: err, skipped: false})
|
validationResults = append(validationResults, validationResult{kind: sig.Kind, version: sig.Version, Name: sig.Name, err: err, skipped: false})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,14 +112,14 @@ func ValidateStream(r io.Reader, regs []registry.Registry, k8sVersion string, c
|
||||||
|
|
||||||
if schema == nil {
|
if schema == nil {
|
||||||
if ignoreMissingSchemas {
|
if ignoreMissingSchemas {
|
||||||
validationResults = append(validationResults, validationResult{kind: sig.Kind, version: sig.Version, err: nil, skipped: true})
|
validationResults = append(validationResults, validationResult{kind: sig.Kind, version: sig.Version, Name: sig.Name, err: nil, skipped: true})
|
||||||
} else {
|
} else {
|
||||||
validationResults = append(validationResults, validationResult{kind: sig.Kind, version: sig.Version, err: fmt.Errorf("could not find schema for %s", sig.Kind), skipped: false})
|
validationResults = append(validationResults, validationResult{kind: sig.Kind, version: sig.Version, Name: sig.Name, err: fmt.Errorf("could not find schema for %s", sig.Kind), skipped: false})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validator.Validate(rawResource, schema)
|
err = validator.Validate(rawResource, schema)
|
||||||
validationResults = append(validationResults, validationResult{kind: sig.Kind, version: sig.Version, err: err})
|
validationResults = append(validationResults, validationResult{kind: sig.Kind, version: sig.Version, Name: sig.Name, err: err})
|
||||||
}
|
}
|
||||||
|
|
||||||
return validationResults
|
return validationResults
|
||||||
|
|
@ -162,7 +168,7 @@ func processResults(o output.Output, validationResults chan []validationResult,
|
||||||
success = false
|
success = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := o.Write(result.filename, result.kind, result.version, result.err, result.skipped); err != nil {
|
if err := o.Write(result.filename, result.kind, result.Name, result.version, result.err, result.skipped); err != nil {
|
||||||
fmt.Fprint(os.Stderr, "failed writing log\n")
|
fmt.Fprint(os.Stderr, "failed writing log\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
---
|
---
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: apps/v1
|
||||||
kind: DaemonSet
|
kind: DaemonSet
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx-ds
|
name: nginx-ds
|
||||||
spec:
|
spec:
|
||||||
replicas: 2
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
k8s-app: nginx-ds
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
|
|
|
||||||
13
fixtures/generate_name.yaml
Normal file
13
fixtures/generate_name.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
generateName: pi-
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: pi
|
||||||
|
image: perl
|
||||||
|
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
|
||||||
|
restartPolicy: Never
|
||||||
|
backoffLimit: 4
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: apps/v1
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
k8s-app: kubernetes-dashboard
|
k8s-app: kubernetes-dashboard
|
||||||
|
|
|
||||||
418
fixtures/registry/sagemaker.aws.amazon.com_trainingjobs.yaml
Normal file
418
fixtures/registry/sagemaker.aws.amazon.com_trainingjobs.yaml
Normal file
|
|
@ -0,0 +1,418 @@
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.3.0
|
||||||
|
creationTimestamp: null
|
||||||
|
name: trainingjobs.sagemaker.aws.amazon.com
|
||||||
|
spec:
|
||||||
|
additionalPrinterColumns:
|
||||||
|
- JSONPath: .status.trainingJobStatus
|
||||||
|
name: Status
|
||||||
|
type: string
|
||||||
|
- JSONPath: .status.secondaryStatus
|
||||||
|
name: Secondary-Status
|
||||||
|
type: string
|
||||||
|
- JSONPath: .metadata.creationTimestamp
|
||||||
|
format: date
|
||||||
|
name: Creation-Time
|
||||||
|
type: string
|
||||||
|
- JSONPath: .status.sageMakerTrainingJobName
|
||||||
|
name: Sagemaker-Job-Name
|
||||||
|
type: string
|
||||||
|
group: sagemaker.aws.amazon.com
|
||||||
|
names:
|
||||||
|
kind: TrainingJob
|
||||||
|
listKind: TrainingJobList
|
||||||
|
plural: trainingjobs
|
||||||
|
singular: trainingjob
|
||||||
|
scope: Namespaced
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
|
validation:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: TrainingJob is the Schema for the trainingjobs API
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||||
|
type: string
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
spec:
|
||||||
|
description: TrainingJobSpec defines the desired state of TrainingJob
|
||||||
|
properties:
|
||||||
|
algorithmSpecification:
|
||||||
|
properties:
|
||||||
|
algorithmName:
|
||||||
|
minLength: 1
|
||||||
|
type: string
|
||||||
|
metricDefinitions:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
minLength: 1
|
||||||
|
type: string
|
||||||
|
regex:
|
||||||
|
minLength: 1
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- regex
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
trainingImage:
|
||||||
|
minLength: 1
|
||||||
|
type: string
|
||||||
|
trainingInputMode:
|
||||||
|
enum:
|
||||||
|
- File
|
||||||
|
- Pipe
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- trainingInputMode
|
||||||
|
type: object
|
||||||
|
checkpointConfig:
|
||||||
|
properties:
|
||||||
|
localPath:
|
||||||
|
type: string
|
||||||
|
s3Uri:
|
||||||
|
pattern: ^(https|s3)://([^/]+)/?(.*)$
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- s3Uri
|
||||||
|
type: object
|
||||||
|
debugHookConfig:
|
||||||
|
description: DebugHookConfig https://docs.aws.amazon.com/sagemaker/latest/dg/API_DebugHookConfig.html
|
||||||
|
properties:
|
||||||
|
collectionConfigurations:
|
||||||
|
items:
|
||||||
|
description: CollectionConfiguration https://docs.aws.amazon.com/sagemaker/latest/dg/API_CollectionConfiguration.html
|
||||||
|
properties:
|
||||||
|
collectionName:
|
||||||
|
type: string
|
||||||
|
collectionParameters:
|
||||||
|
items:
|
||||||
|
description: Used in describing maps in Kubernetes.
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
localPath:
|
||||||
|
type: string
|
||||||
|
ruleParameters:
|
||||||
|
items:
|
||||||
|
description: Used in describing maps in Kubernetes.
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
s3OutputPath:
|
||||||
|
pattern: ^(https|s3)://([^/]+)/?(.*)$
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- s3OutputPath
|
||||||
|
type: object
|
||||||
|
debugRuleConfigurations:
|
||||||
|
items:
|
||||||
|
description: DebugRuleConfiguration https://docs.aws.amazon.com/sagemaker/latest/dg/API_DebugRuleConfiguration.html
|
||||||
|
properties:
|
||||||
|
instanceType:
|
||||||
|
type: string
|
||||||
|
localPath:
|
||||||
|
type: string
|
||||||
|
ruleConfigurationName:
|
||||||
|
type: string
|
||||||
|
ruleEvaluatorImage:
|
||||||
|
type: string
|
||||||
|
ruleParameters:
|
||||||
|
items:
|
||||||
|
description: Used in describing maps in Kubernetes.
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
s3OutputPath:
|
||||||
|
pattern: ^(https|s3)://([^/]+)/?(.*)$
|
||||||
|
type: string
|
||||||
|
volumeSizeInGB:
|
||||||
|
format: int64
|
||||||
|
minimum: 1
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- ruleConfigurationName
|
||||||
|
- ruleEvaluatorImage
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
enableInterContainerTrafficEncryption:
|
||||||
|
type: boolean
|
||||||
|
enableManagedSpotTraining:
|
||||||
|
type: boolean
|
||||||
|
enableNetworkIsolation:
|
||||||
|
type: boolean
|
||||||
|
hyperParameters:
|
||||||
|
items:
|
||||||
|
description: Used in describing maps in Kubernetes.
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
inputDataConfig:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
channelName:
|
||||||
|
minLength: 1
|
||||||
|
pattern: '[A-Za-z0-9\.\-_]+'
|
||||||
|
type: string
|
||||||
|
compressionType:
|
||||||
|
enum:
|
||||||
|
- None
|
||||||
|
- Gzip
|
||||||
|
type: string
|
||||||
|
contentType:
|
||||||
|
type: string
|
||||||
|
dataSource:
|
||||||
|
properties:
|
||||||
|
fileSystemDataSource:
|
||||||
|
properties:
|
||||||
|
directoryPath:
|
||||||
|
type: string
|
||||||
|
fileSystemAccessMode:
|
||||||
|
type: string
|
||||||
|
fileSystemId:
|
||||||
|
type: string
|
||||||
|
fileSystemType:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- directoryPath
|
||||||
|
- fileSystemAccessMode
|
||||||
|
- fileSystemId
|
||||||
|
- fileSystemType
|
||||||
|
type: object
|
||||||
|
s3DataSource:
|
||||||
|
properties:
|
||||||
|
attributeNames:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
s3DataDistributionType:
|
||||||
|
enum:
|
||||||
|
- FullyReplicated
|
||||||
|
- ShardedByS3Key
|
||||||
|
type: string
|
||||||
|
s3DataType:
|
||||||
|
enum:
|
||||||
|
- S3Prefix
|
||||||
|
- ManifestFile
|
||||||
|
- AugmentedManifestFile
|
||||||
|
type: string
|
||||||
|
s3Uri:
|
||||||
|
pattern: ^(https|s3)://([^/]+)/?(.*)$
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- s3DataType
|
||||||
|
- s3Uri
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
inputMode:
|
||||||
|
enum:
|
||||||
|
- Pipe
|
||||||
|
- File
|
||||||
|
type: string
|
||||||
|
recordWrapperType:
|
||||||
|
type: string
|
||||||
|
shuffleConfig:
|
||||||
|
properties:
|
||||||
|
seed:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- seed
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- channelName
|
||||||
|
- dataSource
|
||||||
|
type: object
|
||||||
|
minItems: 1
|
||||||
|
type: array
|
||||||
|
outputDataConfig:
|
||||||
|
properties:
|
||||||
|
kmsKeyId:
|
||||||
|
type: string
|
||||||
|
s3OutputPath:
|
||||||
|
pattern: ^(https|s3)://([^/]+)/?(.*)$
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- s3OutputPath
|
||||||
|
type: object
|
||||||
|
region:
|
||||||
|
minLength: 1
|
||||||
|
type: string
|
||||||
|
resourceConfig:
|
||||||
|
properties:
|
||||||
|
instanceCount:
|
||||||
|
format: int64
|
||||||
|
minimum: 1
|
||||||
|
type: integer
|
||||||
|
instanceType:
|
||||||
|
minLength: 1
|
||||||
|
type: string
|
||||||
|
volumeKmsKeyId:
|
||||||
|
type: string
|
||||||
|
volumeSizeInGB:
|
||||||
|
format: int64
|
||||||
|
minimum: 1
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- instanceCount
|
||||||
|
- instanceType
|
||||||
|
- volumeSizeInGB
|
||||||
|
type: object
|
||||||
|
roleArn:
|
||||||
|
minLength: 20
|
||||||
|
type: string
|
||||||
|
sageMakerEndpoint:
|
||||||
|
description: A custom SageMaker endpoint to use when communicating with SageMaker.
|
||||||
|
pattern: ^(https|http)://.*$
|
||||||
|
type: string
|
||||||
|
stoppingCondition:
|
||||||
|
properties:
|
||||||
|
maxRuntimeInSeconds:
|
||||||
|
format: int64
|
||||||
|
minimum: 1
|
||||||
|
type: integer
|
||||||
|
maxWaitTimeInSeconds:
|
||||||
|
format: int64
|
||||||
|
minimum: 1
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
tags:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
minLength: 1
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- key
|
||||||
|
- value
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
tensorBoardOutputConfig:
|
||||||
|
description: TensorBoardOutputConfig https://docs.aws.amazon.com/sagemaker/latest/dg/API_TensorBoardOutputConfig.html
|
||||||
|
properties:
|
||||||
|
localPath:
|
||||||
|
type: string
|
||||||
|
s3OutputPath:
|
||||||
|
pattern: ^(https|s3)://([^/]+)/?(.*)$
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- s3OutputPath
|
||||||
|
type: object
|
||||||
|
trainingJobName:
|
||||||
|
description: The SageMaker training job name. This is optional for the SageMaker K8s operator. If it is empty, the operator will populate it with a generated name.
|
||||||
|
maxLength: 63
|
||||||
|
type: string
|
||||||
|
vpcConfig:
|
||||||
|
properties:
|
||||||
|
securityGroupIds:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
maxItems: 5
|
||||||
|
minItems: 1
|
||||||
|
type: array
|
||||||
|
subnets:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
maxItems: 16
|
||||||
|
minItems: 1
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- securityGroupIds
|
||||||
|
- subnets
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- algorithmSpecification
|
||||||
|
- outputDataConfig
|
||||||
|
- region
|
||||||
|
- resourceConfig
|
||||||
|
- roleArn
|
||||||
|
- stoppingCondition
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: TrainingJobStatus defines the observed state of TrainingJob
|
||||||
|
properties:
|
||||||
|
additional:
|
||||||
|
description: Field to store additional information, for example if we are unable to check the status we update this.
|
||||||
|
type: string
|
||||||
|
cloudWatchLogUrl:
|
||||||
|
description: Cloud Watch url for training log
|
||||||
|
type: string
|
||||||
|
debugRuleEvaluationStatuses:
|
||||||
|
description: Status of rule evaluation jobs, obtained from DebugRuleEvaluationStatuses. https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_DescribeTrainingJob.html#sagemaker-DescribeTrainingJob-response-DebugRuleEvaluationStatuses
|
||||||
|
items:
|
||||||
|
description: DebugRuleEvaluationStatus https://docs.aws.amazon.com/sagemaker/latest/dg/API_DebugRuleEvaluationStatus.html
|
||||||
|
properties:
|
||||||
|
lastModifiedTime:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
ruleConfigurationName:
|
||||||
|
type: string
|
||||||
|
ruleEvaluationJobArn:
|
||||||
|
type: string
|
||||||
|
ruleEvaluationStatus:
|
||||||
|
type: string
|
||||||
|
statusDetail:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
lastCheckTime:
|
||||||
|
description: The last time that we checked the status of the SageMaker job.
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
modelPath:
|
||||||
|
description: Full path to the training artifact (model)
|
||||||
|
type: string
|
||||||
|
sageMakerTrainingJobName:
|
||||||
|
description: SageMaker training job name
|
||||||
|
type: string
|
||||||
|
secondaryStatus:
|
||||||
|
description: The secondary, more granular status of the training job. https://docs.aws.amazon.com/sagemaker/latest/dg/API_DescribeTrainingJob.html#SageMaker-DescribeTrainingJob-response-SecondaryStatus
|
||||||
|
type: string
|
||||||
|
trainingJobStatus:
|
||||||
|
description: The status of the training job. https://docs.aws.amazon.com/sagemaker/latest/dg/API_DescribeTrainingJob.html#SageMaker-DescribeTrainingJob-response-TrainingJobStatus
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- spec
|
||||||
|
type: object
|
||||||
|
version: v1
|
||||||
|
versions:
|
||||||
|
- name: v1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
status:
|
||||||
|
acceptedNames:
|
||||||
|
kind: ""
|
||||||
|
plural: ""
|
||||||
|
conditions: []
|
||||||
|
storedVersions: []
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
type result struct {
|
type result struct {
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
Kind string `json:"kind"`
|
Kind string `json:"kind"`
|
||||||
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Msg string `json:"msg"`
|
Msg string `json:"msg"`
|
||||||
|
|
@ -37,10 +38,10 @@ func JSON(w io.Writer, withSummary bool, verbose bool) Output {
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON.Write will only write when JSON.Flush has been called
|
// JSON.Write will only write when JSON.Flush has been called
|
||||||
func (o *jsono) Write(filename, kind, version string, err error, skipped bool) error {
|
func (o *jsono) Write(filename, kind, name, version string, err error, skipped bool) error {
|
||||||
msg, st := "", ""
|
msg, st := "", ""
|
||||||
|
|
||||||
s := status(err, skipped)
|
s := status(kind, name, err, skipped)
|
||||||
|
|
||||||
switch s {
|
switch s {
|
||||||
case VALID:
|
case VALID:
|
||||||
|
|
@ -57,10 +58,11 @@ func (o *jsono) Write(filename, kind, version string, err error, skipped bool) e
|
||||||
case SKIPPED:
|
case SKIPPED:
|
||||||
st = "SKIPPED"
|
st = "SKIPPED"
|
||||||
o.nSkipped++
|
o.nSkipped++
|
||||||
|
case EMPTY:
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.verbose || (s != VALID && s != SKIPPED) {
|
if o.verbose || (s != VALID && s != SKIPPED && s != EMPTY ) {
|
||||||
o.results = append(o.results, result{Filename: filename, Kind: kind, Version: version, Status: st, Msg: msg})
|
o.results = append(o.results, result{Filename: filename, Kind: kind, Name: name, Version: version, Status: st, Msg: msg})
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ import (
|
||||||
|
|
||||||
func TestJSONWrite(t *testing.T) {
|
func TestJSONWrite(t *testing.T) {
|
||||||
type result struct {
|
type result struct {
|
||||||
fileName, kind, version string
|
fileName, kind, name, version string
|
||||||
err error
|
err error
|
||||||
skipped bool
|
skipped bool
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range []struct {
|
for _, testCase := range []struct {
|
||||||
|
|
@ -28,6 +28,7 @@ func TestJSONWrite(t *testing.T) {
|
||||||
{
|
{
|
||||||
"deployment.yml",
|
"deployment.yml",
|
||||||
"Deployment",
|
"Deployment",
|
||||||
|
"my-app",
|
||||||
"apps/v1",
|
"apps/v1",
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
|
|
@ -46,6 +47,7 @@ func TestJSONWrite(t *testing.T) {
|
||||||
{
|
{
|
||||||
"deployment.yml",
|
"deployment.yml",
|
||||||
"Deployment",
|
"Deployment",
|
||||||
|
"my-app",
|
||||||
"apps/v1",
|
"apps/v1",
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
|
|
@ -70,6 +72,7 @@ func TestJSONWrite(t *testing.T) {
|
||||||
{
|
{
|
||||||
"deployment.yml",
|
"deployment.yml",
|
||||||
"Deployment",
|
"Deployment",
|
||||||
|
"my-app",
|
||||||
"apps/v1",
|
"apps/v1",
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
|
|
@ -80,6 +83,7 @@ func TestJSONWrite(t *testing.T) {
|
||||||
{
|
{
|
||||||
"filename": "deployment.yml",
|
"filename": "deployment.yml",
|
||||||
"kind": "Deployment",
|
"kind": "Deployment",
|
||||||
|
"name": "my-app",
|
||||||
"version": "apps/v1",
|
"version": "apps/v1",
|
||||||
"status": "VALID",
|
"status": "VALID",
|
||||||
"msg": ""
|
"msg": ""
|
||||||
|
|
@ -99,7 +103,7 @@ func TestJSONWrite(t *testing.T) {
|
||||||
o := JSON(w, testCase.withSummary, testCase.verbose)
|
o := JSON(w, testCase.withSummary, testCase.verbose)
|
||||||
|
|
||||||
for _, res := range testCase.res {
|
for _, res := range testCase.res {
|
||||||
o.Write(res.fileName, res.kind, res.version, res.err, res.skipped)
|
o.Write(res.fileName, res.kind, res.name, res.version, res.err, res.skipped)
|
||||||
}
|
}
|
||||||
o.Flush()
|
o.Flush()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,19 @@ const (
|
||||||
INVALID
|
INVALID
|
||||||
ERROR
|
ERROR
|
||||||
SKIPPED
|
SKIPPED
|
||||||
|
EMPTY
|
||||||
)
|
)
|
||||||
|
|
||||||
type Output interface {
|
type Output interface {
|
||||||
Write(filename, kind, version string, err error, skipped bool) error
|
Write(filename, kind, name, version string, err error, skipped bool) error
|
||||||
Flush() error
|
Flush() error
|
||||||
}
|
}
|
||||||
|
|
||||||
func status(err error, skipped bool) int {
|
func status(kind, name string, err error, skipped bool) int {
|
||||||
|
if name == "" && kind == "" && err == nil && skipped == false {
|
||||||
|
return EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
if skipped {
|
if skipped {
|
||||||
return SKIPPED
|
return SKIPPED
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,30 +29,35 @@ func Text(w io.Writer, withSummary, verbose bool) Output {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *text) Write(filename, kind, version string, reserr error, skipped bool) error {
|
func (o *text) Write(filename, kind, name, version string, reserr error, skipped bool) error {
|
||||||
o.Lock()
|
o.Lock()
|
||||||
defer o.Unlock()
|
defer o.Unlock()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
o.files[filename] = true
|
o.files[filename] = true
|
||||||
switch status(reserr, skipped) {
|
switch status(kind, name, reserr, skipped) {
|
||||||
case VALID:
|
case VALID:
|
||||||
if o.verbose {
|
if o.verbose {
|
||||||
_, err = fmt.Fprintf(o.w, "%s - %s is valid\n", filename, kind)
|
_, err = fmt.Fprintf(o.w, "%s - %s %s is valid\n", filename, kind, name)
|
||||||
}
|
}
|
||||||
o.nValid++
|
o.nValid++
|
||||||
case INVALID:
|
case INVALID:
|
||||||
_, err = fmt.Fprintf(o.w, "%s - %s is invalid: %s\n", filename, kind, reserr)
|
_, err = fmt.Fprintf(o.w, "%s - %s %s is invalid: %s\n", filename, kind, name, reserr)
|
||||||
o.nInvalid++
|
o.nInvalid++
|
||||||
case ERROR:
|
case ERROR:
|
||||||
_, err = fmt.Fprintf(o.w, "%s - %s failed validation: %s\n", filename, kind, reserr)
|
if kind != "" && name != "" {
|
||||||
|
_, err = fmt.Fprintf(o.w, "%s - %s %s failed validation: %s\n", filename, kind, name, reserr)
|
||||||
|
} else {
|
||||||
|
_, err = fmt.Fprintf(o.w, "%s - failed validation: %s\n", filename, reserr)
|
||||||
|
}
|
||||||
o.nErrors++
|
o.nErrors++
|
||||||
case SKIPPED:
|
case SKIPPED:
|
||||||
if o.verbose {
|
if o.verbose {
|
||||||
_, err = fmt.Fprintf(o.w, "%s - %s skipped\n", filename, kind)
|
_, err = fmt.Fprintf(o.w, "%s - %s %s skipped\n", filename, name, kind)
|
||||||
}
|
}
|
||||||
o.nSkipped++
|
o.nSkipped++
|
||||||
|
case EMPTY: // sent to ensure we count the filename as parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ import (
|
||||||
|
|
||||||
func TestTextWrite(t *testing.T) {
|
func TestTextWrite(t *testing.T) {
|
||||||
type result struct {
|
type result struct {
|
||||||
fileName, kind, version string
|
fileName, kind, name, version string
|
||||||
err error
|
err error
|
||||||
skipped bool
|
skipped bool
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range []struct {
|
for _, testCase := range []struct {
|
||||||
|
|
@ -28,6 +28,7 @@ func TestTextWrite(t *testing.T) {
|
||||||
{
|
{
|
||||||
"deployment.yml",
|
"deployment.yml",
|
||||||
"Deployment",
|
"Deployment",
|
||||||
|
"my-app",
|
||||||
"apps/v1",
|
"apps/v1",
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
|
|
@ -43,6 +44,7 @@ func TestTextWrite(t *testing.T) {
|
||||||
{
|
{
|
||||||
"deployment.yml",
|
"deployment.yml",
|
||||||
"Deployment",
|
"Deployment",
|
||||||
|
"my-app",
|
||||||
"apps/v1",
|
"apps/v1",
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
|
|
@ -58,12 +60,13 @@ func TestTextWrite(t *testing.T) {
|
||||||
{
|
{
|
||||||
"deployment.yml",
|
"deployment.yml",
|
||||||
"Deployment",
|
"Deployment",
|
||||||
|
"my-app",
|
||||||
"apps/v1",
|
"apps/v1",
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
`deployment.yml - Deployment is valid
|
`deployment.yml - Deployment my-app is valid
|
||||||
Summary: 1 resource found in 1 file - Valid: 1, Invalid: 0, Errors: 0 Skipped: 0
|
Summary: 1 resource found in 1 file - Valid: 1, Invalid: 0, Errors: 0 Skipped: 0
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
|
@ -72,7 +75,7 @@ Summary: 1 resource found in 1 file - Valid: 1, Invalid: 0, Errors: 0 Skipped: 0
|
||||||
o := Text(w, testCase.withSummary, testCase.verbose)
|
o := Text(w, testCase.withSummary, testCase.verbose)
|
||||||
|
|
||||||
for _, res := range testCase.res {
|
for _, res := range testCase.res {
|
||||||
o.Write(res.fileName, res.kind, res.version, res.err, res.skipped)
|
o.Write(res.fileName, res.kind, res.name, res.version, res.err, res.skipped)
|
||||||
}
|
}
|
||||||
o.Flush()
|
o.Flush()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Signature struct {
|
type Signature struct {
|
||||||
Kind, Version, Namespace string
|
Kind, Version, Namespace, Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignatureFromBytes returns key identifying elements from a []byte representing the resource
|
// SignatureFromBytes returns key identifying elements from a []byte representing the resource
|
||||||
|
|
@ -14,10 +14,17 @@ func SignatureFromBytes(res []byte) (Signature, error) {
|
||||||
APIVersion string `yaml:"apiVersion"`
|
APIVersion string `yaml:"apiVersion"`
|
||||||
Kind string `yaml:"kind"`
|
Kind string `yaml:"kind"`
|
||||||
Metadata struct {
|
Metadata struct {
|
||||||
Namespace string `yaml:"Namespace"`
|
Name string `yaml:"name"`
|
||||||
|
Namespace string `yaml:"namespace"`
|
||||||
|
GenerateName string `yaml:"generateName"`
|
||||||
} `yaml:"Metadata"`
|
} `yaml:"Metadata"`
|
||||||
}{}
|
}{}
|
||||||
err := yaml.Unmarshal(res, &resource)
|
err := yaml.Unmarshal(res, &resource)
|
||||||
|
|
||||||
return Signature{Kind: resource.Kind, Version: resource.APIVersion, Namespace: resource.Metadata.Namespace}, err
|
name := resource.Metadata.Name
|
||||||
|
if resource.Metadata.GenerateName != "" {
|
||||||
|
name = resource.Metadata.GenerateName + "{{ generateName }}"
|
||||||
|
}
|
||||||
|
|
||||||
|
return Signature{Kind: resource.Kind, Version: resource.APIVersion, Namespace: resource.Metadata.Namespace, Name: name}, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue