aboutsummaryrefslogtreecommitdiffhomepage
path: root/lib/datasource/helm/index.ts
blob: d646d07d2178fd7de7056ba3ec2997237d97f70b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import yaml from 'js-yaml';

import { logger } from '../../logger';
import * as globalCache from '../../util/cache/global';
import { Http } from '../../util/http';
import { ensureTrailingSlash } from '../../util/url';
import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common';

export const id = 'helm';

const http = new Http(id);

export const defaultRegistryUrls = [
  'https://kubernetes-charts.storage.googleapis.com/',
];

export async function getRepositoryData(
  repository: string
): Promise<ReleaseResult[]> {
  const cacheNamespace = 'datasource-helm';
  const cacheKey = repository;
  const cachedIndex = await globalCache.get(cacheNamespace, cacheKey);
  // istanbul ignore if
  if (cachedIndex) {
    return cachedIndex;
  }
  let res: any;
  try {
    res = await http.get('index.yaml', {
      baseUrl: ensureTrailingSlash(repository),
    });
    if (!res || !res.body) {
      logger.warn(`Received invalid response from ${repository}`);
      return null;
    }
  } catch (err) {
    // istanbul ignore if
    if (err.code === 'ERR_INVALID_URL') {
      logger.debug(
        { helmRepository: repository },
        'helm repository is not a valid URL - skipping'
      );
      return null;
    }
    // istanbul ignore if
    if (
      err.code === 'ENOTFOUND' ||
      err.code === 'EAI_AGAIN' ||
      err.code === 'ETIMEDOUT'
    ) {
      logger.debug({ err }, 'Could not connect to helm repository');
      return null;
    }
    if (err.statusCode === 404 || err.code === 'ENOTFOUND') {
      logger.debug({ err }, 'Helm Chart not found');
      return null;
    }
    if (
      err.statusCode === 429 ||
      (err.statusCode >= 500 && err.statusCode < 600)
    ) {
      throw new DatasourceError(err);
    }
    // istanbul ignore if
    if (err.name === 'UnsupportedProtocolError') {
      logger.debug({ repository }, 'Unsupported protocol');
      return null;
    }
    logger.warn(
      { err },
      `helm datasource ${repository} lookup failure: Unknown error`
    );
    return null;
  }
  try {
    const doc = yaml.safeLoad(res.body, { json: true });
    if (!doc) {
      logger.warn(`Failed to parse index.yaml from ${repository}`);
      return null;
    }
    const result: ReleaseResult[] = Object.entries(doc.entries).map(
      ([k, v]: [string, any]): ReleaseResult => ({
        name: k,
        homepage: v[0].home,
        sourceUrl: v[0].sources ? v[0].sources[0] : undefined,
        releases: v.map((x: any) => ({
          version: x.version,
          releaseTimestamp: x.created ? x.created : null,
        })),
      })
    );
    const cacheMinutes = 20;
    await globalCache.set(cacheNamespace, cacheKey, result, cacheMinutes);
    return result;
  } catch (err) {
    logger.warn(`Failed to parse index.yaml from ${repository}`);
    logger.debug(err);
    return null;
  }
}

export async function getReleases({
  lookupName,
  registryUrls,
}: GetReleasesConfig): Promise<ReleaseResult | null> {
  const [helmRepository] = registryUrls;
  if (!helmRepository) {
    logger.warn(`helmRepository was not provided to getReleases`);
    return null;
  }
  const repositoryData = await getRepositoryData(helmRepository);
  if (!repositoryData) {
    logger.debug(`Couldn't get index.yaml file from ${helmRepository}`);
    return null;
  }
  const releases = repositoryData.find((chart) => chart.name === lookupName);
  if (!releases) {
    logger.debug(
      { dependency: lookupName },
      `Entry ${lookupName} doesn't exist in index.yaml from ${helmRepository}`
    );
    return null;
  }
  return releases;
}