Merge pull request #1528 from jessvalarezo/labels-validate
Labels are consistently validated across services.
This commit is contained in:
		| @@ -65,6 +65,8 @@ type Container struct { | |||||||
| 	ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` | 	ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` | ||||||
| 	// Labels provides an area to include arbitrary data on containers. | 	// Labels provides an area to include arbitrary data on containers. | ||||||
| 	// | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
|  | 	// | ||||||
| 	// Note that to add a new value to this field, read the existing set and | 	// Note that to add a new value to this field, read the existing set and | ||||||
| 	// include the entire result in the update call. | 	// include the entire result in the update call. | ||||||
| 	Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | 	Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||||
|   | |||||||
| @@ -42,6 +42,8 @@ message Container { | |||||||
|  |  | ||||||
| 	// Labels provides an area to include arbitrary data on containers. | 	// Labels provides an area to include arbitrary data on containers. | ||||||
| 	// | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
|  | 	// | ||||||
| 	// Note that to add a new value to this field, read the existing set and | 	// Note that to add a new value to this field, read the existing set and | ||||||
| 	// include the entire result in the update call. | 	// include the entire result in the update call. | ||||||
| 	map<string, string> labels  = 2; | 	map<string, string> labels  = 2; | ||||||
|   | |||||||
| @@ -115,7 +115,9 @@ type Info struct { | |||||||
| 	CreatedAt time.Time `protobuf:"bytes,3,opt,name=created_at,json=createdAt,stdtime" json:"created_at"` | 	CreatedAt time.Time `protobuf:"bytes,3,opt,name=created_at,json=createdAt,stdtime" json:"created_at"` | ||||||
| 	// UpdatedAt provides the time the info was last updated. | 	// UpdatedAt provides the time the info was last updated. | ||||||
| 	UpdatedAt time.Time `protobuf:"bytes,4,opt,name=updated_at,json=updatedAt,stdtime" json:"updated_at"` | 	UpdatedAt time.Time `protobuf:"bytes,4,opt,name=updated_at,json=updatedAt,stdtime" json:"updated_at"` | ||||||
| 	// Labels are arbitrary data on content. | 	// Labels are arbitrary data on snapshots. | ||||||
|  | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	Labels map[string]string `protobuf:"bytes,5,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | 	Labels map[string]string `protobuf:"bytes,5,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -321,7 +323,9 @@ type WriteContentRequest struct { | |||||||
| 	// If this is empty and the message is not a commit, a response will be | 	// If this is empty and the message is not a commit, a response will be | ||||||
| 	// returned with the current write state. | 	// returned with the current write state. | ||||||
| 	Data []byte `protobuf:"bytes,6,opt,name=data,proto3" json:"data,omitempty"` | 	Data []byte `protobuf:"bytes,6,opt,name=data,proto3" json:"data,omitempty"` | ||||||
| 	// Labels are arbitrary data to set on commit. | 	// Labels are arbitrary data on snapshots. | ||||||
|  | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	Labels map[string]string `protobuf:"bytes,7,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | 	Labels map[string]string `protobuf:"bytes,7,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -87,7 +87,9 @@ message Info { | |||||||
| 	// UpdatedAt provides the time the info was last updated. | 	// UpdatedAt provides the time the info was last updated. | ||||||
| 	google.protobuf.Timestamp updated_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; | 	google.protobuf.Timestamp updated_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; | ||||||
|  |  | ||||||
| 	// Labels are arbitrary data on content. | 	// Labels are arbitrary data on snapshots. | ||||||
|  | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	map<string, string> labels  = 5; | 	map<string, string> labels  = 5; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -270,7 +272,9 @@ message WriteContentRequest { | |||||||
| 	// returned with the current write state. | 	// returned with the current write state. | ||||||
| 	bytes data = 6; | 	bytes data = 6; | ||||||
|  |  | ||||||
| 	// Labels are arbitrary data to set on commit. | 	// Labels are arbitrary data on snapshots. | ||||||
|  | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	map<string, string> labels  = 7; | 	map<string, string> labels  = 7; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -67,6 +67,7 @@ type Image struct { | |||||||
| 	// and do not get inherited into the package image in any way. | 	// and do not get inherited into the package image in any way. | ||||||
| 	// | 	// | ||||||
| 	// Labels may be updated using the field mask. | 	// Labels may be updated using the field mask. | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | 	Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||||
| 	// Target describes the content entry point of the image. | 	// Target describes the content entry point of the image. | ||||||
| 	Target containerd_types.Descriptor `protobuf:"bytes,3,opt,name=target" json:"target"` | 	Target containerd_types.Descriptor `protobuf:"bytes,3,opt,name=target" json:"target"` | ||||||
|   | |||||||
| @@ -51,6 +51,7 @@ message Image { | |||||||
| 	// and do not get inherited into the package image in any way. | 	// and do not get inherited into the package image in any way. | ||||||
| 	// | 	// | ||||||
| 	// Labels may be updated using the field mask. | 	// Labels may be updated using the field mask. | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	map<string, string> labels = 2; | 	map<string, string> labels = 2; | ||||||
|  |  | ||||||
| 	// Target describes the content entry point of the image. | 	// Target describes the content entry point of the image. | ||||||
|   | |||||||
| @@ -55,6 +55,8 @@ type Namespace struct { | |||||||
| 	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` | 	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` | ||||||
| 	// Labels provides an area to include arbitrary data on namespaces. | 	// Labels provides an area to include arbitrary data on namespaces. | ||||||
| 	// | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
|  | 	// | ||||||
| 	// Note that to add a new value to this field, read the existing set and | 	// Note that to add a new value to this field, read the existing set and | ||||||
| 	// include the entire result in the update call. | 	// include the entire result in the update call. | ||||||
| 	Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | 	Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||||
|   | |||||||
| @@ -32,6 +32,8 @@ message Namespace { | |||||||
|  |  | ||||||
| 	// Labels provides an area to include arbitrary data on namespaces. | 	// Labels provides an area to include arbitrary data on namespaces. | ||||||
| 	// | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
|  | 	// | ||||||
| 	// Note that to add a new value to this field, read the existing set and | 	// Note that to add a new value to this field, read the existing set and | ||||||
| 	// include the entire result in the update call. | 	// include the entire result in the update call. | ||||||
| 	map<string, string> labels  = 2; | 	map<string, string> labels  = 2; | ||||||
|   | |||||||
| @@ -97,6 +97,8 @@ type PrepareSnapshotRequest struct { | |||||||
| 	Key         string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` | 	Key         string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` | ||||||
| 	Parent      string `protobuf:"bytes,3,opt,name=parent,proto3" json:"parent,omitempty"` | 	Parent      string `protobuf:"bytes,3,opt,name=parent,proto3" json:"parent,omitempty"` | ||||||
| 	// Labels are arbitrary data on snapshots. | 	// Labels are arbitrary data on snapshots. | ||||||
|  | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	Labels map[string]string `protobuf:"bytes,4,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | 	Labels map[string]string `protobuf:"bytes,4,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -117,6 +119,8 @@ type ViewSnapshotRequest struct { | |||||||
| 	Key         string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` | 	Key         string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` | ||||||
| 	Parent      string `protobuf:"bytes,3,opt,name=parent,proto3" json:"parent,omitempty"` | 	Parent      string `protobuf:"bytes,3,opt,name=parent,proto3" json:"parent,omitempty"` | ||||||
| 	// Labels are arbitrary data on snapshots. | 	// Labels are arbitrary data on snapshots. | ||||||
|  | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	Labels map[string]string `protobuf:"bytes,4,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | 	Labels map[string]string `protobuf:"bytes,4,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -163,6 +167,8 @@ type CommitSnapshotRequest struct { | |||||||
| 	Name        string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` | 	Name        string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` | ||||||
| 	Key         string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` | 	Key         string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` | ||||||
| 	// Labels are arbitrary data on snapshots. | 	// Labels are arbitrary data on snapshots. | ||||||
|  | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	Labels map[string]string `protobuf:"bytes,4,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | 	Labels map[string]string `protobuf:"bytes,4,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -188,6 +194,8 @@ type Info struct { | |||||||
| 	// UpdatedAt provides the time the info was last updated. | 	// UpdatedAt provides the time the info was last updated. | ||||||
| 	UpdatedAt time.Time `protobuf:"bytes,5,opt,name=updated_at,json=updatedAt,stdtime" json:"updated_at"` | 	UpdatedAt time.Time `protobuf:"bytes,5,opt,name=updated_at,json=updatedAt,stdtime" json:"updated_at"` | ||||||
| 	// Labels are arbitrary data on snapshots. | 	// Labels are arbitrary data on snapshots. | ||||||
|  | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	Labels map[string]string `protobuf:"bytes,6,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | 	Labels map[string]string `protobuf:"bytes,6,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -29,6 +29,8 @@ message PrepareSnapshotRequest { | |||||||
| 	string parent = 3; | 	string parent = 3; | ||||||
|  |  | ||||||
| 	// Labels are arbitrary data on snapshots. | 	// Labels are arbitrary data on snapshots. | ||||||
|  | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	map<string, string> labels  = 4; | 	map<string, string> labels  = 4; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -42,6 +44,8 @@ message ViewSnapshotRequest { | |||||||
| 	string parent = 3; | 	string parent = 3; | ||||||
|  |  | ||||||
| 	// Labels are arbitrary data on snapshots. | 	// Labels are arbitrary data on snapshots. | ||||||
|  | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	map<string, string> labels  = 4; | 	map<string, string> labels  = 4; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -69,6 +73,8 @@ message CommitSnapshotRequest { | |||||||
| 	string key = 3; | 	string key = 3; | ||||||
|  |  | ||||||
| 	// Labels are arbitrary data on snapshots. | 	// Labels are arbitrary data on snapshots. | ||||||
|  | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	map<string, string> labels  = 4; | 	map<string, string> labels  = 4; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -99,6 +105,8 @@ message Info { | |||||||
| 	google.protobuf.Timestamp updated_at = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; | 	google.protobuf.Timestamp updated_at = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; | ||||||
|  |  | ||||||
| 	// Labels are arbitrary data on snapshots. | 	// Labels are arbitrary data on snapshots. | ||||||
|  | 	// | ||||||
|  | 	// The combined size of a key/value pair cannot exceed 4096 bytes. | ||||||
| 	map<string, string> labels  = 6; | 	map<string, string> labels  = 6; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -108,10 +108,16 @@ var imagesSetLabelsCommand = cli.Command{ | |||||||
| 	Usage:       "set and clear labels for an image.", | 	Usage:       "set and clear labels for an image.", | ||||||
| 	ArgsUsage:   "[flags] <name> [<key>=<value>, ...]", | 	ArgsUsage:   "[flags] <name> [<key>=<value>, ...]", | ||||||
| 	Description: "Set and clear labels for an image.", | 	Description: "Set and clear labels for an image.", | ||||||
| 	Flags:       []cli.Flag{}, | 	Flags: []cli.Flag{ | ||||||
|  | 		cli.BoolFlag{ | ||||||
|  | 			Name:  "replace-all, r", | ||||||
|  | 			Usage: "replace all labels", | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
| 	Action: func(clicontext *cli.Context) error { | 	Action: func(clicontext *cli.Context) error { | ||||||
| 		var ( | 		var ( | ||||||
| 			ctx, cancel  = appContext(clicontext) | 			ctx, cancel  = appContext(clicontext) | ||||||
|  | 			replaceAll   = clicontext.Bool("replace-all") | ||||||
| 			name, labels = objectWithLabelArgs(clicontext) | 			name, labels = objectWithLabelArgs(clicontext) | ||||||
| 		) | 		) | ||||||
| 		defer cancel() | 		defer cancel() | ||||||
| @@ -131,8 +137,12 @@ var imagesSetLabelsCommand = cli.Command{ | |||||||
| 		) | 		) | ||||||
|  |  | ||||||
| 		for k := range labels { | 		for k := range labels { | ||||||
|  | 			if replaceAll { | ||||||
|  | 				fieldpaths = append(fieldpaths, "labels") | ||||||
|  | 			} else { | ||||||
| 				fieldpaths = append(fieldpaths, strings.Join([]string{"labels", k}, ".")) | 				fieldpaths = append(fieldpaths, strings.Join([]string{"labels", k}, ".")) | ||||||
| 			} | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		image := images.Image{ | 		image := images.Image{ | ||||||
| 			Name:   name, | 			Name:   name, | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								labels/validate.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								labels/validate.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | package labels | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/containerd/containerd/errdefs" | ||||||
|  | 	"github.com/pkg/errors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	maxSize = 4096 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func Validate(k, v string) error { | ||||||
|  | 	// A label key and value should be under 4096 bytes | ||||||
|  | 	if (len(k) + len(v)) > maxSize { | ||||||
|  | 		if len(k) > 10 { | ||||||
|  | 			k = k[:10] | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return errors.Wrapf(errdefs.ErrInvalidArgument, "label key and value greater than maximum size (%d bytes), key: %s", maxSize, k) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								labels/validate_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								labels/validate_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | package labels | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/containerd/containerd/errdefs" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestValidLabels(t *testing.T) { | ||||||
|  | 	shortStr := "s" | ||||||
|  | 	longStr := strings.Repeat("s", maxSize-1) | ||||||
|  |  | ||||||
|  | 	for key, value := range map[string]string{ | ||||||
|  | 		"some":   "value", | ||||||
|  | 		shortStr: longStr, | ||||||
|  | 	} { | ||||||
|  | 		if err := Validate(key, value); err != nil { | ||||||
|  | 			t.Fatalf("unexpected error: %v != nil", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestInvalidLabels(t *testing.T) { | ||||||
|  | 	addOneStr := "s" | ||||||
|  | 	maxSizeStr := strings.Repeat("s", maxSize) | ||||||
|  |  | ||||||
|  | 	for key, value := range map[string]string{ | ||||||
|  | 		maxSizeStr: addOneStr, | ||||||
|  | 	} { | ||||||
|  | 		if err := Validate(key, value); err == nil { | ||||||
|  | 			t.Fatal("expected invalid error") | ||||||
|  | 		} else if !errdefs.IsInvalidArgument(err) { | ||||||
|  | 			t.Fatal("error should be an invalid label error") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"github.com/containerd/containerd/errdefs" | 	"github.com/containerd/containerd/errdefs" | ||||||
| 	"github.com/containerd/containerd/filters" | 	"github.com/containerd/containerd/filters" | ||||||
| 	"github.com/containerd/containerd/identifiers" | 	"github.com/containerd/containerd/identifiers" | ||||||
|  | 	"github.com/containerd/containerd/labels" | ||||||
| 	"github.com/containerd/containerd/metadata/boltutil" | 	"github.com/containerd/containerd/metadata/boltutil" | ||||||
| 	"github.com/containerd/containerd/namespaces" | 	"github.com/containerd/containerd/namespaces" | ||||||
| 	"github.com/gogo/protobuf/proto" | 	"github.com/gogo/protobuf/proto" | ||||||
| @@ -243,7 +244,13 @@ func validateContainer(container *containers.Container) error { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// labels and image have no validation | 	// image has no validation | ||||||
|  | 	for k, v := range container.Labels { | ||||||
|  | 		if err := labels.Validate(k, v); err == nil { | ||||||
|  | 			return errors.Wrapf(err, "containers.Labels") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if container.Runtime.Name == "" { | 	if container.Runtime.Name == "" { | ||||||
| 		return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Runtime.Name must be set") | 		return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Runtime.Name must be set") | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"github.com/containerd/containerd/content" | 	"github.com/containerd/containerd/content" | ||||||
| 	"github.com/containerd/containerd/errdefs" | 	"github.com/containerd/containerd/errdefs" | ||||||
| 	"github.com/containerd/containerd/filters" | 	"github.com/containerd/containerd/filters" | ||||||
|  | 	"github.com/containerd/containerd/labels" | ||||||
| 	"github.com/containerd/containerd/metadata/boltutil" | 	"github.com/containerd/containerd/metadata/boltutil" | ||||||
| 	"github.com/containerd/containerd/namespaces" | 	"github.com/containerd/containerd/namespaces" | ||||||
| 	digest "github.com/opencontainers/go-digest" | 	digest "github.com/opencontainers/go-digest" | ||||||
| @@ -94,6 +95,9 @@ func (cs *contentStore) Update(ctx context.Context, info content.Info, fieldpath | |||||||
| 			// Set mutable fields | 			// Set mutable fields | ||||||
| 			updated.Labels = info.Labels | 			updated.Labels = info.Labels | ||||||
| 		} | 		} | ||||||
|  | 		if err := validateInfo(&updated); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		updated.UpdatedAt = time.Now().UTC() | 		updated.UpdatedAt = time.Now().UTC() | ||||||
| 		return writeInfo(&updated, bkt) | 		return writeInfo(&updated, bkt) | ||||||
| @@ -371,6 +375,10 @@ func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64, | |||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	if err := validateInfo(&base); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	status, err := nw.Writer.Status() | 	status, err := nw.Writer.Status() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -446,6 +454,16 @@ func (cs *contentStore) checkAccess(ctx context.Context, dgst digest.Digest) err | |||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func validateInfo(info *content.Info) error { | ||||||
|  | 	for k, v := range info.Labels { | ||||||
|  | 		if err := labels.Validate(k, v); err == nil { | ||||||
|  | 			return errors.Wrapf(err, "info.Labels") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func readInfo(info *content.Info, bkt *bolt.Bucket) error { | func readInfo(info *content.Info, bkt *bolt.Bucket) error { | ||||||
| 	if err := boltutil.ReadTimestamps(bkt, &info.CreatedAt, &info.UpdatedAt); err != nil { | 	if err := boltutil.ReadTimestamps(bkt, &info.CreatedAt, &info.UpdatedAt); err != nil { | ||||||
| 		return err | 		return err | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import ( | |||||||
| 	"github.com/containerd/containerd/errdefs" | 	"github.com/containerd/containerd/errdefs" | ||||||
| 	"github.com/containerd/containerd/filters" | 	"github.com/containerd/containerd/filters" | ||||||
| 	"github.com/containerd/containerd/images" | 	"github.com/containerd/containerd/images" | ||||||
|  | 	"github.com/containerd/containerd/labels" | ||||||
| 	"github.com/containerd/containerd/metadata/boltutil" | 	"github.com/containerd/containerd/metadata/boltutil" | ||||||
| 	"github.com/containerd/containerd/namespaces" | 	"github.com/containerd/containerd/namespaces" | ||||||
| 	digest "github.com/opencontainers/go-digest" | 	digest "github.com/opencontainers/go-digest" | ||||||
| @@ -101,6 +102,10 @@ func (s *imageStore) Create(ctx context.Context, image images.Image) (images.Ima | |||||||
| 		return images.Image{}, errors.Wrapf(errdefs.ErrInvalidArgument, "image name is required for create") | 		return images.Image{}, errors.Wrapf(errdefs.ErrInvalidArgument, "image name is required for create") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if err := validateImage(&image); err != nil { | ||||||
|  | 		return images.Image{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return image, withImagesBucket(s.tx, namespace, func(bkt *bolt.Bucket) error { | 	return image, withImagesBucket(s.tx, namespace, func(bkt *bolt.Bucket) error { | ||||||
| 		ibkt, err := bkt.CreateBucket([]byte(image.Name)) | 		ibkt, err := bkt.CreateBucket([]byte(image.Name)) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @@ -170,6 +175,10 @@ func (s *imageStore) Update(ctx context.Context, image images.Image, fieldpaths | |||||||
| 			updated = image | 			updated = image | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		if err := validateImage(&image); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		updated.CreatedAt = createdat | 		updated.CreatedAt = createdat | ||||||
| 		updated.UpdatedAt = time.Now().UTC() | 		updated.UpdatedAt = time.Now().UTC() | ||||||
| 		return writeImage(ibkt, &updated) | 		return writeImage(ibkt, &updated) | ||||||
| @@ -191,6 +200,16 @@ func (s *imageStore) Delete(ctx context.Context, name string) error { | |||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func validateImage(image *images.Image) error { | ||||||
|  | 	for k, v := range image.Labels { | ||||||
|  | 		if err := labels.Validate(k, v); err != nil { | ||||||
|  | 			return errors.Wrapf(err, "image.Labels") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func readImage(image *images.Image, bkt *bolt.Bucket) error { | func readImage(image *images.Image, bkt *bolt.Bucket) error { | ||||||
| 	if err := boltutil.ReadTimestamps(bkt, &image.CreatedAt, &image.UpdatedAt); err != nil { | 	if err := boltutil.ReadTimestamps(bkt, &image.CreatedAt, &image.UpdatedAt); err != nil { | ||||||
| 		return err | 		return err | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/boltdb/bolt" | 	"github.com/boltdb/bolt" | ||||||
| 	"github.com/containerd/containerd/errdefs" | 	"github.com/containerd/containerd/errdefs" | ||||||
|  | 	l "github.com/containerd/containerd/labels" | ||||||
| 	"github.com/containerd/containerd/namespaces" | 	"github.com/containerd/containerd/namespaces" | ||||||
| 	"github.com/pkg/errors" | 	"github.com/pkg/errors" | ||||||
| ) | ) | ||||||
| @@ -27,6 +28,12 @@ func (s *namespaceStore) Create(ctx context.Context, namespace string, labels ma | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	for k, v := range labels { | ||||||
|  | 		if err := l.Validate(k, v); err != nil { | ||||||
|  | 			return errors.Wrapf(err, "namespace.Labels") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// provides the already exists error. | 	// provides the already exists error. | ||||||
| 	bkt, err := topbkt.CreateBucket([]byte(namespace)) | 	bkt, err := topbkt.CreateBucket([]byte(namespace)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -70,6 +77,10 @@ func (s *namespaceStore) Labels(ctx context.Context, namespace string) (map[stri | |||||||
| } | } | ||||||
|  |  | ||||||
| func (s *namespaceStore) SetLabel(ctx context.Context, namespace, key, value string) error { | func (s *namespaceStore) SetLabel(ctx context.Context, namespace, key, value string) error { | ||||||
|  | 	if err := l.Validate(key, value); err != nil { | ||||||
|  | 		return errors.Wrapf(err, "namespace.Labels") | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return withNamespacesLabelsBucket(s.tx, namespace, func(bkt *bolt.Bucket) error { | 	return withNamespacesLabelsBucket(s.tx, namespace, func(bkt *bolt.Bucket) error { | ||||||
| 		if value == "" { | 		if value == "" { | ||||||
| 			return bkt.Delete([]byte(key)) | 			return bkt.Delete([]byte(key)) | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/boltdb/bolt" | 	"github.com/boltdb/bolt" | ||||||
| 	"github.com/containerd/containerd/errdefs" | 	"github.com/containerd/containerd/errdefs" | ||||||
|  | 	"github.com/containerd/containerd/labels" | ||||||
| 	"github.com/containerd/containerd/metadata/boltutil" | 	"github.com/containerd/containerd/metadata/boltutil" | ||||||
| 	"github.com/containerd/containerd/mount" | 	"github.com/containerd/containerd/mount" | ||||||
| 	"github.com/containerd/containerd/namespaces" | 	"github.com/containerd/containerd/namespaces" | ||||||
| @@ -180,6 +181,9 @@ func (s *snapshotter) Update(ctx context.Context, info snapshot.Info, fieldpaths | |||||||
| 		} else { | 		} else { | ||||||
| 			local.Labels = info.Labels | 			local.Labels = info.Labels | ||||||
| 		} | 		} | ||||||
|  | 		if err := validateSnapshot(&local); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
| 		local.Updated = time.Now().UTC() | 		local.Updated = time.Now().UTC() | ||||||
|  |  | ||||||
| 		if err := boltutil.WriteTimestamps(sbkt, local.Created, local.Updated); err != nil { | 		if err := boltutil.WriteTimestamps(sbkt, local.Created, local.Updated); err != nil { | ||||||
| @@ -257,6 +261,10 @@ func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, re | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if err := validateSnapshot(&base); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	var m []mount.Mount | 	var m []mount.Mount | ||||||
| 	if err := update(ctx, s.db, func(tx *bolt.Tx) error { | 	if err := update(ctx, s.db, func(tx *bolt.Tx) error { | ||||||
| 		bkt, err := createSnapshotterBucket(tx, ns, s.name) | 		bkt, err := createSnapshotterBucket(tx, ns, s.name) | ||||||
| @@ -328,6 +336,10 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if err := validateSnapshot(&base); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return update(ctx, s.db, func(tx *bolt.Tx) error { | 	return update(ctx, s.db, func(tx *bolt.Tx) error { | ||||||
| 		bkt := getSnapshotterBucket(tx, ns, s.name) | 		bkt := getSnapshotterBucket(tx, ns, s.name) | ||||||
| 		if bkt == nil { | 		if bkt == nil { | ||||||
| @@ -502,3 +514,13 @@ func (s *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapsho | |||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func validateSnapshot(info *snapshot.Info) error { | ||||||
|  | 	for k, v := range info.Labels { | ||||||
|  | 		if err := labels.Validate(k, v); err != nil { | ||||||
|  | 			return errors.Wrapf(err, "info.Labels") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|   | |||||||
| @@ -354,7 +354,7 @@ func (s *Service) Write(session api.Content_WriteServer) (err error) { | |||||||
| 		// 	2. Compress inline. | 		// 	2. Compress inline. | ||||||
| 		// 	3. Validate digest and size (maybe). | 		// 	3. Validate digest and size (maybe). | ||||||
| 		// | 		// | ||||||
| 		// Supporting these two paths is quite awkward but it let's both API | 		// Supporting these two paths is quite awkward but it lets both API | ||||||
| 		// users use the same writer style for each with a minimum of overhead. | 		// users use the same writer style for each with a minimum of overhead. | ||||||
| 		if req.Expected != "" { | 		if req.Expected != "" { | ||||||
| 			if expected != "" && expected != req.Expected { | 			if expected != "" && expected != req.Expected { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Stephen Day
					Stephen Day