Merge pull request #6 from yannh/minor-fixes-and-tests

Minor fixes and tests
This commit is contained in:
Yann Hamon 2020-10-17 17:23:09 +02:00 committed by GitHub
commit 33fb52241a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 590 additions and 10 deletions

View file

@ -9,8 +9,9 @@ configuration using the schemas from the registry maintained by the
It is inspired by and similar to [Kubeval](https://github.com/instrumenta/kubeval), but with the
following improvements:
* **high performance**: will validate & download manifests over multiple routines
* support for **Kubernetes CRDs**
* **high performance**: will validate & download manifests over multiple routines, caching
downloaded files in memory
* configurable list of schemas registries, enabling validating Kubernetes custom resources (CRDs)
### A small overview of Kubernetes manifest validation
@ -117,6 +118,16 @@ in a local folder - for example schemas. Then we specify this folder as an addit
$ ./bin/kubeconform -registry kubernetesjsonschema.dev -registry 'schemas/{{ .ResourceKind }}{{ .KindSuffix }}.json' fixtures/custom-resource.yaml
```
### Generating a JSON schema from an OpenAPI file
Kubeconform uses JSON schemas to validate Kubernetes resources. For Custom Resource, the CustomResourceDefinition
first needs to be converted to JSON Schema. A script is provided to convert these CustomResourceDefinitions
to JSON schema. Here is an example how to use it:
```
$ ./cmd/openapi2jsonschema/main.py https://raw.githubusercontent.com/aws/amazon-sagemaker-operator-for-k8s/master/config/crd/bases/sagemaker.aws.amazon.com_trainingjobs.yaml > fixtures/registry/trainingjob-sagemaker-v1.json
```
### Credits
* @garethr for the [Kubeval](https://github.com/instrumenta/kubeval) and

View file

@ -56,8 +56,12 @@
[ "$status" -eq 1 ]
}
@test "Pass when parsing a config with CRD and ignoring missing schemas" {
@test "Pass when parsing a config with Custom Resource and ignoring missing schemas" {
run bin/kubeconform -ignore-missing-schemas fixtures/test_crd.yaml
[ "$status" -eq 0 ]
}
@test "Pass when parsing a Custom Resource and using a local schema registry with appropriate CRD" {
run bin/kubeconform -registry './fixtures/registry/{{ .ResourceKind }}{{ .KindSuffix }}.json' fixtures/test_crd.yaml
[ "$status" -eq 0 ]
}

View file

@ -263,7 +263,7 @@ func realMain() int {
var o output.Output
if o, err = getLogger(outputFormat, summary, verbose); err != nil {
fmt.Println(err)
fmt.Fprintln(os.Stderr, err)
return 1
}

View file

@ -1,7 +1,10 @@
#!/usr/bin/env python
# Derived from https://github.com/instrumenta/openapi2jsonschema
import yaml
import json
import sys
import urllib.request
def iteritems(d):
if hasattr(dict, "iteritems"):
@ -83,9 +86,19 @@ def append_no_duplicates(obj, key, value):
if value not in obj[key]:
obj[key].append(value)
with open(r'synced_secrets.yaml') as f:
if len(sys.argv) == 0:
print("missing file")
exit(1)
if sys.argv[1].startswith("http"):
f = urllib.request.urlopen(sys.argv[1])
else:
f = open(sys.argv[1])
with f:
y = yaml.load(f, Loader=yaml.SafeLoader)
schema = y["spec"]["validation"]["openAPIV3Schema"]
schema = additional_properties(schema)
schema = replace_int_or_string(schema)
print(json.dumps(schema))
print(json.dumps(schema))
exit(0)

View file

@ -0,0 +1,555 @@
{
"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",
"additionalProperties": false
},
"type": "array"
},
"trainingImage": {
"minLength": 1,
"type": "string"
},
"trainingInputMode": {
"enum": [
"File",
"Pipe"
],
"type": "string"
}
},
"required": [
"trainingInputMode"
],
"type": "object",
"additionalProperties": false
},
"checkpointConfig": {
"properties": {
"localPath": {
"type": "string"
},
"s3Uri": {
"pattern": "^(https|s3)://([^/]+)/?(.*)$",
"type": "string"
}
},
"required": [
"s3Uri"
],
"type": "object",
"additionalProperties": false
},
"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",
"additionalProperties": false
},
"type": "array"
}
},
"type": "object",
"additionalProperties": false
},
"type": "array"
},
"localPath": {
"type": "string"
},
"ruleParameters": {
"items": {
"description": "Used in describing maps in Kubernetes.",
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
}
},
"type": "object",
"additionalProperties": false
},
"type": "array"
},
"s3OutputPath": {
"pattern": "^(https|s3)://([^/]+)/?(.*)$",
"type": "string"
}
},
"required": [
"s3OutputPath"
],
"type": "object",
"additionalProperties": false
},
"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",
"additionalProperties": false
},
"type": "array"
},
"s3OutputPath": {
"pattern": "^(https|s3)://([^/]+)/?(.*)$",
"type": "string"
},
"volumeSizeInGB": {
"format": "int64",
"minimum": 1,
"type": "integer"
}
},
"required": [
"ruleConfigurationName",
"ruleEvaluatorImage"
],
"type": "object",
"additionalProperties": false
},
"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",
"additionalProperties": false
},
"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",
"additionalProperties": false
},
"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",
"additionalProperties": false
}
},
"type": "object",
"additionalProperties": false
},
"inputMode": {
"enum": [
"Pipe",
"File"
],
"type": "string"
},
"recordWrapperType": {
"type": "string"
},
"shuffleConfig": {
"properties": {
"seed": {
"format": "int64",
"type": "integer"
}
},
"required": [
"seed"
],
"type": "object",
"additionalProperties": false
}
},
"required": [
"channelName",
"dataSource"
],
"type": "object",
"additionalProperties": false
},
"minItems": 1,
"type": "array"
},
"outputDataConfig": {
"properties": {
"kmsKeyId": {
"type": "string"
},
"s3OutputPath": {
"pattern": "^(https|s3)://([^/]+)/?(.*)$",
"type": "string"
}
},
"required": [
"s3OutputPath"
],
"type": "object",
"additionalProperties": false
},
"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",
"additionalProperties": false
},
"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",
"additionalProperties": false
},
"tags": {
"items": {
"properties": {
"key": {
"minLength": 1,
"type": "string"
},
"value": {
"type": "string"
}
},
"required": [
"key",
"value"
],
"type": "object",
"additionalProperties": false
},
"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",
"additionalProperties": false
},
"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",
"additionalProperties": false
}
},
"required": [
"algorithmSpecification",
"outputDataConfig",
"region",
"resourceConfig",
"roleArn",
"stoppingCondition"
],
"type": "object",
"additionalProperties": false
},
"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",
"additionalProperties": false
},
"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",
"additionalProperties": false
}
},
"required": [
"spec"
],
"type": "object"
}

3
go.mod
View file

@ -3,8 +3,7 @@ module github.com/yannh/kubeconform
go 1.14
require (
github.com/instrumenta/kubeval v0.0.0-20200515185822-7721cbec724c
github.com/xeipuuv/gojsonschema v1.2.0
gopkg.in/yaml.v2 v2.3.0
gopkg.in/yaml.v2 v2.3.0 // indirect
sigs.k8s.io/yaml v1.2.0
)

2
vendor/modules.txt vendored
View file

@ -1,5 +1,3 @@
# github.com/instrumenta/kubeval v0.0.0-20200515185822-7721cbec724c
## explicit
# github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f
github.com/xeipuuv/gojsonpointer
# github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415