-
Notifications
You must be signed in to change notification settings - Fork 6
/
krew-plugins.nix
125 lines (124 loc) · 4.5 KB
/
krew-plugins.nix
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
# The Krew repository lists available plugin artifacts in YAML files. This
# module consumes the Krew repository to automatically create a set of Nix
# derivations. Most plugins are statically compiled binaries, but some are shell
# scripts or may have external dependencies (which Krew plugin definitions do
# not express), so YMMV.
{ autoPatchelfHook
, buildPackages
, fetchFromGitHub
, go
, lib
, stdenv
, targetPlatform
, unzip
}:
let
inherit (builtins) filter length elem any replaceStrings;
inherit (lib)
head nameValuePair assertMsg filesystem id listToAttrs concatStringsSep
licenses;
pluginDerivations = listToAttrs (map
(yamlFile:
let
pluginDefinition = readYaml yamlFile;
pluginName = pluginDefinition.metadata.name;
in
nameValuePair pluginName (mkPlugin pluginDefinition))
allPluginDefinitions);
mkPlugin = pluginDefinition:
let
pluginName = pluginDefinition.metadata.name;
matchingPlatforms =
filter isPlatformMatch pluginDefinition.spec.platforms;
selectedPlatform = assert (assertMsg (length matchingPlatforms > 0)
"target platform is not supported by plugin ${pluginName}");
head matchingPlatforms;
# Plugin files to be installed are sometimes listed as from/to-pairs (copy
# from foo to bar). The “from” value may be preceeded by a slash, so we
# need to force a relative path.
copyPluginFilesCommands =
if selectedPlatform ? files then
map (fromTo: "cp -a ./${fromTo.from} $out/lib/${fromTo.to}")
selectedPlatform.files
else
[ "cp -a * $out/lib" ];
# When plugin definitions contain dashes, such as “foo-bar”, Krew
# implicitly transforms dashes into underscores. This ensures that the
# plugin can be invoked as “kubectl foo-bar”, instead of “kubectl foo
# bar”.
pluginBinaryName = "kubectl-${replaceStrings [ "-" ] [ "_" ] pluginName}";
in
stdenv.mkDerivation {
pname = "kubectl-krew-plugin-${pluginName}";
version = pluginDefinition.spec.version;
src = builtins.fetchurl {
url = selectedPlatform.uri;
sha256 = selectedPlatform.sha256;
};
sourceRoot = ".";
dontBuild = true;
nativeBuildInputs = [ unzip ] ++ lib.optionals (stdenv.isLinux) [ autoPatchelfHook ];
installPhase = ''
runHook preInstall
mkdir -p $out/{bin,lib}
${concatStringsSep "\n" copyPluginFilesCommands}
ln -s $out/lib/${selectedPlatform.bin} $out/bin/${pluginBinaryName}
runHook postInstall
'';
meta = {
description = pluginDefinition.spec.description or "Plugin for kubectl";
platforms =
if length matchingPlatforms > 0 then
[ targetPlatform.system ]
else
[ ];
};
};
krewIndex = fetchFromGitHub {
owner = "kubernetes-sigs";
repo = "krew-index";
rev = "400c05bc0e4e64a287a8773435d5d4f45dd615d2";
sha256 = "sha256-fIgenKymQO9qD1GQRysB1GRWfdGiMVp88X/MVks8ClE=";
};
allPluginDefinitions = filesystem.listFilesRecursive "${krewIndex}/plugins";
# Krew is using Golang terminology when listing plugin artifacts by platform.
targetOs = go.GOOS;
targetArch = go.GOARCH;
# Krew plugin definitions list artifact URLs in conjunction with selectors to
# determine plugin artifacts to install.
isPlatformMatch = platform:
if platform.selector ? matchExpressions then
isPlatformMatchByExpressions platform
else
(if platform.selector ? matchLabels then
isPlatformMatchByLabels platform
else
abort "unhandled switch case");
isPlatformMatchByExpressions = platform:
let
matchesExpression = expression:
assert expression.operator == "In";
assert expression.key == "os";
elem targetOs expression.values;
matchResults = map matchesExpression platform.selector.matchExpressions;
in
any id matchResults;
isPlatformMatchByLabels = platform:
let
matchesOs = platform.selector.matchLabels.os or targetOs == targetOs;
matchesArch = platform.selector.matchLabels.arch or targetArch
== targetArch;
in
matchesOs && matchesArch;
readYaml = yamlFile:
let
jsonFile = buildPackages.runCommand "read-yaml"
{
allowSubstitutes = false;
preferLocalBuild = true;
}
"${buildPackages.remarshal}/bin/remarshal -if yaml -i ${yamlFile} -of json -o $out";
in
builtins.fromJSON (builtins.readFile jsonFile);
in
pluginDerivations