11import * as tl from "azure-pipelines-task-lib/task" ;
22import * as util from "util" ;
3+ import * as yaml from 'js-yaml' ;
34
45const matchPatternForImageName = new RegExp ( / \: \/ \/ ( .+ ?) \@ / ) ;
56const matchPatternForDigest = new RegExp ( / \@ s h a 2 5 6 \: ( .+ ) / ) ;
67const matchPatternForFileArgument = new RegExp ( / - f \s | - f i l e n a m e \s / ) ;
78const matchPatternForServerUrl = new RegExp ( / h t t p s \: \/ \/ ( .+ ) / ) ;
9+ const matchPatternForSource = new RegExp ( / s o u r c e : ( .+ ) / ig) ;
10+ const matchPatternForChartPath = new RegExp ( / c h a r t p a t h : ( .+ ) / i) ;
811const orgUrl = tl . getVariable ( 'System.TeamFoundationCollectionUri' ) ;
912const build = "build" ;
1013const hostType = tl . getVariable ( "System.HostType" ) . toLowerCase ( ) ;
1114const isBuild = hostType === build ;
1215const deploymentTypes : string [ ] = [ "deployment" , "replicaset" , "daemonset" , "pod" , "statefulset" ] ;
16+ const workingDirectory = tl . getVariable ( "System.DefaultWorkingDirectory" ) ;
17+ const branch = tl . getVariable ( "Build.SourceBranchName" ) || tl . getVariable ( "Build.SourceBranch" ) ;
18+ const repositoryProvider = tl . getVariable ( "Build.Repository.Provider" ) ;
19+ const repositoryUrl = tl . getVariable ( "Build.Repository.Uri" ) ;
1320
1421// ToDo: Add UTs for public methods
15- export function getDeploymentMetadata ( deploymentObject : any , allPods : any , deploymentStrategy : string , clusterInfo : any , manifestFilePaths ? : string [ ] ) : any {
22+ export function getDeploymentMetadata ( deploymentObject : any , allPods : any , deploymentStrategy : string , clusterInfo : any , manifestUrls : string [ ] ) : any {
1623 let imageIds : string [ ] = [ ] ;
24+ let containers = [ ] ;
1725 let kind : string = deploymentObject . kind ;
1826 try {
1927 if ( isPodEntity ( kind ) ) {
20- imageIds = getImageIdsForPod ( deploymentObject ) ;
28+ containers = deploymentObject . spec . containers ;
2129 }
2230 else {
23- let containers = deploymentObject . spec . template . spec . containers ;
24- if ( containers && containers . length > 0 ) {
25- containers . forEach ( container => {
26- // Filter all pods using the container names in this deployment,
27- // and get the imageIds from pod status
28- imageIds = getImageIdsForPodsInDeployment ( container . name , allPods . items ) ;
29- } ) ;
30- }
31+ containers = deploymentObject . spec . template . spec . containers ;
32+ }
33+
34+ if ( containers && containers . length > 0 ) {
35+ containers . forEach ( container => {
36+ // Filter all pods using the container names in this deployment,
37+ // and get the imageIds from pod status
38+ imageIds = getImageIdsForPodsInDeployment ( container . name , allPods . items ) ;
39+ } ) ;
3140 }
3241 }
3342 catch ( e ) {
@@ -42,14 +51,8 @@ export function getDeploymentMetadata(deploymentObject: any, allPods: any, deplo
4251 relatedUrls . push ( clusterUrl ) ;
4352 }
4453
45- if ( manifestFilePaths ) {
46- relatedUrls . push ( ...manifestFilePaths ) ;
47- }
48- else {
49- let manifestPaths = getManifestFilePaths ( ) ;
50- if ( manifestPaths . length > 0 ) {
51- relatedUrls . push ( ...manifestPaths ) ;
52- }
54+ if ( manifestUrls . length > 0 ) {
55+ relatedUrls . push ( ...manifestUrls ) ;
5356 }
5457
5558 const metadataDetails = {
@@ -192,19 +195,130 @@ function getServerUrl(clusterInfo: any): string {
192195 return serverUrl ;
193196}
194197
195- function getManifestFilePaths ( ) : string [ ] {
196- let manifestFilePaths : string [ ] = [ ] ;
197- const commandArguments = tl . getInput ( "arguments" , false ) ;
198- const filePathMatch : string [ ] = commandArguments . split ( matchPatternForFileArgument ) ;
199- if ( filePathMatch && filePathMatch . length >= 0 ) {
198+ export function extractManifestsFromHelmOutput ( helmOutput : string ) : any {
199+ let manifestObjects = [ ] ;
200+ let manifestFiles = "" ;
201+ // The output stream contains the manifest file between the manifest and last deployed fields
202+ const manifestString = "manifest:" ;
203+ const lastDeployedString = "last deployed:" ;
204+ let indexOfManifests = helmOutput . toLowerCase ( ) . indexOf ( manifestString ) ;
205+ let indexOfLastDeployed = helmOutput . toLowerCase ( ) . indexOf ( lastDeployedString ) ;
206+ if ( indexOfManifests >= 0 && indexOfLastDeployed >= 0 ) {
207+ manifestFiles = helmOutput . substring ( indexOfManifests + manifestString . length , indexOfLastDeployed ) ;
208+ }
209+
210+ if ( manifestFiles ) {
211+ // Each of the source manifests is separated in output stream via string '---'
212+ const files = manifestFiles . split ( "---" ) ;
213+ files . forEach ( file => {
214+ file = file . trim ( ) ;
215+ if ( file ) {
216+ const parsedObject = yaml . safeLoad ( file ) ;
217+ manifestObjects . push ( parsedObject ) ;
218+ }
219+ } ) ;
220+ }
221+
222+ return manifestObjects ;
223+ }
224+
225+ export function getManifestFileUrlsFromArgumentsInput ( fileArgs : string ) : string [ ] {
226+ let manifestFileUrls : string [ ] = [ ] ;
227+ const filePathMatch : string [ ] = fileArgs . split ( matchPatternForFileArgument ) ;
228+ if ( filePathMatch && filePathMatch . length > 0 ) {
200229 filePathMatch . forEach ( manifestPath => {
201230 if ( ! ! manifestPath ) {
202- manifestFilePaths . push ( manifestPath . trim ( ) )
231+ if ( manifestPath . startsWith ( "http" ) || manifestPath . startsWith ( "https:" ) ) {
232+ manifestFileUrls . push ( manifestPath ) ;
233+ }
234+ else {
235+ manifestFileUrls . push ( ...getManifestUrls ( [ manifestPath ] ) ) ;
236+ }
203237 }
204238 } ) ;
205239 }
206240
207- return manifestFilePaths ;
241+ return manifestFileUrls ;
242+ }
243+
244+ export function getManifestFileUrlsFromHelmOutput ( helmOutput : string ) : string [ ] {
245+ const chartType = tl . getInput ( "chartType" , true ) ;
246+ // Raw github links are supported only for chart names not chart paths
247+ if ( chartType === "Name" ) {
248+ const chartName = tl . getInput ( "chartName" , true ) ;
249+ if ( chartName . startsWith ( "http:" ) || chartName . startsWith ( "https:" ) ) {
250+ return [ chartName ] ;
251+ }
252+ }
253+
254+ let manifestFilePaths : string [ ] = [ ] ;
255+ // Extract the chart directory
256+ const directoryName = getChartDirectoryName ( helmOutput ) ;
257+ // Extract all source paths; source path example - # Source: MyChart/templates/pod.yaml
258+ const filePathMatches = helmOutput . match ( matchPatternForSource ) ;
259+ if ( filePathMatches && filePathMatches . length >= 1 ) {
260+ filePathMatches . forEach ( filePathMatch => {
261+ // Strip the Chart name from source path to get the template path
262+ let indexOfTemplate = filePathMatch . toLowerCase ( ) . indexOf ( "templates" ) ;
263+ const templatePath = indexOfTemplate >= 0 ? filePathMatch . substr ( indexOfTemplate ) : filePathMatch ;
264+ manifestFilePaths . push ( directoryName + "/" + templatePath . trim ( ) ) ;
265+ } ) ;
266+ }
267+
268+ return getManifestUrls ( manifestFilePaths ) ;
269+ }
270+
271+ export function getChartDirectoryName ( helmOutput : string ) : string {
272+ // The output contains chart path in the following format - CHART PATH: C:\agent\_work\2\s\helm-chart-directory
273+ let directoryName = "" ;
274+ const chartPathMatch = helmOutput . match ( matchPatternForChartPath ) ;
275+ if ( chartPathMatch && chartPathMatch . length >= 1 ) {
276+ let fullPath = chartPathMatch [ 1 ] ;
277+ let indexOfLastSeparator = fullPath . lastIndexOf ( "\\" ) ;
278+ directoryName = indexOfLastSeparator >= 0 ? fullPath . substr ( indexOfLastSeparator + 1 ) : fullPath ;
279+ }
280+
281+ return directoryName ;
282+ }
283+
284+ export function getManifestUrls ( manifestFilePaths : string [ ] ) : string [ ] {
285+ let manifestUrls = [ ] ;
286+ const branchName = getBranchName ( branch ) ;
287+ for ( const path of manifestFilePaths ) {
288+ let manifestUrl = "" ;
289+ let normalisedPath = path . indexOf ( workingDirectory ) === 0 ? path . substr ( workingDirectory . length ) : path ;
290+ normalisedPath = normalisedPath . replace ( / \\ / g, "/" ) ;
291+
292+ if ( repositoryProvider && ( repositoryProvider . toLowerCase ( ) === "githubenterprise" || repositoryProvider . toLowerCase ( ) === "github" ) ) {
293+ if ( normalisedPath . indexOf ( "/" ) != 0 ) {
294+ // Prepend "/" if not present in path beginning as the path is appended as it is in manifest url to access github repo
295+ normalisedPath = "/" + normalisedPath ;
296+ }
297+
298+ manifestUrl = repositoryUrl + "/blob/" + branchName + normalisedPath ;
299+ }
300+ else if ( repositoryProvider && repositoryProvider . toLowerCase ( ) === "tfsgit" ) {
301+ if ( normalisedPath . indexOf ( "/" ) === 0 ) {
302+ // Remove "/" from path if present in the beginning as we need to append path as a query string in manifest url to access tfs repo
303+ normalisedPath = normalisedPath . substr ( 1 ) ;
304+ }
305+
306+ manifestUrl = repositoryUrl + "?path=" + normalisedPath ;
307+ }
308+
309+ manifestUrls . push ( manifestUrl ) ;
310+ }
311+
312+ return manifestUrls ;
313+ }
314+
315+ function getBranchName ( ref : string ) : string {
316+ const gitRefsHeadsPrefix = "refs/heads/" ;
317+ if ( ref && ref . indexOf ( gitRefsHeadsPrefix ) === 0 ) {
318+ return ref . substr ( gitRefsHeadsPrefix . length ) ;
319+ }
320+
321+ return ref ;
208322}
209323
210324function getPlatform ( ) : string {
0 commit comments