Skip to content

Commit

Permalink
Add container image validation
Browse files Browse the repository at this point in the history
  • Loading branch information
f-higashi committed Mar 3, 2016
1 parent 5dd4bdd commit 634df61
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 1 deletion.
21 changes: 21 additions & 0 deletions src/app/backend/apihandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ func CreateHttpApiHandler(client *client.Client, heapsterClient HeapsterClient,
To(apiHandler.handleNameValidity).
Reads(AppNameValiditySpec{}).
Writes(AppNameValidity{}))
deployWs.Route(
deployWs.POST("/validate/imagereference").
To(apiHandler.handleImageReferenceValidity).
Reads(ImageReferenceValiditySpec{}).
Writes(ImageReferenceValidity{}))
deployWs.Route(
deployWs.POST("/validate/protocol").
To(apiHandler.handleProtocolValidity).
Expand Down Expand Up @@ -246,6 +251,22 @@ func (apiHandler *ApiHandler) handleNameValidity(request *restful.Request, respo
response.WriteHeaderAndEntity(http.StatusCreated, validity)
}

// Handles image reference validation API call.
func (ApiHandler *ApiHandler) handleImageReferenceValidity(request *restful.Request, response *restful.Response){
spec := new(ImageReferenceValiditySpec)
if err := request.ReadEntity(spec); err != nil {
handleInternalError(response, err)
return
}

validity, err := ValidateImageReference(spec)
if err != nil {
handleInternalError(response, err)
return
}
response.WriteHeaderAndEntity(http.StatusCreated, validity)
}

// Handles protocol validation API call.
func (apiHandler *ApiHandler) handleProtocolValidity(request *restful.Request, response *restful.Response) {
spec := new(ProtocolValiditySpec)
Expand Down
46 changes: 46 additions & 0 deletions src/app/backend/validateimagereference.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"log"
distreference "github.com/docker/distribution/reference"
)

// Specification for image referecne validation request.
type ImageReferenceValiditySpec struct {
// Reference of the image
Reference string `json:"reference"`
}

// Describe validity of the image reference.
type ImageReferenceValidity struct {
// True when the image reference is valid.
Valid bool `json:"valid"`
// Error reason when image reference is valid
Reason string `json:"reason"`
}

// Validation image reference.
func ValidateImageReference(spec *ImageReferenceValiditySpec) (*ImageReferenceValidity, error) {
log.Printf("Validating %s as a image reference", spec.Reference)

s := spec.Reference
_, err := distreference.ParseNamed(s)
if err != nil {
return &ImageReferenceValidity{Valid: false, Reason: err.Error()}, nil
}
return &ImageReferenceValidity{Valid: true}, nil
}
15 changes: 15 additions & 0 deletions src/app/externs/backendapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,21 @@ backendApi.AppNameValiditySpec;
*/
backendApi.AppNameValidity;

/**
* @typedef {{
* reference: string
* }}
*/
backendApi.ImageReferenceValiditySpec;

/**
* @typedef {{
* valid: boolean,
* reason: string
* }}
*/
backendApi.ImageReferenceValidity;

/**
* @typedef {{
* protocols: !Array<string>
Expand Down
2 changes: 2 additions & 0 deletions src/app/frontend/deploy/deploy_module.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import portMappingsDirective from './portmappings_directive';
import environmentVariablesDirective from './environmentvariables_directive';
import uploadDirective from './upload_directive';
import uniqueNameDirective from './uniquename_directive';
import validImageReferenceDirective from './validimagereference_directive';
import validProtocolDirective from './validprotocol_directive';
import helpSectionModule from './helpsection/helpsection_module';

Expand All @@ -44,6 +45,7 @@ export default angular
.directive('deployFromSettings', deployFromSettingsDirective)
.directive('deployFromFile', deployFromFileDirective)
.directive('kdUniqueName', uniqueNameDirective)
.directive('kdValidImagereference', validImageReferenceDirective)
.directive('kdValidProtocol', validProtocolDirective)
.directive('kdFileReader', fileReaderDirective)
.directive('kdUpload', uploadDirective)
Expand Down
3 changes: 2 additions & 1 deletion src/app/frontend/deploy/deployfromsettings.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@
<kd-help-section>
<md-input-container class="md-block">
<label>Container image</label>
<input ng-model="ctrl.containerImage" name="containerImage" required>
<input ng-model="ctrl.containerImage" name="containerImage" required kd-valid-imagereference >
<ng-messages for="ctrl.form.containerImage.$error" role="alert" multiple>
<ng-message when="required">Container image is required.</ng-message>
<ng-message when="validImageReference">Container image is invalid format.</ng-message>
</ng-messages>
</md-input-container>
<kd-user-help>
Expand Down
67 changes: 67 additions & 0 deletions src/app/frontend/deploy/validimagereference_directive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/** The name of this directive. */
export const validImageReferenceValidationKey = 'validImageReference';

/**
* Validates image reference
*
* @param {!angular.$resource} $resource
* @param {!angular.$q} $q
* @return {!angular.Directive}
* @ngInject
*/
export default function validImageReferenceDirective($resource, $q) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
/** @type {!angular.NgModelController} */
let ngModelController = ctrl;

ngModelController.$asyncValidators[validImageReferenceValidationKey] =
(reference) => { return validate(reference, $resource, $q); };
},
};
}

/**
* @param {string} reference
* @param {!angular.$resource} resource
* @param {!angular.$q} q
*/
function validate(reference, resource, q) {
let deferred = q.defer();

/** @type {!angular.Resource<!backendApi.ProtocolValiditySpec>} */
let resourceClass = resource('api/v1/appdeployments/validate/imagereference');
/** @type {!backendApi.ImageReferenceValiditySpec} */
let spec = {reference: reference};
resourceClass.save(
spec,
/**
* @param {!backendApi.ImageReferenceValidity} validity
*/
(validity) => {
if (validity.valid === true) {
deferred.resolve();
} else {
deferred.reject(validity.reason);
}
},
() => { deferred.reject(); });

return deferred.promise;
}
102 changes: 102 additions & 0 deletions src/test/backend/validateimagereference_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"testing"
)

func TestValidateImageReference(t *testing.T) {
cases := []struct {
reference string
expected bool
}{
{
"test",
true,
},
{
"test:1",
true,
},
{
"test:tag",
true,
},
{
"private.registry:5000/test:1",
true,
},
{
"private.registry:5000/test",
true,
},
{
"private.registry:5000/namespace/test:1",
true,
},
{
"private.registry:port/namespace/test:1",
false,
},
{
"private.registry:5000/n/a/m/e/s/test:1",
true,
},
{
"private.registry:5000/namespace/test:image:1",
false,
},
{
":",
false,
},
{
"private.registry:5000/test:1@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
true,
},
{
"Test",
false,
},
{
"private.registry:5000/Test",
false,
},
{
"private@registry:5000/test",
false,
},
{
"",
false,
},
{
"test image:1",
false,
},
}

for _, c := range cases {
spec := &ImageReferenceValiditySpec{
Reference: c.reference,
}
validity, _ := ValidateImageReference(spec)
if validity.Valid != c.expected {
t.Errorf("Expected %#v validity to be %#v, but was %#v\n",
c.reference, c.expected, validity)
}
}
}

0 comments on commit 634df61

Please sign in to comment.