Skip to content

Commit

Permalink
feat(artifact/decoration): Artifact decoration spinnaker/spinnaker#1348
Browse files Browse the repository at this point in the history
… (#138)

This PR makes igor understand deb and rpm packages by heart and enables igor to be configured to support various file artifact types. The artifact decoration is currently opt in through a feature flag in the igor configuration. spinnaker/spinnaker#1348
  • Loading branch information
gardleopard authored and Matt Duftler committed Mar 30, 2017
1 parent f800783 commit 4a3a410
Show file tree
Hide file tree
Showing 15 changed files with 702 additions and 1 deletion.
9 changes: 9 additions & 0 deletions igor-web/config/igor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ spinnaker:

endpoints.health.sensitive: false

#artifact:
# This is a feature toggle for decoration of artifacts.
# decorator:
# enabled: true
# fileDecorators:
# - type: mavenUrl
# decoratorRegex: /[\/\:.]*\/([a-zA-Z-]+)\-([\d\.]+\-[\d\.]+)[a-z\-\d]+\.[jw]ar$/
# identifierRegex: /https?\:\/\/[\/\:.]*\/([a-zA-Z-]+)\-([\d\.]+\-[\d\.]+)[a-z\-\d]+\.[jw]ar$/

#travis:
# enabled: true
# Repository sync makes a call to travis telling travis to sync repos against github.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.netflix.spinnaker.igor.build.model.GenericBuild
import com.netflix.spinnaker.igor.jenkins.client.model.JobConfig
import com.netflix.spinnaker.igor.model.BuildServiceProvider
import com.netflix.spinnaker.igor.service.ArtifactDecorator
import com.netflix.spinnaker.igor.service.BuildMasters
import groovy.transform.InheritConstructors
import groovy.util.logging.Slf4j
Expand Down Expand Up @@ -52,6 +53,9 @@ class BuildController {
@Autowired
ObjectMapper objectMapper

@Autowired(required = false)
ArtifactDecorator artifactDecorator

@RequestMapping(value = '/builds/status/{buildNumber}/{master:.+}/**')
GenericBuild getJobStatus(@PathVariable String master, @PathVariable Integer buildNumber, HttpServletRequest request) {
def job = (String) request.getAttribute(
Expand All @@ -63,6 +67,11 @@ class BuildController {
} catch (Exception e) {
log.error("could not get scm results for $master / $job / $buildNumber")
}

if (artifactDecorator) {
artifactDecorator.decorate(build)
}

return build
} else {
throw new MasterNotFoundException("Master '${master}' not found")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2017 Schibsted ASA.
*
* 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 com.netflix.spinnaker.igor.build.artifact.decorator

import com.netflix.spinnaker.igor.build.model.GenericArtifact

interface ArtifactDetailsDecorator {

GenericArtifact decorate(GenericArtifact genericArtifact)

boolean handles(GenericArtifact genericArtifact)

String decoratorName()

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2017 Schibsted ASA.
*
* 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 com.netflix.spinnaker.igor.build.artifact.decorator

import com.netflix.spinnaker.igor.build.model.GenericArtifact

class ConfigurableFileDecorator implements ArtifactDetailsDecorator {

final String decoratorRegex
final String identifierRegex
final String type

ConfigurableFileDecorator(String type, String decoratorRegex, String identifierRegex) {
this.decoratorRegex = decoratorRegex
this.identifierRegex = identifierRegex
this.type = type
}

@Override
GenericArtifact decorate(GenericArtifact genericArtifact) {
def m

if ((m = genericArtifact.fileName =~ decoratorRegex)) {
genericArtifact.name = m.group(1)
genericArtifact.version = m.group(2)
genericArtifact.type = m.groupCount() > 2 ? m.group(3) : type
genericArtifact.reference = genericArtifact.fileName
}

return genericArtifact
}

@Override
boolean handles(GenericArtifact genericArtifact) {
return genericArtifact.fileName =~ identifierRegex
}

@Override
String decoratorName() {
return type
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2017 Schibsted ASA.
*
* 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 com.netflix.spinnaker.igor.build.artifact.decorator

import com.netflix.spinnaker.igor.build.model.GenericArtifact
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.stereotype.Component

@Component
@ConditionalOnProperty('artifact.decorator.enabled')
class DebDetailsDecorator implements ArtifactDetailsDecorator {

String packageType = "deb"
String versionDelimiter = '_'

@Override
GenericArtifact decorate(GenericArtifact genericArtifact) {
genericArtifact.name = extractName(genericArtifact.fileName)
genericArtifact.version = extractVersion(genericArtifact.fileName)
genericArtifact.type = packageType
genericArtifact.reference = genericArtifact.fileName
return genericArtifact
}

@Override
boolean handles(GenericArtifact genericArtifact) {
if (!genericArtifact.fileName) {
return false
}
return genericArtifact.fileName.tokenize('.').last() == "deb"
}

@Override
String decoratorName() {
return packageType
}

String extractVersion(String file) {
List<String> parts = file.tokenize(versionDelimiter)
parts.pop()
return parts.pop()
}

String extractName(String file) {
List<String> parts = file.tokenize(versionDelimiter)
return parts.first()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2017 Schibsted ASA.
*
* 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 com.netflix.spinnaker.igor.build.artifact.decorator

import com.netflix.spinnaker.igor.build.model.GenericArtifact
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.stereotype.Component

@Component
@ConditionalOnProperty('artifact.decorator.enabled')
class RpmDetailsDecorator implements ArtifactDetailsDecorator {

String packageType = 'rpm'
String versionDelimiter = '-'

@Override
GenericArtifact decorate(GenericArtifact genericArtifact) {
genericArtifact.name = extractName(genericArtifact.fileName)
genericArtifact.version = extractVersion(genericArtifact.fileName)
genericArtifact.type = packageType
genericArtifact.reference = genericArtifact.fileName
return genericArtifact
}

@Override
boolean handles(GenericArtifact genericArtifact) {
if (!genericArtifact.fileName) {
return false
}
return genericArtifact.fileName.tokenize('.').last() == "rpm"
}

@Override
String decoratorName() {
return packageType
}

String extractVersion(String file) {
List<String> parts = file.tokenize(versionDelimiter)
parts.pop()
return parts.pop()
}

String extractName(String file) {
List<String> parts = file.tokenize(versionDelimiter)
parts.pop()
parts.pop()
return parts.join(versionDelimiter)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,24 @@

package com.netflix.spinnaker.igor.build.model

import com.fasterxml.jackson.annotation.JsonInclude
import groovy.transform.ToString

@ToString(includeNames = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
class GenericArtifact {
String fileName
String displayPath
String relativePath
String reference
String name
String type
String version

GenericArtifact(String fileName, String displayPath, String relativePath) {
this.fileName = fileName
this.displayPath = displayPath
this.relativePath = relativePath
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2017 Schibsted ASA.
*
* 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 com.netflix.spinnaker.igor.config

import groovy.transform.CompileStatic
import org.hibernate.validator.constraints.NotEmpty
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration

import javax.validation.Valid

@Configuration
@CompileStatic
@ConfigurationProperties(prefix = 'artifact.decorator')
class ArtifactDecorationProperties {
boolean enabled = false

@Valid
List<FileDecorator> fileDecorators

static class FileDecorator {
@NotEmpty
String type

@NotEmpty
String decoratorRegex

@NotEmpty
String identifierRegex
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.netflix.spinnaker.igor.config

import com.netflix.hystrix.exception.HystrixRuntimeException
import com.netflix.spectator.api.Registry
import com.netflix.spinnaker.igor.service.ArtifactDecorator
import com.netflix.spinnaker.igor.service.BuildMasters
import com.netflix.spinnaker.kork.web.interceptors.MetricsInterceptor
import groovy.transform.CompileStatic
Expand Down Expand Up @@ -47,6 +48,9 @@ class IgorConfig extends WebMvcConfigurerAdapter {
@Autowired
Registry registry

@Autowired(required = false)
ArtifactDecorator artifactDecorator

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(
Expand Down
Loading

0 comments on commit 4a3a410

Please sign in to comment.