diff --git a/coverage.txt b/coverage.txt
index 11ed75c5..3a7dc270 100644
--- a/coverage.txt
+++ b/coverage.txt
@@ -1,1715 +1,29 @@
mode: atomic
-mode: atomic
-mode: atomic
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:341.36,343.16 2 4
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:343.16,344.13 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:347.2,462.3 1 4
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:479.157,481.16 2 4
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:481.16,483.3 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:485.2,486.16 2 4
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:486.16,488.3 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:491.2,491.27 1 4
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:491.27,493.3 1 2
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:495.2,507.23 2 4
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:510.67,512.24 2 8
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:512.24,514.3 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:515.2,516.32 2 8
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:519.43,521.67 2 4
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:521.67,523.3 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:524.2,525.24 2 4
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:528.64,532.16 3 4
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:532.16,534.3 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:536.2,536.20 1 4
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:539.72,543.2 2 4
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:545.78,548.45 2 6
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:548.45,549.25 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:549.25,551.18 2 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:551.18,553.5 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:554.4,554.16 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:555.9,557.4 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:560.2,561.16 2 6
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:561.16,563.3 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:565.2,565.54 1 6
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:565.54,567.3 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:569.2,569.18 1 6
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:576.83,578.16 2 2
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:578.16,580.3 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:582.2,582.49 1 2
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:582.49,584.3 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:586.2,587.16 2 2
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:587.16,589.3 1 0
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:591.2,591.49 1 2
-github.com/jesseduffield/lazydocker/pkg/config/app_config.go:595.45,597.2 1 4
-github.com/jesseduffield/lazydocker/pkg/config/config_default_platform.go:7.42,12.2 1 4
-mode: atomic
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:29.50,31.54 2 3
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:31.54,33.3 1 2
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:34.2,35.31 2 1
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:35.31,37.3 1 1
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:38.2,38.14 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:42.50,44.33 2 8
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:44.33,46.3 1 1
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:47.2,47.61 1 7
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:52.71,54.37 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:54.37,56.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:57.2,58.41 2 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:67.43,68.46 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:68.46,70.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:71.2,73.36 3 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:73.36,78.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:79.2,79.38 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:79.38,84.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:85.2,85.38 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:85.38,90.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:91.2,91.38 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:91.38,96.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:97.2,97.30 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:102.79,105.2 2 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:109.66,111.2 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:114.44,118.2 3 4
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:121.22,127.2 5 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:130.79,131.36 1 7
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:131.36,133.3 1 5
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:134.2,134.12 1 7
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:138.24,139.11 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:139.11,141.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:142.2,142.10 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:146.59,147.28 1 3
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:147.28,149.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:150.2,150.41 1 3
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:150.41,152.3 1 1
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:154.2,157.54 3 2
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:161.36,164.2 2 16
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:166.50,167.31 1 5
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:167.31,169.3 1 2
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:170.2,171.27 2 3
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:171.27,172.40 1 4
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:172.40,174.43 2 8
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:174.43,176.5 1 4
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:179.2,179.18 1 3
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:182.81,184.43 2 3
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:184.43,185.28 1 6
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:185.28,186.12 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:188.3,188.38 1 6
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:188.38,190.4 1 6
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:191.3,191.57 1 6
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:193.2,193.29 1 3
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:198.57,199.39 1 5
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:199.39,200.43 1 10
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:200.43,202.4 1 2
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:204.2,204.13 1 3
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:207.38,210.29 3 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:210.29,211.26 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:211.26,213.4 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:213.9,215.22 2 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:215.22,217.5 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:218.4,218.14 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:221.2,221.16 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:224.39,227.29 3 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:227.29,228.26 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:228.26,230.4 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:230.9,232.22 2 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:232.22,234.5 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:235.4,235.14 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:238.2,238.16 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:241.59,245.2 3 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:248.52,264.13 3 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:264.13,266.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:267.2,267.27 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:271.52,286.13 3 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:286.13,288.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:289.2,289.22 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:293.38,295.29 2 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:295.29,297.22 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:297.22,299.4 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:301.2,301.33 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:305.65,307.2 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:310.57,311.17 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:311.17,313.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:315.2,318.21 3 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:318.21,320.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:321.2,322.27 2 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:322.27,324.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:326.2,326.15 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:331.34,334.24 3 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:334.24,336.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:337.2,337.19 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:340.43,342.28 2 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:342.28,344.17 2 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:344.17,346.4 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:348.2,348.19 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:348.19,350.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:351.2,351.12 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:354.49,355.22 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:355.22,357.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:357.8,359.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:362.37,363.32 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:363.32,365.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:367.2,367.17 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:367.17,369.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:371.2,371.29 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:371.29,372.15 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:373.117,374.12 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:375.11,376.16 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:380.2,380.13 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:384.40,386.2 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:390.56,392.2 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:394.73,397.16 2 3
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:397.16,399.3 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:400.2,400.16 1 3
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:401.14,402.23 1 1
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:403.14,406.63 2 1
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:406.63,408.4 1 0
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:409.3,409.34 1 1
-github.com/jesseduffield/lazydocker/pkg/utils/utils.go:410.10,411.86 1 1
-mode: atomic
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:20.51,22.36 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:22.36,23.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:23.26,25.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:27.2,27.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:30.55,38.2 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:40.50,41.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:41.26,43.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:44.2,45.39 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:45.39,47.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:48.2,48.23 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:52.70,53.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:53.12,56.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:56.16,58.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:60.3,60.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:60.13,63.23 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:63.23,65.24 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:65.24,67.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:68.5,68.75 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:68.75,70.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:74.3,74.29 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:74.29,75.42 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:75.42,77.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/app_status_manager.go:81.2,81.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:16.110,20.52 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:20.52,22.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:24.2,28.18 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:28.18,30.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:32.2,34.21 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:34.21,36.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:38.2,64.60 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:67.51,76.68 5 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:76.68,79.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:79.8,80.42 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:80.42,82.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:82.9,82.49 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:82.49,84.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:87.2,87.45 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:90.95,93.24 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:93.24,100.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:102.2,102.29 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:102.29,113.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:115.2,129.15 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:132.42,133.100 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:133.100,135.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:137.2,137.83 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:137.83,139.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:142.75,146.80 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:146.80,147.55 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:147.55,148.31 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:148.31,153.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:153.10,158.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:161.3,161.76 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:161.76,163.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:165.8,165.25 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:165.25,167.66 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:167.66,168.59 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:168.59,173.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:175.4,175.21 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:178.3,183.76 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:183.76,185.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:186.8,188.19 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:188.19,190.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:192.3,192.62 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:192.62,193.31 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:193.31,198.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:198.10,203.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:206.3,206.76 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/arrangement.go:206.76,208.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:16.127,17.49 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:17.49,18.55 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:18.55,20.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:22.3,22.22 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:22.22,23.41 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:23.41,25.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:28.3,28.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:32.49,33.42 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:33.42,35.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:36.2,38.12 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:41.76,45.10 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:45.10,46.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:46.30,48.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:49.8,51.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:52.2,52.18 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:55.95,63.2 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:65.106,68.16 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:68.16,70.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:71.2,72.54 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:75.86,79.16 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:79.16,81.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:82.2,83.15 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:83.15,85.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:86.2,88.40 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:88.40,90.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:91.2,91.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:94.35,97.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:102.133,104.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:106.142,108.40 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:108.40,109.46 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:109.46,110.56 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:110.56,112.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:114.3,115.17 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:115.17,117.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:118.3,119.69 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:119.69,121.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:122.3,122.59 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:124.2,124.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:127.116,129.135 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:129.135,131.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:132.2,132.124 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:132.124,134.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:136.2,136.131 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:136.131,138.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:139.2,139.122 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:139.122,141.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:143.2,143.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:146.56,150.2 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/confirmation_panel.go:152.51,158.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:19.89,21.64 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:21.64,23.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:26.41,26.64 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:32.127,34.15 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:34.15,36.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:38.2,40.73 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:40.73,42.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:46.2,48.6 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:48.6,49.10 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:50.21,51.10 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:52.19,54.18 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:54.18,58.5 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:59.4,59.28 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:59.28,61.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:66.67,71.12 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:71.12,75.3 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:77.2,77.40 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:77.40,80.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:82.2,82.15 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:82.15,83.40 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:83.40,85.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:88.2,88.74 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:88.74,91.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:93.2,93.22 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:96.34,97.50 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:97.50,101.41 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:101.41,103.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:107.112,116.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:116.16,118.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:120.2,120.63 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:120.63,122.17 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:122.17,124.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:125.8,127.17 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:127.17,129.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/container_logs.go:132.2,132.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:22.81,24.68 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:24.68,25.54 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:25.54,27.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:29.3,29.98 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:29.98,31.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:34.2,36.62 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:36.62,64.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:65.71,72.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:82.65,84.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:85.52,89.89 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:89.89,91.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:93.4,93.80 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:93.80,95.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:97.4,97.15 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:109.89,110.16 1 7
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:110.16,112.3 1 3
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:114.2,116.29 3 4
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:116.29,118.3 1 1
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:120.2,120.80 1 3
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:123.82,124.53 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:124.53,124.91 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:127.68,128.32 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:128.32,130.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:132.2,132.44 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:132.44,134.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:136.2,136.90 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:136.90,140.24 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:140.24,142.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:143.3,146.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:149.2,150.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:150.16,153.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:155.2,155.15 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:158.85,159.53 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:159.53,159.97 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:162.74,163.32 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:163.32,165.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:167.2,177.39 10 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:177.39,179.50 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:179.50,180.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:180.30,182.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:182.10,184.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:186.8,188.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:190.2,191.54 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:191.54,193.61 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:193.61,194.27 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:194.27,196.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:198.8,200.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:202.2,203.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:203.16,205.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:207.2,209.15 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:212.84,214.64 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:214.64,216.18 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:216.18,218.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:220.4,220.36 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:223.41,223.64 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:229.82,231.64 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:231.64,233.18 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:233.18,235.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:237.4,237.36 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:240.41,240.64 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:246.54,247.33 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:247.33,250.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:253.2,260.16 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:260.16,262.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:264.2,268.23 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:268.23,269.63 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:269.63,270.40 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:270.40,271.37 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:271.37,272.11 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:274.5,275.34 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:280.2,280.42 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:283.53,284.46 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:284.46,285.60 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:285.60,287.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:290.2,290.61 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:290.61,292.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:294.2,294.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:297.80,301.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:303.79,305.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:305.16,307.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:309.2,309.82 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:309.82,310.68 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:310.68,311.58 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:311.58,312.63 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:312.63,313.132 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:313.132,314.72 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:314.72,317.8 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:320.5,320.45 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:322.4,322.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:326.2,329.31 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:329.31,329.95 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:333.31,333.114 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:337.2,340.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:343.69,344.72 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:344.72,345.37 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:345.37,347.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:347.9,349.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:351.3,351.17 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:351.17,353.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:355.3,355.44 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:359.73,361.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:361.16,363.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:365.2,365.38 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:368.72,370.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:370.16,372.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:374.2,374.115 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:374.115,375.68 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:375.68,376.43 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:376.43,378.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:380.4,380.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:385.75,387.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:387.16,389.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:391.2,391.69 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:391.69,392.45 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:392.45,394.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:396.3,396.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:400.74,402.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:402.16,404.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:406.2,407.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:407.16,409.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:411.2,411.29 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:414.47,415.124 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:415.124,416.67 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:416.67,418.18 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:418.18,420.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:421.4,421.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:426.76,428.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:428.16,430.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:432.2,434.12 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:437.78,439.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:439.16,441.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:443.2,443.42 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:446.73,456.2 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:458.82,460.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:460.16,462.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:464.2,470.67 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:473.46,474.123 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:474.123,475.68 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:475.68,476.71 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:476.71,477.44 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:477.44,479.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:482.4,482.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:487.48,488.125 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:488.125,489.68 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:489.68,490.71 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:490.71,491.93 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:491.93,493.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:496.4,496.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:501.80,521.2 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:524.89,526.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:526.16,528.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:530.2,530.46 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:533.77,535.41 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:535.41,537.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:539.2,540.19 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:540.19,542.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:543.2,544.21 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:544.21,546.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/containers_panel.go:547.2,548.37 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:12.154,13.96 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:13.96,16.27 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:16.27,17.39 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:17.39,19.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:21.4,22.21 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:22.21,24.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:27.4,27.22 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:27.22,29.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:31.4,31.61 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:31.61,32.69 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:32.69,34.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:35.5,35.15 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:39.3,45.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:48.2,51.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:54.124,56.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/custom_commands.go:58.122,60.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:9.42,11.9 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:11.9,13.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:15.2,15.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:15.30,17.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:19.2,22.42 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:25.55,29.2 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:31.169,32.78 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:32.78,34.14 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:34.14,35.73 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:35.73,37.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:39.3,39.17 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:43.44,44.42 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:44.42,46.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:48.2,48.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:51.37,58.18 6 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:58.18,60.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:62.2,64.29 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:68.38,69.35 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:69.35,70.43 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:70.43,72.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:75.2,75.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/filtering.go:78.39,80.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:8.53,9.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:9.14,11.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:13.2,14.8 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:14.8,16.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:18.2,18.18 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:19.22,20.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:21.14,23.13 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:24.16,25.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:26.10,27.59 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:32.56,37.2 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:39.59,43.64 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:43.64,45.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:47.2,49.49 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:49.49,51.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:53.2,55.106 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:55.106,56.43 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:56.43,58.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:62.2,62.40 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:62.40,64.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:66.2,66.36 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:69.37,73.35 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:73.35,75.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:77.2,79.16 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:79.16,81.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:83.2,83.41 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:86.55,90.89 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:90.89,92.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:96.39,99.22 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:99.22,100.90 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:100.90,102.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:106.2,106.44 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:106.44,108.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:111.2,111.89 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:111.89,113.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:115.2,115.57 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:119.48,123.53 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:123.53,124.65 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:124.65,126.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:129.2,129.39 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:132.46,137.39 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:137.39,140.49 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:140.49,142.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/focus.go:145.2,145.39 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gocui.go:25.52,26.32 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gocui.go:26.32,29.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gocui.go:31.2,32.13 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gocui.go:32.13,34.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gocui.go:35.2,35.27 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gocui.go:39.51,41.27 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gocui.go:41.27,43.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gocui.go:44.2,44.18 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:118.189,147.2 5 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:149.45,157.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:159.72,161.12 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:161.12,164.22 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:164.22,165.34 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:165.34,167.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:169.4,169.18 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:175.29,180.16 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:180.16,182.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:183.2,186.50 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:186.50,188.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:190.2,194.65 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:194.65,196.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:198.2,198.45 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:198.45,200.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:202.2,205.12 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:205.12,206.34 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:206.34,207.18 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:207.18,208.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:210.4,210.58 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:210.58,213.13 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:215.4,215.41 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:219.2,221.45 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:221.45,223.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:224.2,224.52 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:224.52,226.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:229.2,231.42 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:231.42,233.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:235.2,235.32 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:235.32,238.17 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:238.17,240.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:242.3,242.47 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:242.47,244.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:247.2,253.12 5 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:253.12,261.3 5 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:263.2,264.26 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:264.26,266.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:267.2,267.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:270.29,280.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:282.48,284.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:286.27,287.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:287.12,288.46 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:288.46,290.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:292.2,292.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:292.12,293.60 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:293.60,295.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:297.2,297.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:297.12,298.45 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:298.45,300.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:302.2,302.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:302.12,303.46 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:303.46,305.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:307.2,307.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:307.12,308.44 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:308.44,310.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:314.70,317.29 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:317.29,318.17 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:318.17,320.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:321.3,322.30 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:325.1,326.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:326.6,329.21 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:329.21,330.11 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:331.22,332.11 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:333.26,335.19 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:336.12,345.19 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:349.3,349.7 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:349.7,350.11 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:351.22,352.11 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:353.34,359.62 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:360.26,362.19 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:369.47,371.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:373.38,375.21 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:375.21,377.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:378.2,378.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:378.26,379.41 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:379.41,381.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:383.2,383.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:386.57,387.41 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:387.41,388.102 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:388.102,390.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:392.2,392.22 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:397.32,398.29 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:398.29,400.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:402.2,402.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:405.65,406.18 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:406.18,408.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:410.2,411.29 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:411.29,413.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:414.2,414.76 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:417.49,419.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:419.16,421.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:423.2,423.31 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:426.49,427.57 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:427.57,429.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:430.2,430.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:433.72,434.98 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:434.98,437.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:440.48,441.44 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:441.44,443.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:445.2,446.13 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:449.51,450.46 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:450.46,452.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:453.2,453.21 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:456.42,458.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:460.40,461.38 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:461.38,461.52 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:464.60,469.6 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:469.6,470.10 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:471.21,472.10 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:473.19,474.71 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:474.71,475.35 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:475.35,477.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:486.32,488.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:488.16,489.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:491.2,493.45 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:493.45,494.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/gui.go:497.2,497.17 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:21.73,26.58 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:26.58,34.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:35.63,37.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:45.57,46.50 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:46.50,48.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:50.4,50.50 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:50.50,52.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:54.4,54.24 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:54.24,56.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:58.4,58.22 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:58.22,60.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:62.4,62.22 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:68.77,70.32 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:70.32,70.68 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:76.62,86.16 9 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:86.16,88.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:90.2,92.15 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:95.38,96.49 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:96.49,98.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:100.2,100.41 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:103.44,105.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:105.16,107.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:109.2,111.12 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:114.55,115.79 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:115.79,117.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:119.2,119.32 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:122.75,130.16 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:130.16,132.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:134.2,160.86 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:160.86,166.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:166.26,167.62 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:167.62,169.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:171.5,171.15 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:176.2,179.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:182.43,183.120 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:183.120,184.67 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:184.67,186.18 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:186.18,188.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:189.4,189.29 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:194.78,196.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:196.16,198.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:200.2,206.67 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/images_panel.go:209.76,221.2 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:21.35,24.22 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:25.12,26.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:27.17,28.31 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:32.2,32.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:33.10,34.15 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:35.10,36.17 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:37.10,38.17 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:39.13,40.15 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:41.13,42.15 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:43.13,44.15 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:45.13,46.15 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:47.13,48.16 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:49.13,50.16 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:53.2,53.31 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:57.52,514.44 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:514.44,523.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:525.2,525.112 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:525.112,535.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:537.2,537.44 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:537.44,539.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:541.2,543.44 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:543.44,567.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:569.2,569.44 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:569.44,570.32 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:570.32,578.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:581.2,581.17 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:584.49,587.35 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:587.35,588.107 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:588.107,590.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:593.2,593.73 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:593.73,595.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:597.2,597.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:600.73,601.49 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/keybindings.go:601.49,603.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:10.59,12.34 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:12.34,14.45 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:14.45,16.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:18.3,18.67 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:18.67,22.4 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:23.3,23.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:27.39,29.37 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:29.37,31.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:32.2,32.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:35.65,36.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:36.14,38.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:40.2,40.39 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:40.39,42.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:45.2,47.40 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:50.40,51.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:51.14,53.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:55.2,57.42 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:61.44,69.89 5 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:69.89,73.17 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:73.17,75.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:77.3,77.10 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:77.10,85.4 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:87.3,88.17 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:88.17,90.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:91.3,101.19 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:104.2,104.57 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:104.57,106.58 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:106.58,108.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:115.2,115.39 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:118.52,119.17 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:119.17,121.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:123.2,124.8 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:124.8,126.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/layout.go:129.67,133.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:9.38,15.2 5 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:17.40,23.49 5 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:23.49,26.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:28.2,29.36 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:29.36,31.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:33.2,33.74 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:36.67,42.2 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:44.68,50.30 5 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:50.30,51.43 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:51.43,53.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:56.2,57.43 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:57.43,59.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:61.2,61.74 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:64.67,67.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:69.66,74.2 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:76.52,81.9 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:81.9,83.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:85.2,86.40 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:89.68,94.2 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:96.67,99.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:101.41,102.29 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:102.29,104.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:106.2,108.34 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:108.34,110.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/main_panel.go:112.2,112.40 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:16.71,27.28 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:27.28,29.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:38.61,39.46 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:39.46,41.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:43.2,43.29 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:43.29,45.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:47.2,47.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:50.41,52.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:52.16,54.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:56.2,56.42 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:59.52,60.22 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:60.22,64.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:64.26,66.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:70.2,72.34 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:72.34,73.31 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:73.31,75.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:77.3,77.21 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:77.21,79.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:81.3,81.67 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:84.2,84.34 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:84.34,85.45 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:85.45,89.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:92.2,95.55 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:95.55,97.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:99.2,102.40 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:107.43,114.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:116.41,121.47 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:121.47,122.43 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:122.43,124.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:129.3,129.44 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/menu_panel.go:132.2,132.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:18.77,21.60 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:21.60,29.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:30.67,32.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:43.61,45.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:50.79,51.53 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:51.53,51.93 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:54.68,67.41 12 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:67.41,69.48 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:69.48,71.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:72.8,74.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:76.2,80.15 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:83.40,84.51 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:84.51,86.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:88.2,88.43 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:91.46,93.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:93.16,95.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:97.2,99.12 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:102.77,104.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:104.16,106.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:108.2,120.88 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:120.88,123.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:123.26,124.70 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:124.70,125.45 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:125.45,127.7 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:128.6,128.16 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:134.2,137.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:140.45,141.122 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:141.122,142.67 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:142.67,144.18 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:144.18,146.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:147.4,147.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:152.80,154.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:154.16,156.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:158.2,164.67 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/networks_panel.go:167.78,179.2 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:10.55,15.35 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:15.35,16.58 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:16.58,17.28 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:18.12,19.53 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:20.18,21.51 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:27.2,27.25 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:27.25,29.36 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:29.36,30.59 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:30.59,31.48 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:31.48,33.47 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:33.47,34.50 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:34.50,35.18 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:38.6,38.52 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:46.2,47.49 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:50.76,51.32 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:51.32,53.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:55.2,55.88 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:55.88,58.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:58.26,59.27 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:59.27,61.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:63.5,63.33 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/options_menu_panel.go:68.2,72.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels.go:5.45,7.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:22.76,25.60 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:25.60,26.49 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:26.49,44.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:46.5,52.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:54.67,56.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:66.61,68.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:75.40,78.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:80.41,82.46 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:82.46,83.66 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:83.66,85.53 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:85.53,87.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:91.2,91.20 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:94.74,95.53 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:95.53,95.80 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:98.37,113.2 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:115.74,119.35 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:119.35,135.14 7 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:135.14,137.51 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:137.51,139.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:142.4,142.18 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:147.86,148.53 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:148.53,150.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:153.69,155.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:157.69,159.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:161.31,172.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:175.70,177.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:177.16,179.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/project_panel.go:181.2,181.29 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:20.77,23.60 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:23.60,51.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:52.67,53.33 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:53.33,55.6 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:56.5,56.107 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:66.61,67.48 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:67.48,69.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:71.4,71.48 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:71.48,73.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:75.4,75.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:78.21,80.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:84.88,85.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:85.30,86.54 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:86.54,86.83 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:89.2,89.53 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:92.85,93.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:93.30,94.54 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:94.54,94.83 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:97.2,97.50 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:100.78,101.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:101.30,102.54 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:102.54,102.83 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:105.2,105.52 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:108.76,110.64 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:110.64,112.18 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:112.18,114.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:116.4,116.36 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:119.41,119.64 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:125.77,126.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:126.30,127.54 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:127.54,127.93 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:130.2,130.57 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:139.54,141.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:143.76,145.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:145.16,147.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:149.2,162.82 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:162.82,165.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:165.26,166.70 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:166.70,167.69 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:167.69,169.7 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:171.6,171.16 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:177.2,180.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:183.71,185.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:185.16,187.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:188.2,188.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:188.30,190.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:192.2,192.46 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:195.70,197.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:197.16,199.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:201.2,201.113 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:201.113,202.68 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:202.68,203.41 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:203.41,205.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:207.4,207.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:212.68,214.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:214.16,216.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:218.2,218.72 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:218.72,219.38 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:219.38,221.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:223.3,223.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:227.73,229.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:229.16,231.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:233.2,233.69 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:233.69,234.43 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:234.43,236.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:238.3,238.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:242.71,244.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:244.16,246.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:248.2,248.67 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:248.67,249.41 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:249.41,251.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:253.3,253.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:257.72,259.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:259.16,261.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:263.2,263.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:263.30,265.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:267.2,268.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:268.16,270.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:272.2,272.29 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:275.82,277.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:277.16,279.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:281.2,282.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:282.16,284.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:286.2,286.29 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:289.68,290.118 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:290.118,296.73 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:296.73,297.59 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:297.59,299.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:300.4,300.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:305.70,320.26 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:320.26,321.69 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:321.69,322.66 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:322.66,324.7 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:325.6,325.16 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:332.26,333.69 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:333.69,334.77 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:334.77,336.7 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:337.6,337.16 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:343.2,343.82 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:343.82,348.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:350.2,353.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:356.77,358.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:358.16,360.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:362.2,379.26 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:379.26,380.72 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:380.72,381.46 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:381.46,383.7 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:384.6,384.16 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:394.26,395.72 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:395.72,396.70 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:396.70,398.7 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:399.6,399.16 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:409.26,411.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:415.2,415.82 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:415.82,420.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:422.2,425.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:428.80,430.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:430.16,432.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:434.2,444.44 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:444.44,445.33 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:445.33,447.14 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:449.3,449.48 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:449.48,450.35 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:450.35,453.15 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:458.2,458.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:458.30,460.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:462.2,462.67 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:465.78,470.2 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:472.76,474.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:474.16,476.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:478.2,479.22 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:479.22,481.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:483.2,483.42 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:486.87,488.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:488.16,490.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:492.2,493.22 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:493.22,495.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/services_panel.go:497.2,497.46 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/subprocess.go:15.52,19.40 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/subprocess.go:19.40,21.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/subprocess.go:23.2,27.39 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/subprocess.go:27.39,29.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/subprocess.go:31.2,33.12 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/subprocess.go:36.43,44.12 6 0
-github.com/jesseduffield/lazydocker/pkg/gui/subprocess.go:44.12,48.49 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/subprocess.go:48.49,50.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/subprocess.go:53.2,55.34 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/subprocess.go:55.34,59.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/subprocess.go:61.2,65.22 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:10.62,12.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:34.79,38.35 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:38.35,40.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:43.2,43.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:47.84,53.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:55.55,56.35 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:56.35,62.3 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:68.67,71.36 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:71.36,72.25 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:72.25,74.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:75.3,79.7 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:79.7,80.11 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:81.25,83.11 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:84.22,86.11 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:87.22,89.34 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/tasks_adapter.go:94.2,100.30 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/theme.go:8.60,10.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/theme.go:13.40,19.2 5 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:15.61,18.65 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:18.65,20.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:20.8,22.32 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:22.32,23.36 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:23.36,25.10 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:27.4,27.33 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:27.33,30.5 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:33.2,34.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:34.16,35.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:37.2,38.37 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:41.65,44.46 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:44.46,46.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:46.8,48.32 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:48.32,49.36 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:49.36,51.10 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:53.4,53.33 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:53.33,56.5 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:59.2,60.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:60.16,61.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:63.2,64.37 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:67.33,70.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:74.88,75.44 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:75.44,77.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:78.2,89.29 9 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:89.29,91.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:91.8,91.34 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:91.34,93.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:95.2,95.29 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:95.29,98.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:100.2,100.22 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:100.22,102.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:104.2,105.22 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:105.22,107.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:110.69,112.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:114.44,117.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:119.46,122.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:124.63,128.2 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:131.70,132.34 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:132.34,134.17 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:134.17,136.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:137.3,137.43 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:137.43,139.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:140.3,140.43 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:140.43,142.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:143.3,143.34 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:145.2,145.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:148.44,150.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:153.46,155.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:158.52,159.38 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:159.38,161.17 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:161.17,163.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:164.3,164.34 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:168.73,170.43 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:170.43,172.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:173.2,174.41 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:177.70,179.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:181.43,183.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:185.54,187.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:189.42,192.24 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:192.24,194.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:195.2,195.27 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:198.61,200.32 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:200.32,202.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:203.2,203.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:206.55,212.54 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:212.54,214.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:215.2,216.12 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:219.44,221.28 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:222.14,223.33 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:224.22,225.41 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:227.2,227.34 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:230.52,232.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:234.42,236.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:238.33,243.2 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:245.111,246.65 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:246.65,248.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:249.2,249.76 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:252.137,253.72 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:253.72,255.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:257.2,262.25 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:262.25,264.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:266.2,266.35 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:266.35,268.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:270.2,272.39 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:272.39,273.44 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:273.44,275.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:278.2,278.31 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:281.40,282.37 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:282.37,286.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:288.2,290.12 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:293.40,294.37 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:294.37,298.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:300.2,302.12 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:305.93,306.25 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:306.25,307.21 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:307.21,308.22 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:308.22,310.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:311.4,311.18 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:314.2,314.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:317.93,318.25 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:318.25,319.21 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:319.21,320.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:320.13,322.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:323.4,323.24 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:326.2,326.22 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:329.43,331.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:333.66,336.48 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:336.48,337.45 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:337.45,339.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:342.2,342.19 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:346.66,349.48 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:349.48,350.45 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:350.45,352.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:355.2,355.19 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:358.57,367.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:369.57,371.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/view_helpers.go:373.54,375.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:19.29,20.27 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:20.27,23.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:25.2,25.24 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:65.61,92.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:94.40,96.56 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:96.56,98.58 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:98.58,100.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:101.3,101.50 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:104.2,118.94 10 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:118.94,120.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:120.8,122.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:124.2,164.12 31 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:167.47,168.92 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:168.92,170.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:172.2,174.12 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:177.48,179.18 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:179.18,181.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:183.2,184.24 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:184.24,186.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:188.2,189.38 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:192.43,194.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:197.52,198.102 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:198.102,200.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:202.2,202.75 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/views.go:202.75,204.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:18.75,21.59 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:21.59,29.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:30.65,32.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:43.59,44.61 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:44.61,46.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:47.4,47.61 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:47.61,49.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:50.4,50.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:56.76,57.53 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:57.53,57.91 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:60.65,71.33 10 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:71.33,73.42 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:73.42,75.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:76.8,78.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:80.2,80.36 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:80.36,83.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:85.2,85.15 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:88.39,89.50 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:89.50,91.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:93.2,93.42 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:96.45,98.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:98.16,100.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:102.2,104.12 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:107.76,109.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:109.16,111.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:113.2,132.87 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:132.87,135.26 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:135.26,136.70 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:136.70,137.56 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:137.56,139.7 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:140.6,140.16 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:146.2,149.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:152.44,153.121 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:153.121,154.67 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:154.67,156.18 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:156.18,158.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:159.4,159.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:164.79,166.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:166.16,168.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:170.2,176.67 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/volumes_panel.go:179.77,191.2 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/window.go:10.50,12.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/window.go:14.48,16.2 1 0
-mode: atomic
-mode: atomic
-github.com/jesseduffield/lazydocker/pkg/gui/panels/context_state.go:33.58,34.71 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/context_state.go:34.71,36.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/context_state.go:39.66,41.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/context_state.go:43.61,45.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/context_state.go:47.50,50.20 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/context_state.go:50.20,52.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/context_state.go:54.2,54.53 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/context_state.go:57.50,60.20 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/context_state.go:60.20,62.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/context_state.go:64.2,64.65 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/context_state.go:67.57,69.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:16.55,18.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:20.50,26.30 5 2
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:26.30,28.3 1 4
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:31.63,36.37 4 2
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:36.37,37.22 1 6
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:37.22,39.4 1 3
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:43.57,47.17 3 2
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:47.17,49.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:51.2,51.47 1 2
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:51.47,53.3 1 5
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:56.47,61.2 3 3
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:63.58,67.45 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:67.45,70.3 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:72.2,72.49 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:76.40,81.2 3 2
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:83.51,87.37 3 3
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:87.37,88.35 1 4
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:88.35,90.4 1 2
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:92.2,92.11 1 1
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:95.45,100.37 4 2
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:100.37,102.3 1 4
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:103.2,103.15 1 2
-github.com/jesseduffield/lazydocker/pkg/gui/panels/filtered_list.go:106.48,111.2 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/list_panel.go:14.57,16.25 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/list_panel.go:16.25,18.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/list_panel.go:20.2,20.33 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/list_panel.go:23.50,26.33 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/list_panel.go:26.33,28.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/list_panel.go:32.55,34.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/list_panel.go:36.44,38.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/list_panel.go:40.44,42.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:80.51,85.95 4 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:85.95,87.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:89.2,89.25 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:89.25,91.17 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:91.17,93.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:96.2,96.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:99.53,101.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:103.52,105.16 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:105.16,106.41 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:106.41,108.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:110.3,110.32 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:110.32,111.53 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:111.53,111.83 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:114.3,114.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:117.2,119.33 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:122.59,123.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:123.30,125.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:127.2,128.34 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:128.34,130.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:132.2,138.33 5 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:141.60,145.9 3 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:145.9,148.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:150.2,150.18 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:153.54,157.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:159.54,163.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:165.57,166.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:166.30,168.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:170.2,172.28 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:175.57,176.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:176.30,178.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:180.2,182.28 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:185.41,187.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:189.51,192.2 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:194.47,197.48 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:197.48,198.47 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:198.47,200.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:202.3,202.67 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:202.67,203.78 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:203.78,205.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:206.6,208.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:210.3,210.25 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:210.25,211.78 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:211.78,213.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:216.3,216.14 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:219.2,221.29 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:224.52,227.31 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:227.31,229.74 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:229.74,231.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:232.3,233.17 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:233.17,235.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:236.3,238.29 2 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:238.29,239.44 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:239.44,241.5 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:244.3,244.40 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:244.40,246.4 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:247.3,247.13 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:250.2,250.12 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:253.58,254.30 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:254.30,256.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:258.2,258.42 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:261.55,263.2 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:265.47,266.22 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:266.22,268.3 1 0
-github.com/jesseduffield/lazydocker/pkg/gui/panels/side_list_panel.go:270.2,270.20 1 0
-mode: atomic
-mode: atomic
-mode: atomic
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:46.78,48.86 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:48.86,49.100 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:49.100,55.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:56.3,56.13 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:59.2,59.12 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:63.34,66.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:69.35,72.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:75.37,78.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:81.37,84.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:87.49,88.24 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:88.24,90.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:93.2,93.33 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:93.33,95.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:97.2,97.35 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:97.35,99.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:101.2,104.17 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:108.84,110.16 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:110.16,112.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:115.2,115.27 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:115.27,117.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:119.2,119.53 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:123.49,126.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:129.66,131.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:134.68,136.16 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:136.16,138.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:140.2,140.82 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container.go:144.42,146.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:138.68,143.23 4 1
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:143.23,145.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:146.2,146.14 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:150.66,152.23 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:152.23,154.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:155.2,155.14 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:158.82,164.2 4 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:167.64,168.22 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:168.22,170.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:172.2,172.37 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:172.37,173.48 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:173.48,176.4 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:180.59,184.23 4 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:184.23,186.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/container_stats.go:187.2,187.38 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:66.75,70.2 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:73.161,75.16 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:75.16,77.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:79.2,80.16 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:80.16,82.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:84.2,85.16 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:85.16,87.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:89.2,113.16 5 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:113.16,116.3 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:118.2,118.27 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:121.39,123.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:125.71,128.16 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:128.16,134.3 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:136.2,139.21 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:139.21,154.3 5 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:156.2,156.35 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:159.148,164.16 4 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:164.16,166.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:168.2,170.28 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:170.28,172.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:172.8,174.17 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:174.17,176.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:179.2,181.34 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:184.98,186.35 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:186.35,187.40 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:187.40,188.66 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:188.66,190.15 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:193.3,193.26 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:198.94,203.16 4 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:203.16,205.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:207.2,209.39 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:209.39,213.56 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:213.56,214.44 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:214.44,216.10 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:221.3,221.26 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:221.26,230.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:232.3,234.47 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:234.47,236.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:236.9,238.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:239.3,244.34 5 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:247.2,247.27 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:251.59,252.31 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:252.31,254.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:256.2,258.16 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:258.16,260.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:266.2,268.28 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:268.28,276.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:278.2,278.22 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:283.79,287.39 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:287.39,289.17 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:289.17,291.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:291.9,293.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:296.2,296.12 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:300.58,311.2 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:314.54,321.16 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:321.16,323.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:324.2,324.15 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:332.44,335.36 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:335.36,337.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:339.2,340.26 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:340.26,342.53 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:342.53,344.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:345.3,346.17 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:346.17,348.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:349.3,349.37 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:352.2,352.26 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:352.26,357.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:359.2,360.22 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:360.22,360.56 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:361.74,361.108 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:364.2,366.16 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:366.16,368.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:369.2,370.9 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:370.9,372.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:373.2,374.9 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:374.9,376.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:378.2,378.29 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:378.29,380.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/docker.go:388.2,388.31 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/dummies.go:14.37,16.2 1 19
-github.com/jesseduffield/lazydocker/pkg/commands/dummies.go:19.44,29.2 2 19
-github.com/jesseduffield/lazydocker/pkg/commands/dummies.go:32.34,36.2 3 19
-github.com/jesseduffield/lazydocker/pkg/commands/dummies.go:39.45,41.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/dummies.go:44.78,52.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/errors.go:18.33,19.16 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/errors.go:19.16,21.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/errors.go:23.2,23.28 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/errors.go:35.61,39.2 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/errors.go:42.52,44.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/errors.go:46.39,48.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/errors.go:51.45,53.35 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/errors.go:53.35,55.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/errors.go:56.2,56.14 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:31.70,32.85 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:32.85,34.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:36.2,36.12 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:39.85,41.25 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:41.25,43.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:45.2,46.18 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:46.18,48.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:49.2,50.23 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:50.23,52.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:54.2,56.64 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:56.64,60.3 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:62.2,66.18 4 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:66.18,68.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:70.2,75.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:79.49,81.16 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:81.16,83.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:85.2,85.85 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:85.85,87.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:89.2,92.33 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:96.59,98.16 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:98.16,100.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:102.2,104.31 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:104.31,107.20 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:107.20,109.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:111.3,114.25 4 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:114.25,118.88 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:118.88,119.40 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:119.40,121.11 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:126.3,135.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:138.2,138.23 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/image.go:142.45,145.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/network.go:23.63,25.16 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/network.go:25.16,27.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/network.go:29.2,31.35 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/network.go:31.35,40.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/network.go:42.2,42.25 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/network.go:46.47,49.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/network.go:52.34,54.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:42.75,50.2 1 19
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:54.71,56.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:59.74,65.2 5 5
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:68.102,74.2 5 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:77.76,79.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:82.56,85.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:88.71,91.2 2 5
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:94.99,97.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:99.73,102.32 2 2
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:102.32,111.3 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:111.8,113.3 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:115.2,115.86 1 2
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:119.54,122.2 2 3
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:125.50,127.16 2 4
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:127.16,129.3 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:130.2,130.22 1 3
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:130.22,132.3 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:133.2,133.15 1 2
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:136.71,138.16 2 5
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:138.16,142.9 2 3
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:142.9,144.4 1 2
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:145.3,145.28 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:147.2,147.26 1 2
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:151.53,160.2 5 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:163.49,172.2 5 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:176.66,178.18 2 4
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:178.18,180.3 1 3
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:181.2,181.18 1 4
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:181.18,182.50 1 2
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:182.50,184.4 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:186.2,186.18 1 4
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:186.18,188.3 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:190.2,190.51 1 3
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:194.88,196.2 1 3
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:199.50,201.32 2 5
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:201.32,207.3 2 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:207.8,215.3 2 4
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:216.2,216.32 1 5
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:221.52,223.2 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:226.67,228.16 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:228.16,230.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:231.2,234.16 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:234.16,236.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:237.2,237.12 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:241.78,243.16 2 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:243.16,246.3 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:248.2,248.56 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:248.56,251.3 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:252.2,252.40 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:252.40,255.3 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:257.2,257.28 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:261.51,264.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:267.59,268.41 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:268.41,269.25 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:269.25,271.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:272.3,272.20 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:274.2,274.18 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:280.61,284.16 4 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:284.16,285.26 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:285.26,287.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:288.3,288.31 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:290.2,290.12 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:294.48,296.16 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:296.16,298.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:299.2,299.29 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:303.64,305.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:308.66,311.37 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:311.37,313.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:315.2,315.35 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:315.35,317.17 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:317.17,319.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:321.3,321.27 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:327.2,332.27 4 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:332.27,334.13 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:334.13,336.18 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:336.18,338.5 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:340.4,340.45 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:340.45,342.5 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:344.4,344.52 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:344.52,345.19 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:345.19,347.6 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:350.4,350.44 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:350.44,352.5 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:354.4,354.13 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:358.2,360.26 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:360.26,362.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:363.2,363.12 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:367.47,369.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os.go:372.55,374.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/os_default_platform.go:10.30,18.2 1 19
-github.com/jesseduffield/lazydocker/pkg/commands/service.go:23.76,25.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/service.go:28.32,30.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/service.go:33.30,35.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/service.go:38.35,40.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/service.go:43.33,45.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/service.go:47.59,53.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/service.go:56.47,58.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/service.go:61.49,72.2 5 0
-github.com/jesseduffield/lazydocker/pkg/commands/service.go:75.66,83.2 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/volume.go:23.61,25.16 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/volume.go:25.16,27.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/volume.go:29.2,33.33 3 0
-github.com/jesseduffield/lazydocker/pkg/commands/volume.go:33.33,42.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/volume.go:44.2,44.24 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/volume.go:48.46,51.2 2 0
-github.com/jesseduffield/lazydocker/pkg/commands/volume.go:54.43,56.2 1 0
-mode: atomic
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:32.53,36.83 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:36.83,38.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:39.39,39.61 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:48.66,52.16 4 3
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:52.16,55.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:58.2,58.23 1 3
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:58.23,60.17 2 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:60.17,62.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:63.3,64.17 2 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:64.17,66.4 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:68.3,68.21 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:70.2,70.26 1 2
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:75.33,75.47 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:85.44,87.2 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:89.117,91.16 2 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:91.16,93.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:94.2,97.16 3 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:97.16,99.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:103.2,108.16 5 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:108.16,110.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:113.2,118.8 2 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:123.87,127.6 3 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:127.6,128.10 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:129.21,130.20 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:131.14,131.14 0 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:134.3,135.17 2 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:135.17,136.12 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:138.3,138.13 1 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:143.79,145.16 2 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:145.16,147.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:148.2,149.12 2 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:152.101,156.16 4 1
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:156.16,158.3 1 0
-github.com/jesseduffield/lazydocker/pkg/commands/ssh/ssh.go:159.2,159.17 1 1
-mode: atomic
-mode: atomic
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:34.90,36.2 1 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:39.31,40.26 1 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:40.26,42.3 1 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:44.2,46.12 2 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:46.12,49.3 2 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:51.2,51.9 1 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:52.11,53.9 1 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:54.37,55.41 1 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:59.66,60.12 1 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:60.12,69.27 8 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:69.27,72.4 2 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:73.3,78.27 4 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:78.27,82.4 3 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:84.3,92.13 2 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:92.13,96.4 3 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:99.2,99.12 1 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:102.23,105.15 3 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:105.15,107.3 1 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:109.2,113.18 5 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:119.159,122.45 2 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:122.45,123.20 1 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:123.20,125.4 1 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:126.3,130.7 4 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:130.7,131.11 1 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:132.25,134.11 2 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:135.22,137.11 2 0
+github.com/christophe-duc/lazypodman/pkg/tasks/tasks.go:138.22,140.26 2 0
diff --git a/lazypodman b/lazypodman
index 0fcce3f9..6962c7c3 100755
Binary files a/lazypodman and b/lazypodman differ
diff --git a/pkg/commands/runtime_libpod.go b/pkg/commands/runtime_libpod.go
index 477e1941..b771ff82 100644
--- a/pkg/commands/runtime_libpod.go
+++ b/pkg/commands/runtime_libpod.go
@@ -8,6 +8,8 @@ import (
"github.com/containers/podman/v5/libpod"
"github.com/containers/podman/v5/libpod/define"
+ "go.podman.io/common/libimage"
+ nettypes "go.podman.io/common/libnetwork/types"
)
// LibpodRuntime implements ContainerRuntime using libpod directly.
@@ -193,25 +195,25 @@ func (r *LibpodRuntime) ContainerStats(ctx context.Context, id string, stream bo
// ListImages returns all images.
func (r *LibpodRuntime) ListImages(ctx context.Context) ([]ImageSummary, error) {
- imgs, err := r.runtime.ImageRuntime().GetImages()
+ imgs, err := r.runtime.LibimageRuntime().ListImages(ctx, nil)
if err != nil {
return nil, err
}
- return convertLibpodImageList(imgs), nil
+ return convertLibpodImageList(ctx, imgs), nil
}
// InspectImage returns detailed information about an image.
func (r *LibpodRuntime) InspectImage(ctx context.Context, id string) (*ImageDetails, error) {
- img, _, err := r.runtime.ImageRuntime().LookupImage(id, nil)
+ img, _, err := r.runtime.LibimageRuntime().LookupImage(id, nil)
if err != nil {
return nil, err
}
- return convertLibpodImageInspect(img), nil
+ return convertLibpodImageInspect(ctx, img), nil
}
// ImageHistory returns the history of an image.
func (r *LibpodRuntime) ImageHistory(ctx context.Context, id string) ([]ImageHistoryEntry, error) {
- img, _, err := r.runtime.ImageRuntime().LookupImage(id, nil)
+ img, _, err := r.runtime.LibimageRuntime().LookupImage(id, nil)
if err != nil {
return nil, err
}
@@ -224,17 +226,20 @@ func (r *LibpodRuntime) ImageHistory(ctx context.Context, id string) ([]ImageHis
// RemoveImage removes an image.
func (r *LibpodRuntime) RemoveImage(ctx context.Context, id string, force bool) error {
- img, _, err := r.runtime.ImageRuntime().LookupImage(id, nil)
+ img, _, err := r.runtime.LibimageRuntime().LookupImage(id, nil)
if err != nil {
return err
}
- _, err = r.runtime.ImageRuntime().RemoveImages(ctx, []string{img.ID()}, &libimage.RemoveImagesOptions{Force: force})
- return err
+ _, errs := r.runtime.LibimageRuntime().RemoveImages(ctx, []string{img.ID()}, &libimage.RemoveImagesOptions{Force: force})
+ if len(errs) > 0 {
+ return errs[0]
+ }
+ return nil
}
// PruneImages removes unused images.
func (r *LibpodRuntime) PruneImages(ctx context.Context) error {
- _, err := r.runtime.ImageRuntime().PruneImages(ctx, nil)
+ _, err := r.runtime.LibimageRuntime().PruneImages(ctx, nil)
return err
}
@@ -443,40 +448,59 @@ func convertLibpodContainerStats(stats *define.ContainerStats) ContainerStatsEnt
}
}
-func convertLibpodImageList(imgs []*libimage.Image) []ImageSummary {
+func convertLibpodImageList(ctx context.Context, imgs []*libimage.Image) []ImageSummary {
result := make([]ImageSummary, len(imgs))
for i, img := range imgs {
+ size, _ := img.Size() // Ignore error for list view
+ labels, _ := img.Labels(ctx) // Ignore error for list view
result[i] = ImageSummary{
ID: img.ID(),
RepoTags: img.Names(),
Created: img.Created().Unix(),
- Size: img.Size(),
- Labels: img.Labels(),
+ Size: size,
+ Labels: labels,
}
}
return result
}
-func convertLibpodImageInspect(img *libimage.Image) *ImageDetails {
+func convertLibpodImageInspect(ctx context.Context, img *libimage.Image) *ImageDetails {
if img == nil {
return nil
}
+ data, err := img.Inspect(ctx, nil)
+ if err != nil {
+ return nil
+ }
+ var created time.Time
+ if data.Created != nil {
+ created = *data.Created
+ }
return &ImageDetails{
- ID: img.ID(),
- RepoTags: img.Names(),
- Created: img.Created(),
- Size: img.Size(),
- Architecture: img.Architecture(),
- Os: img.OS(),
+ ID: data.ID,
+ RepoTags: data.RepoTags,
+ RepoDigests: data.RepoDigests,
+ Parent: data.Parent,
+ Comment: data.Comment,
+ Created: created,
+ Author: data.Author,
+ Architecture: data.Architecture,
+ Os: data.Os,
+ Size: data.Size,
+ VirtualSize: data.VirtualSize,
}
}
-func convertLibpodImageHistory(history []*libimage.ImageHistoryLayer) []ImageHistoryEntry {
+func convertLibpodImageHistory(history []libimage.ImageHistory) []ImageHistoryEntry {
result := make([]ImageHistoryEntry, len(history))
for i, layer := range history {
+ created := int64(0)
+ if layer.Created != nil {
+ created = layer.Created.Unix()
+ }
result[i] = ImageHistoryEntry{
ID: layer.ID,
- Created: layer.Created.Unix(),
+ Created: created,
CreatedBy: layer.CreatedBy,
Tags: layer.Tags,
Size: layer.Size,
@@ -502,7 +526,7 @@ func convertLibpodVolumeList(vols []*libpod.Volume) []VolumeSummary {
return result
}
-func convertLibpodNetworkList(nets []types.Network) []NetworkSummary {
+func convertLibpodNetworkList(nets []nettypes.Network) []NetworkSummary {
result := make([]NetworkSummary, len(nets))
for i, nw := range nets {
result[i] = NetworkSummary{
diff --git a/pkg/commands/runtime_socket.go b/pkg/commands/runtime_socket.go
index d9e38f7e..ee6eee24 100644
--- a/pkg/commands/runtime_socket.go
+++ b/pkg/commands/runtime_socket.go
@@ -3,8 +3,10 @@ package commands
import (
"context"
"encoding/json"
+ "strings"
"time"
+ "github.com/containers/podman/v5/libpod/define"
"github.com/containers/podman/v5/pkg/bindings"
"github.com/containers/podman/v5/pkg/bindings/containers"
"github.com/containers/podman/v5/pkg/bindings/images"
@@ -12,6 +14,9 @@ import (
"github.com/containers/podman/v5/pkg/bindings/system"
"github.com/containers/podman/v5/pkg/bindings/volumes"
"github.com/containers/podman/v5/pkg/domain/entities"
+ "github.com/containers/podman/v5/pkg/domain/entities/types"
+ handlertypes "github.com/containers/podman/v5/pkg/api/handlers/types"
+ nettypes "go.podman.io/common/libnetwork/types"
)
// SocketRuntime implements ContainerRuntime using Podman's REST API bindings.
@@ -115,7 +120,16 @@ func (r *SocketRuntime) ContainerTop(ctx context.Context, id string) ([]string,
if err != nil {
return nil, nil, err
}
- return result.Titles, result.Processes, nil
+ // Parse the result - first line is headers, rest are processes
+ if len(result) == 0 {
+ return nil, nil, nil
+ }
+ headers := strings.Fields(result[0])
+ processes := make([][]string, 0, len(result)-1)
+ for _, line := range result[1:] {
+ processes = append(processes, strings.Fields(line))
+ }
+ return headers, processes, nil
}
// PruneContainers removes all stopped containers.
@@ -271,11 +285,20 @@ func (r *SocketRuntime) Events(ctx context.Context) (<-chan Event, <-chan error)
defer close(errChan)
opts := &system.EventsOptions{}
- podmanEventChan, err := system.Events(r.conn, nil, opts)
- if err != nil {
- errChan <- err
- return
- }
+ podmanEventChan := make(chan types.Event)
+ cancelChan := make(chan bool)
+
+ go func() {
+ <-ctx.Done()
+ close(cancelChan)
+ }()
+
+ go func() {
+ err := system.Events(r.conn, podmanEventChan, cancelChan, opts)
+ if err != nil {
+ errChan <- err
+ }
+ }()
for podmanEvent := range podmanEventChan {
event := Event{
@@ -303,6 +326,11 @@ func (r *SocketRuntime) Events(ctx context.Context) (<-chan Event, <-chan error)
func convertPodmanContainerList(podmanContainers []entities.ListContainer) []ContainerSummary {
result := make([]ContainerSummary, len(podmanContainers))
for i, c := range podmanContainers {
+ var sizeRw, sizeRootFs int64
+ if c.Size != nil {
+ sizeRw = c.Size.RwSize
+ sizeRootFs = c.Size.RootFsSize
+ }
result[i] = ContainerSummary{
ID: c.ID,
Names: c.Names,
@@ -314,8 +342,8 @@ func convertPodmanContainerList(podmanContainers []entities.ListContainer) []Con
Status: c.Status,
Ports: convertPodmanPorts(c.Ports),
Labels: c.Labels,
- SizeRw: c.Size.RwSize,
- SizeRootFs: c.Size.RootFsSize,
+ SizeRw: sizeRw,
+ SizeRootFs: sizeRootFs,
Pod: c.Pod,
PodName: c.PodName,
}
@@ -332,7 +360,7 @@ func commandToString(cmd []string) string {
return string(data)
}
-func convertPodmanPorts(ports []entities.PortMapping) []PortMapping {
+func convertPodmanPorts(ports []nettypes.PortMapping) []PortMapping {
result := make([]PortMapping, len(ports))
for i, p := range ports {
result[i] = PortMapping{
@@ -345,7 +373,7 @@ func convertPodmanPorts(ports []entities.PortMapping) []PortMapping {
return result
}
-func convertPodmanContainerInspect(data *entities.InspectContainerData) *ContainerDetails {
+func convertPodmanContainerInspect(data *define.InspectContainerData) *ContainerDetails {
if data == nil {
return nil
}
@@ -361,7 +389,6 @@ func convertPodmanContainerInspect(data *entities.InspectContainerData) *Contain
ResolvConfPath: data.ResolvConfPath,
HostnamePath: data.HostnamePath,
HostsPath: data.HostsPath,
- LogPath: data.LogPath,
RestartCount: int(data.RestartCount),
Driver: data.Driver,
MountLabel: data.MountLabel,
@@ -393,9 +420,13 @@ func convertPodmanContainerInspect(data *entities.InspectContainerData) *Contain
}
if data.Config != nil {
+ var onBuild []string
+ if data.Config.OnBuild != nil {
+ onBuild = []string{*data.Config.OnBuild}
+ }
details.Config = &ContainerConfig{
Hostname: data.Config.Hostname,
- Domainname: data.Config.Domainname,
+ Domainname: data.Config.DomainName,
User: data.Config.User,
AttachStdin: data.Config.AttachStdin,
AttachStdout: data.Config.AttachStdout,
@@ -408,7 +439,7 @@ func convertPodmanContainerInspect(data *entities.InspectContainerData) *Contain
Image: data.Config.Image,
WorkingDir: data.Config.WorkingDir,
Entrypoint: data.Config.Entrypoint,
- OnBuild: data.Config.OnBuild,
+ OnBuild: onBuild,
Labels: data.Config.Labels,
StopSignal: data.Config.StopSignal,
}
@@ -417,7 +448,7 @@ func convertPodmanContainerInspect(data *entities.InspectContainerData) *Contain
return details
}
-func convertPodmanContainerStats(stat entities.ContainerStats) ContainerStatsEntry {
+func convertPodmanContainerStats(stat define.ContainerStats) ContainerStatsEntry {
return ContainerStatsEntry{
Read: time.Now(),
PreRead: time.Now().Add(-time.Second),
@@ -451,10 +482,10 @@ func convertPodmanImageList(podmanImages []*entities.ImageSummary) []ImageSummar
RepoDigests: img.RepoDigests,
Created: img.Created,
Size: img.Size,
- SharedSize: img.SharedSize,
+ SharedSize: int64(img.SharedSize),
VirtualSize: img.VirtualSize,
Labels: img.Labels,
- Containers: img.Containers,
+ Containers: int64(img.Containers),
}
}
return result
@@ -480,21 +511,25 @@ func convertPodmanImageInspect(data *entities.ImageInspectReport) *ImageDetails
}
if data.RootFS != nil {
+ layers := make([]string, len(data.RootFS.Layers))
+ for i, layer := range data.RootFS.Layers {
+ layers[i] = layer.String()
+ }
details.RootFS = RootFS{
Type: data.RootFS.Type,
- Layers: data.RootFS.Layers,
+ Layers: layers,
}
}
return details
}
-func convertPodmanImageHistory(history []*entities.ImageHistoryLayer) []ImageHistoryEntry {
+func convertPodmanImageHistory(history []*handlertypes.HistoryResponse) []ImageHistoryEntry {
result := make([]ImageHistoryEntry, len(history))
for i, layer := range history {
result[i] = ImageHistoryEntry{
ID: layer.ID,
- Created: layer.Created.Unix(),
+ Created: layer.Created,
CreatedBy: layer.CreatedBy,
Tags: layer.Tags,
Size: layer.Size,
@@ -520,7 +555,7 @@ func convertPodmanVolumeList(podmanVolumes []*entities.VolumeListReport) []Volum
return result
}
-func convertPodmanNetworkList(podmanNetworks []entities.NetworkListReport) []NetworkSummary {
+func convertPodmanNetworkList(podmanNetworks []nettypes.Network) []NetworkSummary {
result := make([]NetworkSummary, len(podmanNetworks))
for i, nw := range podmanNetworks {
result[i] = NetworkSummary{
diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go
index 494a9c7e..c2f1a904 100644
--- a/pkg/gui/containers_panel.go
+++ b/pkg/gui/containers_panel.go
@@ -67,7 +67,7 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
// where a container restarts but the new logs don't get read.
// Note that this might be jarring if we have a lot of logs and the container
// restarts a lot, so let's keep an eye on it.
- return "containers-" + container.ID + "-" + container.Container.State
+ return "containers-" + container.ID + "-" + container.Summary.State
},
},
ListPanel: panels.ListPanel[*commands.Container]{
@@ -89,7 +89,7 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
return false
}
- if !gui.State.ShowExitedContainers && container.Container.State == "exited" {
+ if !gui.State.ShowExitedContainers && container.Summary.State == "exited" {
return false
}
@@ -112,13 +112,13 @@ func sortContainers(a *commands.Container, b *commands.Container, legacySort boo
return a.Name < b.Name
}
- stateLeft := containerStates[a.Container.State]
- stateRight := containerStates[b.Container.State]
+ stateLeft := containerStates[a.Summary.State]
+ stateRight := containerStates[b.Summary.State]
if stateLeft == stateRight {
return a.Name < b.Name
}
- return containerStates[a.Container.State] < containerStates[b.Container.State]
+ return containerStates[a.Summary.State] < containerStates[b.Summary.State]
}
func (gui *Gui) renderContainerEnv(container *commands.Container) tasks.TaskFunc {
@@ -532,11 +532,11 @@ func (gui *Gui) handleContainersOpenInBrowserCommand(g *gocui.Gui, v *gocui.View
func (gui *Gui) openContainerInBrowser(ctr *commands.Container) error {
// skip if no any ports
- if len(ctr.Container.Ports) == 0 {
+ if len(ctr.Summary.Ports) == 0 {
return nil
}
// skip if the first port is not published
- port := ctr.Container.Ports[0]
+ port := ctr.Summary.Ports[0]
if port.IP == "" {
return nil
}
diff --git a/pkg/gui/images_panel.go b/pkg/gui/images_panel.go
index 22323efb..e475eade 100644
--- a/pkg/gui/images_panel.go
+++ b/pkg/gui/images_panel.go
@@ -76,10 +76,10 @@ func (gui *Gui) imageConfigStr(image *commands.Image) string {
padding := 10
output := ""
output += utils.WithPadding("Name: ", padding) + image.Name + "\n"
- output += utils.WithPadding("ID: ", padding) + image.Image.ID + "\n"
- output += utils.WithPadding("Tags: ", padding) + utils.ColoredString(strings.Join(image.Image.RepoTags, ", "), color.FgGreen) + "\n"
- output += utils.WithPadding("Size: ", padding) + utils.FormatDecimalBytes(int(image.Image.Size)) + "\n"
- output += utils.WithPadding("Created: ", padding) + fmt.Sprintf("%v", time.Unix(image.Image.Created, 0).Format(time.RFC1123)) + "\n"
+ output += utils.WithPadding("ID: ", padding) + image.Summary.ID + "\n"
+ output += utils.WithPadding("Tags: ", padding) + utils.ColoredString(strings.Join(image.Summary.RepoTags, ", "), color.FgGreen) + "\n"
+ output += utils.WithPadding("Size: ", padding) + utils.FormatDecimalBytes(int(image.Summary.Size)) + "\n"
+ output += utils.WithPadding("Created: ", padding) + fmt.Sprintf("%v", time.Unix(image.Summary.Created, 0).Format(time.RFC1123)) + "\n"
history, err := image.RenderHistory()
if err != nil {
diff --git a/pkg/gui/networks_panel.go b/pkg/gui/networks_panel.go
index 750b15cf..a2f58ed0 100644
--- a/pkg/gui/networks_panel.go
+++ b/pkg/gui/networks_panel.go
@@ -54,19 +54,19 @@ func (gui *Gui) renderNetworkConfig(network *commands.Network) tasks.TaskFunc {
func (gui *Gui) networkConfigStr(network *commands.Network) string {
padding := 15
output := ""
- output += utils.WithPadding("ID: ", padding) + network.Network.ID + "\n"
+ output += utils.WithPadding("ID: ", padding) + network.Summary.ID + "\n"
output += utils.WithPadding("Name: ", padding) + network.Name + "\n"
- output += utils.WithPadding("Driver: ", padding) + network.Network.Driver + "\n"
- output += utils.WithPadding("Scope: ", padding) + network.Network.Scope + "\n"
- output += utils.WithPadding("EnabledIPV6: ", padding) + strconv.FormatBool(network.Network.EnableIPv6) + "\n"
- output += utils.WithPadding("Internal: ", padding) + strconv.FormatBool(network.Network.Internal) + "\n"
- output += utils.WithPadding("Attachable: ", padding) + strconv.FormatBool(network.Network.Attachable) + "\n"
- output += utils.WithPadding("Ingress: ", padding) + strconv.FormatBool(network.Network.Ingress) + "\n"
+ output += utils.WithPadding("Driver: ", padding) + network.Summary.Driver + "\n"
+ output += utils.WithPadding("Scope: ", padding) + network.Summary.Scope + "\n"
+ output += utils.WithPadding("EnabledIPV6: ", padding) + strconv.FormatBool(network.Summary.EnableIPv6) + "\n"
+ output += utils.WithPadding("Internal: ", padding) + strconv.FormatBool(network.Summary.Internal) + "\n"
+ output += utils.WithPadding("Attachable: ", padding) + strconv.FormatBool(network.Summary.Attachable) + "\n"
+ output += utils.WithPadding("Ingress: ", padding) + strconv.FormatBool(network.Summary.Ingress) + "\n"
output += utils.WithPadding("Containers: ", padding)
- if len(network.Network.Containers) > 0 {
+ if len(network.Summary.Containers) > 0 {
output += "\n"
- for _, v := range network.Network.Containers {
+ for _, v := range network.Summary.Containers {
output += utils.FormatMapItem(padding, v.Name, v.EndpointID)
}
} else {
@@ -74,8 +74,8 @@ func (gui *Gui) networkConfigStr(network *commands.Network) string {
}
output += "\n"
- output += utils.WithPadding("Labels: ", padding) + utils.FormatMap(padding, network.Network.Labels) + "\n"
- output += utils.WithPadding("Options: ", padding) + utils.FormatMap(padding, network.Network.Options)
+ output += utils.WithPadding("Labels: ", padding) + utils.FormatMap(padding, network.Summary.Labels) + "\n"
+ output += utils.WithPadding("Options: ", padding) + utils.FormatMap(padding, network.Summary.Options)
return output
}
diff --git a/pkg/gui/presentation/containers.go b/pkg/gui/presentation/containers.go
index 22b38508..baf42565 100644
--- a/pkg/gui/presentation/containers.go
+++ b/pkg/gui/presentation/containers.go
@@ -6,7 +6,6 @@ import (
"strconv"
"strings"
- "github.com/docker/docker/api/types/container"
"github.com/fatih/color"
"github.com/christophe-duc/lazypodman/pkg/commands"
"github.com/christophe-duc/lazypodman/pkg/config"
@@ -26,11 +25,11 @@ func GetContainerDisplayStrings(guiConfig *config.GuiConfig, container *commands
}
func displayContainerImage(container *commands.Container) string {
- return strings.TrimPrefix(container.Container.Image, "sha256:")
+ return strings.TrimPrefix(container.Summary.Image, "sha256:")
}
func displayPorts(c *commands.Container) string {
- portStrings := lo.Map(c.Container.Ports, func(port container.Port, _ int) string {
+ portStrings := lo.Map(c.Summary.Ports, func(port commands.PortMapping, _ int) string {
if port.PublicPort == 0 {
return fmt.Sprintf("%d/%s", port.PrivatePort, port.Type)
}
@@ -77,13 +76,13 @@ func getContainerDisplayStatus(guiConfig *config.GuiConfig, c *commands.Containe
var containerState string
switch guiConfig.ContainerStatusHealthStyle {
case "short":
- containerState = shortStatusMap[c.Container.State]
+ containerState = shortStatusMap[c.Summary.State]
case "icon":
- containerState = string(iconStatusMap[c.Container.State])
+ containerState = string(iconStatusMap[c.Summary.State])
case "long":
fallthrough
default:
- containerState = c.Container.State
+ containerState = c.Summary.State
}
return utils.ColoredString(containerState, getContainerColor(c))
@@ -95,7 +94,7 @@ func getContainerDisplaySubstatus(guiConfig *config.GuiConfig, c *commands.Conta
return ""
}
- switch c.Container.State {
+ switch c.Summary.State {
case "exited":
return utils.ColoredString(
fmt.Sprintf("(%s)", strconv.Itoa(c.Details.State.ExitCode)), getContainerColor(c),
@@ -176,7 +175,7 @@ func getDisplayCPUPerc(c *commands.Container) string {
// getContainerColor Container color
func getContainerColor(c *commands.Container) color.Attribute {
- switch c.Container.State {
+ switch c.Summary.State {
case "exited":
// This means the colour may be briefly yellow and then switch to red upon starting
// Not sure what a better alternative is.
diff --git a/pkg/gui/presentation/images.go b/pkg/gui/presentation/images.go
index 75363538..f4988761 100644
--- a/pkg/gui/presentation/images.go
+++ b/pkg/gui/presentation/images.go
@@ -9,6 +9,6 @@ func GetImageDisplayStrings(image *commands.Image) []string {
return []string{
image.Name,
image.Tag,
- utils.FormatDecimalBytes(int(image.Image.Size)),
+ utils.FormatDecimalBytes(int(image.Summary.Size)),
}
}
diff --git a/pkg/gui/presentation/networks.go b/pkg/gui/presentation/networks.go
index 1472cc60..eb349ac5 100644
--- a/pkg/gui/presentation/networks.go
+++ b/pkg/gui/presentation/networks.go
@@ -3,5 +3,5 @@ package presentation
import "github.com/christophe-duc/lazypodman/pkg/commands"
func GetNetworkDisplayStrings(network *commands.Network) []string {
- return []string{network.Network.Driver, network.Name}
+ return []string{network.Summary.Driver, network.Name}
}
diff --git a/pkg/gui/presentation/volumes.go b/pkg/gui/presentation/volumes.go
index bcf5c3be..c053f6ba 100644
--- a/pkg/gui/presentation/volumes.go
+++ b/pkg/gui/presentation/volumes.go
@@ -3,5 +3,5 @@ package presentation
import "github.com/christophe-duc/lazypodman/pkg/commands"
func GetVolumeDisplayStrings(volume *commands.Volume) []string {
- return []string{volume.Volume.Driver, volume.Name}
+ return []string{volume.Summary.Driver, volume.Name}
}
diff --git a/pkg/gui/services_panel.go b/pkg/gui/services_panel.go
index 5613b69f..e1ce43e1 100644
--- a/pkg/gui/services_panel.go
+++ b/pkg/gui/services_panel.go
@@ -53,7 +53,7 @@ func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] {
if service.Container == nil {
return "services-" + service.ID
}
- return "services-" + service.ID + "-" + service.Container.ID + "-" + service.Container.Container.State
+ return "services-" + service.ID + "-" + service.Container.ID + "-" + service.Container.Summary.State
},
},
ListPanel: panels.ListPanel[*commands.Service]{
diff --git a/pkg/gui/volumes_panel.go b/pkg/gui/volumes_panel.go
index 46929602..81ceb489 100644
--- a/pkg/gui/volumes_panel.go
+++ b/pkg/gui/volumes_panel.go
@@ -41,10 +41,10 @@ func (gui *Gui) getVolumesPanel() *panels.SideListPanel[*commands.Volume] {
// because those are the ones you typically care about.
// Within that, we also sort them alphabetically
Sort: func(a *commands.Volume, b *commands.Volume) bool {
- if len(a.Volume.Labels) == 0 && len(b.Volume.Labels) > 0 {
+ if len(a.Summary.Labels) == 0 && len(b.Summary.Labels) > 0 {
return false
}
- if len(a.Volume.Labels) > 0 && len(b.Volume.Labels) == 0 {
+ if len(a.Summary.Labels) > 0 && len(b.Summary.Labels) == 0 {
return true
}
return a.Name < b.Name
@@ -61,25 +61,25 @@ func (gui *Gui) volumeConfigStr(volume *commands.Volume) string {
padding := 15
output := ""
output += utils.WithPadding("Name: ", padding) + volume.Name + "\n"
- output += utils.WithPadding("Driver: ", padding) + volume.Volume.Driver + "\n"
- output += utils.WithPadding("Scope: ", padding) + volume.Volume.Scope + "\n"
- output += utils.WithPadding("Mountpoint: ", padding) + volume.Volume.Mountpoint + "\n"
- output += utils.WithPadding("Labels: ", padding) + utils.FormatMap(padding, volume.Volume.Labels) + "\n"
- output += utils.WithPadding("Options: ", padding) + utils.FormatMap(padding, volume.Volume.Options) + "\n"
+ output += utils.WithPadding("Driver: ", padding) + volume.Summary.Driver + "\n"
+ output += utils.WithPadding("Scope: ", padding) + volume.Summary.Scope + "\n"
+ output += utils.WithPadding("Mountpoint: ", padding) + volume.Summary.Mountpoint + "\n"
+ output += utils.WithPadding("Labels: ", padding) + utils.FormatMap(padding, volume.Summary.Labels) + "\n"
+ output += utils.WithPadding("Options: ", padding) + utils.FormatMap(padding, volume.Summary.Options) + "\n"
output += utils.WithPadding("Status: ", padding)
- if volume.Volume.Status != nil {
+ if volume.Summary.Status != nil {
output += "\n"
- for k, v := range volume.Volume.Status {
+ for k, v := range volume.Summary.Status {
output += utils.FormatMapItem(padding, k, v)
}
} else {
output += "n/a"
}
- if volume.Volume.UsageData != nil {
- output += utils.WithPadding("RefCount: ", padding) + fmt.Sprintf("%d", volume.Volume.UsageData.RefCount) + "\n"
- output += utils.WithPadding("Size: ", padding) + utils.FormatBinaryBytes(int(volume.Volume.UsageData.Size)) + "\n"
+ if volume.Summary.UsageData != nil {
+ output += utils.WithPadding("RefCount: ", padding) + fmt.Sprintf("%d", volume.Summary.UsageData.RefCount) + "\n"
+ output += utils.WithPadding("Size: ", padding) + utils.FormatBinaryBytes(int(volume.Summary.UsageData.Size)) + "\n"
}
return output
diff --git a/vendor/dario.cat/mergo/.deepsource.toml b/vendor/dario.cat/mergo/.deepsource.toml
new file mode 100644
index 00000000..a8bc979e
--- /dev/null
+++ b/vendor/dario.cat/mergo/.deepsource.toml
@@ -0,0 +1,12 @@
+version = 1
+
+test_patterns = [
+ "*_test.go"
+]
+
+[[analyzers]]
+name = "go"
+enabled = true
+
+ [analyzers.meta]
+ import_path = "dario.cat/mergo"
\ No newline at end of file
diff --git a/vendor/dario.cat/mergo/.gitignore b/vendor/dario.cat/mergo/.gitignore
new file mode 100644
index 00000000..45ad0f1a
--- /dev/null
+++ b/vendor/dario.cat/mergo/.gitignore
@@ -0,0 +1,36 @@
+#### joe made this: http://goel.io/joe
+
+#### go ####
+# Binaries for programs and plugins
+*.exe
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Golang/Intellij
+.idea
+
+# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
+.glide/
+
+#### vim ####
+# Swap
+[._]*.s[a-v][a-z]
+[._]*.sw[a-p]
+[._]s[a-v][a-z]
+[._]sw[a-p]
+
+# Session
+Session.vim
+
+# Temporary
+.netrwhist
+*~
+# Auto-generated tag files
+tags
diff --git a/vendor/dario.cat/mergo/.travis.yml b/vendor/dario.cat/mergo/.travis.yml
new file mode 100644
index 00000000..d324c43b
--- /dev/null
+++ b/vendor/dario.cat/mergo/.travis.yml
@@ -0,0 +1,12 @@
+language: go
+arch:
+ - amd64
+ - ppc64le
+install:
+ - go get -t
+ - go get golang.org/x/tools/cmd/cover
+ - go get github.com/mattn/goveralls
+script:
+ - go test -race -v ./...
+after_script:
+ - $HOME/gopath/bin/goveralls -service=travis-ci -repotoken $COVERALLS_TOKEN
diff --git a/vendor/dario.cat/mergo/CODE_OF_CONDUCT.md b/vendor/dario.cat/mergo/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..469b4490
--- /dev/null
+++ b/vendor/dario.cat/mergo/CODE_OF_CONDUCT.md
@@ -0,0 +1,46 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at i@dario.im. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/vendor/dario.cat/mergo/CONTRIBUTING.md b/vendor/dario.cat/mergo/CONTRIBUTING.md
new file mode 100644
index 00000000..0a1ff9f9
--- /dev/null
+++ b/vendor/dario.cat/mergo/CONTRIBUTING.md
@@ -0,0 +1,112 @@
+
+# Contributing to mergo
+
+First off, thanks for taking the time to contribute! ❤️
+
+All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉
+
+> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about:
+> - Star the project
+> - Tweet about it
+> - Refer this project in your project's readme
+> - Mention the project at local meetups and tell your friends/colleagues
+
+
+## Table of Contents
+
+- [Code of Conduct](#code-of-conduct)
+- [I Have a Question](#i-have-a-question)
+- [I Want To Contribute](#i-want-to-contribute)
+- [Reporting Bugs](#reporting-bugs)
+- [Suggesting Enhancements](#suggesting-enhancements)
+
+## Code of Conduct
+
+This project and everyone participating in it is governed by the
+[mergo Code of Conduct](https://github.com/imdario/mergoblob/master/CODE_OF_CONDUCT.md).
+By participating, you are expected to uphold this code. Please report unacceptable behavior
+to <>.
+
+
+## I Have a Question
+
+> If you want to ask a question, we assume that you have read the available [Documentation](https://pkg.go.dev/github.com/imdario/mergo).
+
+Before you ask a question, it is best to search for existing [Issues](https://github.com/imdario/mergo/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first.
+
+If you then still feel the need to ask a question and need clarification, we recommend the following:
+
+- Open an [Issue](https://github.com/imdario/mergo/issues/new).
+- Provide as much context as you can about what you're running into.
+- Provide project and platform versions (nodejs, npm, etc), depending on what seems relevant.
+
+We will then take care of the issue as soon as possible.
+
+## I Want To Contribute
+
+> ### Legal Notice
+> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license.
+
+### Reporting Bugs
+
+
+#### Before Submitting a Bug Report
+
+A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible.
+
+- Make sure that you are using the latest version.
+- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](). If you are looking for support, you might want to check [this section](#i-have-a-question)).
+- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/imdario/mergoissues?q=label%3Abug).
+- Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue.
+- Collect information about the bug:
+- Stack trace (Traceback)
+- OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
+- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
+- Possibly your input and the output
+- Can you reliably reproduce the issue? And can you also reproduce it with older versions?
+
+
+#### How Do I Submit a Good Bug Report?
+
+> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to .
+
+
+We use GitHub issues to track bugs and errors. If you run into an issue with the project:
+
+- Open an [Issue](https://github.com/imdario/mergo/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.)
+- Explain the behavior you would expect and the actual behavior.
+- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case.
+- Provide the information you collected in the previous section.
+
+Once it's filed:
+
+- The project team will label the issue accordingly.
+- A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced.
+- If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be implemented by someone.
+
+### Suggesting Enhancements
+
+This section guides you through submitting an enhancement suggestion for mergo, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions.
+
+
+#### Before Submitting an Enhancement
+
+- Make sure that you are using the latest version.
+- Read the [documentation]() carefully and find out if the functionality is already covered, maybe by an individual configuration.
+- Perform a [search](https://github.com/imdario/mergo/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
+- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library.
+
+
+#### How Do I Submit a Good Enhancement Suggestion?
+
+Enhancement suggestions are tracked as [GitHub issues](https://github.com/imdario/mergo/issues).
+
+- Use a **clear and descriptive title** for the issue to identify the suggestion.
+- Provide a **step-by-step description of the suggested enhancement** in as many details as possible.
+- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you.
+- You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux.
+- **Explain why this enhancement would be useful** to most mergo users. You may also want to point out the other projects that solved it better and which could serve as inspiration.
+
+
+## Attribution
+This guide is based on the **contributing-gen**. [Make your own](https://github.com/bttger/contributing-gen)!
diff --git a/vendor/dario.cat/mergo/FUNDING.json b/vendor/dario.cat/mergo/FUNDING.json
new file mode 100644
index 00000000..0585e1fe
--- /dev/null
+++ b/vendor/dario.cat/mergo/FUNDING.json
@@ -0,0 +1,7 @@
+{
+ "drips": {
+ "ethereum": {
+ "ownedBy": "0x6160020e7102237aC41bdb156e94401692D76930"
+ }
+ }
+}
diff --git a/vendor/dario.cat/mergo/LICENSE b/vendor/dario.cat/mergo/LICENSE
new file mode 100644
index 00000000..68668029
--- /dev/null
+++ b/vendor/dario.cat/mergo/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2013 Dario Castañé. All rights reserved.
+Copyright (c) 2012 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/dario.cat/mergo/README.md b/vendor/dario.cat/mergo/README.md
new file mode 100644
index 00000000..0e4a59af
--- /dev/null
+++ b/vendor/dario.cat/mergo/README.md
@@ -0,0 +1,253 @@
+# Mergo
+
+[![GitHub release][5]][6]
+[![GoCard][7]][8]
+[![Test status][1]][2]
+[![OpenSSF Scorecard][21]][22]
+[![OpenSSF Best Practices][19]][20]
+[![Coverage status][9]][10]
+[![Sourcegraph][11]][12]
+[![FOSSA status][13]][14]
+
+[![GoDoc][3]][4]
+[![Become my sponsor][15]][16]
+[![Tidelift][17]][18]
+
+[1]: https://github.com/imdario/mergo/workflows/tests/badge.svg?branch=master
+[2]: https://github.com/imdario/mergo/actions/workflows/tests.yml
+[3]: https://godoc.org/github.com/imdario/mergo?status.svg
+[4]: https://godoc.org/github.com/imdario/mergo
+[5]: https://img.shields.io/github/release/imdario/mergo.svg
+[6]: https://github.com/imdario/mergo/releases
+[7]: https://goreportcard.com/badge/imdario/mergo
+[8]: https://goreportcard.com/report/github.com/imdario/mergo
+[9]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master
+[10]: https://coveralls.io/github/imdario/mergo?branch=master
+[11]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg
+[12]: https://sourcegraph.com/github.com/imdario/mergo?badge
+[13]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=shield
+[14]: https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_shield
+[15]: https://img.shields.io/github/sponsors/imdario
+[16]: https://github.com/sponsors/imdario
+[17]: https://tidelift.com/badges/package/go/github.com%2Fimdario%2Fmergo
+[18]: https://tidelift.com/subscription/pkg/go-github.com-imdario-mergo
+[19]: https://bestpractices.coreinfrastructure.org/projects/7177/badge
+[20]: https://bestpractices.coreinfrastructure.org/projects/7177
+[21]: https://api.securityscorecards.dev/projects/github.com/imdario/mergo/badge
+[22]: https://api.securityscorecards.dev/projects/github.com/imdario/mergo
+
+A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements.
+
+Mergo merges same-type structs and maps by setting default values in zero-value fields. Mergo won't merge unexported (private) fields. It will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection).
+
+Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region of Marche.
+
+## Status
+
+Mergo is stable and frozen, ready for production. Check a short list of the projects using at large scale it [here](https://github.com/imdario/mergo#mergo-in-the-wild).
+
+No new features are accepted. They will be considered for a future v2 that improves the implementation and fixes bugs for corner cases.
+
+### Important notes
+
+#### 1.0.0
+
+In [1.0.0](//github.com/imdario/mergo/releases/tag/1.0.0) Mergo moves to a vanity URL `dario.cat/mergo`. No more v1 versions will be released.
+
+If the vanity URL is causing issues in your project due to a dependency pulling Mergo - it isn't a direct dependency in your project - it is recommended to use [replace](https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive) to pin the version to the last one with the old import URL:
+
+```
+replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16
+```
+
+#### 0.3.9
+
+Please keep in mind that a problematic PR broke [0.3.9](//github.com/imdario/mergo/releases/tag/0.3.9). I reverted it in [0.3.10](//github.com/imdario/mergo/releases/tag/0.3.10), and I consider it stable but not bug-free. Also, this version adds support for go modules.
+
+Keep in mind that in [0.3.2](//github.com/imdario/mergo/releases/tag/0.3.2), Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). I added an optional/variadic argument so that it won't break the existing code.
+
+If you were using Mergo before April 6th, 2015, please check your project works as intended after updating your local copy with ```go get -u dario.cat/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause in existing projects after the change (release 0.2.0).
+
+### Donations
+
+If Mergo is useful to you, consider buying me a coffee, a beer, or making a monthly donation to allow me to keep building great free software. :heart_eyes:
+
+
+
+
+### Mergo in the wild
+
+Mergo is used by [thousands](https://deps.dev/go/dario.cat%2Fmergo/v1.0.0/dependents) [of](https://deps.dev/go/github.com%2Fimdario%2Fmergo/v0.3.16/dependents) [projects](https://deps.dev/go/github.com%2Fimdario%2Fmergo/v0.3.12), including:
+
+* [containerd/containerd](https://github.com/containerd/containerd)
+* [datadog/datadog-agent](https://github.com/datadog/datadog-agent)
+* [docker/cli/](https://github.com/docker/cli/)
+* [goreleaser/goreleaser](https://github.com/goreleaser/goreleaser)
+* [go-micro/go-micro](https://github.com/go-micro/go-micro)
+* [grafana/loki](https://github.com/grafana/loki)
+* [masterminds/sprig](github.com/Masterminds/sprig)
+* [moby/moby](https://github.com/moby/moby)
+* [slackhq/nebula](https://github.com/slackhq/nebula)
+* [volcano-sh/volcano](https://github.com/volcano-sh/volcano)
+
+## Install
+
+ go get dario.cat/mergo
+
+ // use in your .go code
+ import (
+ "dario.cat/mergo"
+ )
+
+## Usage
+
+You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as [they are zero values](https://golang.org/ref/spec#The_zero_value) too. Also, maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection).
+
+```go
+if err := mergo.Merge(&dst, src); err != nil {
+ // ...
+}
+```
+
+Also, you can merge overwriting values using the transformer `WithOverride`.
+
+```go
+if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
+ // ...
+}
+```
+
+If you need to override pointers, so the source pointer's value is assigned to the destination's pointer, you must use `WithoutDereference`:
+
+```go
+package main
+
+import (
+ "fmt"
+
+ "dario.cat/mergo"
+)
+
+type Foo struct {
+ A *string
+ B int64
+}
+
+func main() {
+ first := "first"
+ second := "second"
+ src := Foo{
+ A: &first,
+ B: 2,
+ }
+
+ dest := Foo{
+ A: &second,
+ B: 1,
+ }
+
+ mergo.Merge(&dest, src, mergo.WithOverride, mergo.WithoutDereference)
+}
+```
+
+Additionally, you can map a `map[string]interface{}` to a struct (and otherwise, from struct to map), following the same restrictions as in `Merge()`. Keys are capitalized to find each corresponding exported field.
+
+```go
+if err := mergo.Map(&dst, srcMap); err != nil {
+ // ...
+}
+```
+
+Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as `map[string]interface{}`. They will be just assigned as values.
+
+Here is a nice example:
+
+```go
+package main
+
+import (
+ "fmt"
+ "dario.cat/mergo"
+)
+
+type Foo struct {
+ A string
+ B int64
+}
+
+func main() {
+ src := Foo{
+ A: "one",
+ B: 2,
+ }
+ dest := Foo{
+ A: "two",
+ }
+ mergo.Merge(&dest, src)
+ fmt.Println(dest)
+ // Will print
+ // {two 2}
+}
+```
+
+### Transformers
+
+Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, `time.Time` is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero `time.Time`?
+
+```go
+package main
+
+import (
+ "fmt"
+ "dario.cat/mergo"
+ "reflect"
+ "time"
+)
+
+type timeTransformer struct {
+}
+
+func (t timeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
+ if typ == reflect.TypeOf(time.Time{}) {
+ return func(dst, src reflect.Value) error {
+ if dst.CanSet() {
+ isZero := dst.MethodByName("IsZero")
+ result := isZero.Call([]reflect.Value{})
+ if result[0].Bool() {
+ dst.Set(src)
+ }
+ }
+ return nil
+ }
+ }
+ return nil
+}
+
+type Snapshot struct {
+ Time time.Time
+ // ...
+}
+
+func main() {
+ src := Snapshot{time.Now()}
+ dest := Snapshot{}
+ mergo.Merge(&dest, src, mergo.WithTransformers(timeTransformer{}))
+ fmt.Println(dest)
+ // Will print
+ // { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 }
+}
+```
+
+## Contact me
+
+If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): [@im_dario](https://twitter.com/im_dario)
+
+## About
+
+Written by [Dario Castañé](http://dario.im).
+
+## License
+
+[BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) license, as [Go language](http://golang.org/LICENSE).
+
+[](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_large)
diff --git a/vendor/dario.cat/mergo/SECURITY.md b/vendor/dario.cat/mergo/SECURITY.md
new file mode 100644
index 00000000..3788fcc1
--- /dev/null
+++ b/vendor/dario.cat/mergo/SECURITY.md
@@ -0,0 +1,14 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported |
+| ------- | ------------------ |
+| 1.x.x | :white_check_mark: |
+| < 1.0 | :x: |
+
+## Security contact information
+
+To report a security vulnerability, please use the
+[Tidelift security contact](https://tidelift.com/security).
+Tidelift will coordinate the fix and disclosure.
diff --git a/vendor/dario.cat/mergo/doc.go b/vendor/dario.cat/mergo/doc.go
new file mode 100644
index 00000000..7d96ec05
--- /dev/null
+++ b/vendor/dario.cat/mergo/doc.go
@@ -0,0 +1,148 @@
+// Copyright 2013 Dario Castañé. All rights reserved.
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements.
+
+Mergo merges same-type structs and maps by setting default values in zero-value fields. Mergo won't merge unexported (private) fields. It will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection).
+
+# Status
+
+It is ready for production use. It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc.
+
+# Important notes
+
+1.0.0
+
+In 1.0.0 Mergo moves to a vanity URL `dario.cat/mergo`.
+
+0.3.9
+
+Please keep in mind that a problematic PR broke 0.3.9. We reverted it in 0.3.10. We consider 0.3.10 as stable but not bug-free. . Also, this version adds suppot for go modules.
+
+Keep in mind that in 0.3.2, Mergo changed Merge() and Map() signatures to support transformers. We added an optional/variadic argument so that it won't break the existing code.
+
+If you were using Mergo before April 6th, 2015, please check your project works as intended after updating your local copy with go get -u dario.cat/mergo. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause in existing projects after the change (release 0.2.0).
+
+# Install
+
+Do your usual installation procedure:
+
+ go get dario.cat/mergo
+
+ // use in your .go code
+ import (
+ "dario.cat/mergo"
+ )
+
+# Usage
+
+You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as they are zero values too. Also, maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection).
+
+ if err := mergo.Merge(&dst, src); err != nil {
+ // ...
+ }
+
+Also, you can merge overwriting values using the transformer WithOverride.
+
+ if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
+ // ...
+ }
+
+Additionally, you can map a map[string]interface{} to a struct (and otherwise, from struct to map), following the same restrictions as in Merge(). Keys are capitalized to find each corresponding exported field.
+
+ if err := mergo.Map(&dst, srcMap); err != nil {
+ // ...
+ }
+
+Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as map[string]interface{}. They will be just assigned as values.
+
+Here is a nice example:
+
+ package main
+
+ import (
+ "fmt"
+ "dario.cat/mergo"
+ )
+
+ type Foo struct {
+ A string
+ B int64
+ }
+
+ func main() {
+ src := Foo{
+ A: "one",
+ B: 2,
+ }
+ dest := Foo{
+ A: "two",
+ }
+ mergo.Merge(&dest, src)
+ fmt.Println(dest)
+ // Will print
+ // {two 2}
+ }
+
+# Transformers
+
+Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, time.Time is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero time.Time?
+
+ package main
+
+ import (
+ "fmt"
+ "dario.cat/mergo"
+ "reflect"
+ "time"
+ )
+
+ type timeTransformer struct {
+ }
+
+ func (t timeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
+ if typ == reflect.TypeOf(time.Time{}) {
+ return func(dst, src reflect.Value) error {
+ if dst.CanSet() {
+ isZero := dst.MethodByName("IsZero")
+ result := isZero.Call([]reflect.Value{})
+ if result[0].Bool() {
+ dst.Set(src)
+ }
+ }
+ return nil
+ }
+ }
+ return nil
+ }
+
+ type Snapshot struct {
+ Time time.Time
+ // ...
+ }
+
+ func main() {
+ src := Snapshot{time.Now()}
+ dest := Snapshot{}
+ mergo.Merge(&dest, src, mergo.WithTransformers(timeTransformer{}))
+ fmt.Println(dest)
+ // Will print
+ // { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 }
+ }
+
+# Contact me
+
+If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): https://twitter.com/im_dario
+
+# About
+
+Written by Dario Castañé: https://da.rio.hn
+
+# License
+
+BSD 3-Clause license, as Go language.
+*/
+package mergo
diff --git a/vendor/dario.cat/mergo/map.go b/vendor/dario.cat/mergo/map.go
new file mode 100644
index 00000000..759b4f74
--- /dev/null
+++ b/vendor/dario.cat/mergo/map.go
@@ -0,0 +1,178 @@
+// Copyright 2014 Dario Castañé. All rights reserved.
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Based on src/pkg/reflect/deepequal.go from official
+// golang's stdlib.
+
+package mergo
+
+import (
+ "fmt"
+ "reflect"
+ "unicode"
+ "unicode/utf8"
+)
+
+func changeInitialCase(s string, mapper func(rune) rune) string {
+ if s == "" {
+ return s
+ }
+ r, n := utf8.DecodeRuneInString(s)
+ return string(mapper(r)) + s[n:]
+}
+
+func isExported(field reflect.StructField) bool {
+ r, _ := utf8.DecodeRuneInString(field.Name)
+ return r >= 'A' && r <= 'Z'
+}
+
+// Traverses recursively both values, assigning src's fields values to dst.
+// The map argument tracks comparisons that have already been seen, which allows
+// short circuiting on recursive types.
+func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
+ overwrite := config.Overwrite
+ if dst.CanAddr() {
+ addr := dst.UnsafeAddr()
+ h := 17 * addr
+ seen := visited[h]
+ typ := dst.Type()
+ for p := seen; p != nil; p = p.next {
+ if p.ptr == addr && p.typ == typ {
+ return nil
+ }
+ }
+ // Remember, remember...
+ visited[h] = &visit{typ, seen, addr}
+ }
+ zeroValue := reflect.Value{}
+ switch dst.Kind() {
+ case reflect.Map:
+ dstMap := dst.Interface().(map[string]interface{})
+ for i, n := 0, src.NumField(); i < n; i++ {
+ srcType := src.Type()
+ field := srcType.Field(i)
+ if !isExported(field) {
+ continue
+ }
+ fieldName := field.Name
+ fieldName = changeInitialCase(fieldName, unicode.ToLower)
+ if _, ok := dstMap[fieldName]; !ok || (!isEmptyValue(reflect.ValueOf(src.Field(i).Interface()), !config.ShouldNotDereference) && overwrite) || config.overwriteWithEmptyValue {
+ dstMap[fieldName] = src.Field(i).Interface()
+ }
+ }
+ case reflect.Ptr:
+ if dst.IsNil() {
+ v := reflect.New(dst.Type().Elem())
+ dst.Set(v)
+ }
+ dst = dst.Elem()
+ fallthrough
+ case reflect.Struct:
+ srcMap := src.Interface().(map[string]interface{})
+ for key := range srcMap {
+ config.overwriteWithEmptyValue = true
+ srcValue := srcMap[key]
+ fieldName := changeInitialCase(key, unicode.ToUpper)
+ dstElement := dst.FieldByName(fieldName)
+ if dstElement == zeroValue {
+ // We discard it because the field doesn't exist.
+ continue
+ }
+ srcElement := reflect.ValueOf(srcValue)
+ dstKind := dstElement.Kind()
+ srcKind := srcElement.Kind()
+ if srcKind == reflect.Ptr && dstKind != reflect.Ptr {
+ srcElement = srcElement.Elem()
+ srcKind = reflect.TypeOf(srcElement.Interface()).Kind()
+ } else if dstKind == reflect.Ptr {
+ // Can this work? I guess it can't.
+ if srcKind != reflect.Ptr && srcElement.CanAddr() {
+ srcPtr := srcElement.Addr()
+ srcElement = reflect.ValueOf(srcPtr)
+ srcKind = reflect.Ptr
+ }
+ }
+
+ if !srcElement.IsValid() {
+ continue
+ }
+ if srcKind == dstKind {
+ if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
+ return
+ }
+ } else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface {
+ if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
+ return
+ }
+ } else if srcKind == reflect.Map {
+ if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil {
+ return
+ }
+ } else {
+ return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
+ }
+ }
+ }
+ return
+}
+
+// Map sets fields' values in dst from src.
+// src can be a map with string keys or a struct. dst must be the opposite:
+// if src is a map, dst must be a valid pointer to struct. If src is a struct,
+// dst must be map[string]interface{}.
+// It won't merge unexported (private) fields and will do recursively
+// any exported field.
+// If dst is a map, keys will be src fields' names in lower camel case.
+// Missing key in src that doesn't match a field in dst will be skipped. This
+// doesn't apply if dst is a map.
+// This is separated method from Merge because it is cleaner and it keeps sane
+// semantics: merging equal types, mapping different (restricted) types.
+func Map(dst, src interface{}, opts ...func(*Config)) error {
+ return _map(dst, src, opts...)
+}
+
+// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by
+// non-empty src attribute values.
+// Deprecated: Use Map(…) with WithOverride
+func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
+ return _map(dst, src, append(opts, WithOverride)...)
+}
+
+func _map(dst, src interface{}, opts ...func(*Config)) error {
+ if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
+ return ErrNonPointerArgument
+ }
+ var (
+ vDst, vSrc reflect.Value
+ err error
+ )
+ config := &Config{}
+
+ for _, opt := range opts {
+ opt(config)
+ }
+
+ if vDst, vSrc, err = resolveValues(dst, src); err != nil {
+ return err
+ }
+ // To be friction-less, we redirect equal-type arguments
+ // to deepMerge. Only because arguments can be anything.
+ if vSrc.Kind() == vDst.Kind() {
+ return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
+ }
+ switch vSrc.Kind() {
+ case reflect.Struct:
+ if vDst.Kind() != reflect.Map {
+ return ErrExpectedMapAsDestination
+ }
+ case reflect.Map:
+ if vDst.Kind() != reflect.Struct {
+ return ErrExpectedStructAsDestination
+ }
+ default:
+ return ErrNotSupported
+ }
+ return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config)
+}
diff --git a/vendor/dario.cat/mergo/merge.go b/vendor/dario.cat/mergo/merge.go
new file mode 100644
index 00000000..fd47c95b
--- /dev/null
+++ b/vendor/dario.cat/mergo/merge.go
@@ -0,0 +1,409 @@
+// Copyright 2013 Dario Castañé. All rights reserved.
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Based on src/pkg/reflect/deepequal.go from official
+// golang's stdlib.
+
+package mergo
+
+import (
+ "fmt"
+ "reflect"
+)
+
+func hasMergeableFields(dst reflect.Value) (exported bool) {
+ for i, n := 0, dst.NumField(); i < n; i++ {
+ field := dst.Type().Field(i)
+ if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
+ exported = exported || hasMergeableFields(dst.Field(i))
+ } else if isExportedComponent(&field) {
+ exported = exported || len(field.PkgPath) == 0
+ }
+ }
+ return
+}
+
+func isExportedComponent(field *reflect.StructField) bool {
+ pkgPath := field.PkgPath
+ if len(pkgPath) > 0 {
+ return false
+ }
+ c := field.Name[0]
+ if 'a' <= c && c <= 'z' || c == '_' {
+ return false
+ }
+ return true
+}
+
+type Config struct {
+ Transformers Transformers
+ Overwrite bool
+ ShouldNotDereference bool
+ AppendSlice bool
+ TypeCheck bool
+ overwriteWithEmptyValue bool
+ overwriteSliceWithEmptyValue bool
+ sliceDeepCopy bool
+ debug bool
+}
+
+type Transformers interface {
+ Transformer(reflect.Type) func(dst, src reflect.Value) error
+}
+
+// Traverses recursively both values, assigning src's fields values to dst.
+// The map argument tracks comparisons that have already been seen, which allows
+// short circuiting on recursive types.
+func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
+ overwrite := config.Overwrite
+ typeCheck := config.TypeCheck
+ overwriteWithEmptySrc := config.overwriteWithEmptyValue
+ overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue
+ sliceDeepCopy := config.sliceDeepCopy
+
+ if !src.IsValid() {
+ return
+ }
+ if dst.CanAddr() {
+ addr := dst.UnsafeAddr()
+ h := 17 * addr
+ seen := visited[h]
+ typ := dst.Type()
+ for p := seen; p != nil; p = p.next {
+ if p.ptr == addr && p.typ == typ {
+ return nil
+ }
+ }
+ // Remember, remember...
+ visited[h] = &visit{typ, seen, addr}
+ }
+
+ if config.Transformers != nil && !isReflectNil(dst) && dst.IsValid() {
+ if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
+ err = fn(dst, src)
+ return
+ }
+ }
+
+ switch dst.Kind() {
+ case reflect.Struct:
+ if hasMergeableFields(dst) {
+ for i, n := 0, dst.NumField(); i < n; i++ {
+ if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
+ return
+ }
+ }
+ } else {
+ if dst.CanSet() && (isReflectNil(dst) || overwrite) && (!isEmptyValue(src, !config.ShouldNotDereference) || overwriteWithEmptySrc) {
+ dst.Set(src)
+ }
+ }
+ case reflect.Map:
+ if dst.IsNil() && !src.IsNil() {
+ if dst.CanSet() {
+ dst.Set(reflect.MakeMap(dst.Type()))
+ } else {
+ dst = src
+ return
+ }
+ }
+
+ if src.Kind() != reflect.Map {
+ if overwrite && dst.CanSet() {
+ dst.Set(src)
+ }
+ return
+ }
+
+ for _, key := range src.MapKeys() {
+ srcElement := src.MapIndex(key)
+ if !srcElement.IsValid() {
+ continue
+ }
+ dstElement := dst.MapIndex(key)
+ switch srcElement.Kind() {
+ case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
+ if srcElement.IsNil() {
+ if overwrite {
+ dst.SetMapIndex(key, srcElement)
+ }
+ continue
+ }
+ fallthrough
+ default:
+ if !srcElement.CanInterface() {
+ continue
+ }
+ switch reflect.TypeOf(srcElement.Interface()).Kind() {
+ case reflect.Struct:
+ fallthrough
+ case reflect.Ptr:
+ fallthrough
+ case reflect.Map:
+ srcMapElm := srcElement
+ dstMapElm := dstElement
+ if srcMapElm.CanInterface() {
+ srcMapElm = reflect.ValueOf(srcMapElm.Interface())
+ if dstMapElm.IsValid() {
+ dstMapElm = reflect.ValueOf(dstMapElm.Interface())
+ }
+ }
+ if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil {
+ return
+ }
+ case reflect.Slice:
+ srcSlice := reflect.ValueOf(srcElement.Interface())
+
+ var dstSlice reflect.Value
+ if !dstElement.IsValid() || dstElement.IsNil() {
+ dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
+ } else {
+ dstSlice = reflect.ValueOf(dstElement.Interface())
+ }
+
+ if (!isEmptyValue(src, !config.ShouldNotDereference) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst, !config.ShouldNotDereference)) && !config.AppendSlice && !sliceDeepCopy {
+ if typeCheck && srcSlice.Type() != dstSlice.Type() {
+ return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
+ }
+ dstSlice = srcSlice
+ } else if config.AppendSlice {
+ if srcSlice.Type() != dstSlice.Type() {
+ return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
+ }
+ dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
+ } else if sliceDeepCopy {
+ i := 0
+ for ; i < srcSlice.Len() && i < dstSlice.Len(); i++ {
+ srcElement := srcSlice.Index(i)
+ dstElement := dstSlice.Index(i)
+
+ if srcElement.CanInterface() {
+ srcElement = reflect.ValueOf(srcElement.Interface())
+ }
+ if dstElement.CanInterface() {
+ dstElement = reflect.ValueOf(dstElement.Interface())
+ }
+
+ if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
+ return
+ }
+ }
+
+ }
+ dst.SetMapIndex(key, dstSlice)
+ }
+ }
+
+ if dstElement.IsValid() && !isEmptyValue(dstElement, !config.ShouldNotDereference) {
+ if reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice {
+ continue
+ }
+ if reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map && reflect.TypeOf(dstElement.Interface()).Kind() == reflect.Map {
+ continue
+ }
+ }
+
+ if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement, !config.ShouldNotDereference)) {
+ if dst.IsNil() {
+ dst.Set(reflect.MakeMap(dst.Type()))
+ }
+ dst.SetMapIndex(key, srcElement)
+ }
+ }
+
+ // Ensure that all keys in dst are deleted if they are not in src.
+ if overwriteWithEmptySrc {
+ for _, key := range dst.MapKeys() {
+ srcElement := src.MapIndex(key)
+ if !srcElement.IsValid() {
+ dst.SetMapIndex(key, reflect.Value{})
+ }
+ }
+ }
+ case reflect.Slice:
+ if !dst.CanSet() {
+ break
+ }
+ if (!isEmptyValue(src, !config.ShouldNotDereference) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst, !config.ShouldNotDereference)) && !config.AppendSlice && !sliceDeepCopy {
+ dst.Set(src)
+ } else if config.AppendSlice {
+ if src.Type() != dst.Type() {
+ return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
+ }
+ dst.Set(reflect.AppendSlice(dst, src))
+ } else if sliceDeepCopy {
+ for i := 0; i < src.Len() && i < dst.Len(); i++ {
+ srcElement := src.Index(i)
+ dstElement := dst.Index(i)
+ if srcElement.CanInterface() {
+ srcElement = reflect.ValueOf(srcElement.Interface())
+ }
+ if dstElement.CanInterface() {
+ dstElement = reflect.ValueOf(dstElement.Interface())
+ }
+
+ if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
+ return
+ }
+ }
+ }
+ case reflect.Ptr:
+ fallthrough
+ case reflect.Interface:
+ if isReflectNil(src) {
+ if overwriteWithEmptySrc && dst.CanSet() && src.Type().AssignableTo(dst.Type()) {
+ dst.Set(src)
+ }
+ break
+ }
+
+ if src.Kind() != reflect.Interface {
+ if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
+ if dst.CanSet() && (overwrite || isEmptyValue(dst, !config.ShouldNotDereference)) {
+ dst.Set(src)
+ }
+ } else if src.Kind() == reflect.Ptr {
+ if !config.ShouldNotDereference {
+ if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
+ return
+ }
+ } else if src.Elem().Kind() != reflect.Struct {
+ if overwriteWithEmptySrc || (overwrite && !src.IsNil()) || dst.IsNil() {
+ dst.Set(src)
+ }
+ }
+ } else if dst.Elem().Type() == src.Type() {
+ if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
+ return
+ }
+ } else {
+ return ErrDifferentArgumentsTypes
+ }
+ break
+ }
+
+ if dst.IsNil() || overwrite {
+ if dst.CanSet() && (overwrite || isEmptyValue(dst, !config.ShouldNotDereference)) {
+ dst.Set(src)
+ }
+ break
+ }
+
+ if dst.Elem().Kind() == src.Elem().Kind() {
+ if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
+ return
+ }
+ break
+ }
+ default:
+ mustSet := (isEmptyValue(dst, !config.ShouldNotDereference) || overwrite) && (!isEmptyValue(src, !config.ShouldNotDereference) || overwriteWithEmptySrc)
+ if mustSet {
+ if dst.CanSet() {
+ dst.Set(src)
+ } else {
+ dst = src
+ }
+ }
+ }
+
+ return
+}
+
+// Merge will fill any empty for value type attributes on the dst struct using corresponding
+// src attributes if they themselves are not empty. dst and src must be valid same-type structs
+// and dst must be a pointer to struct.
+// It won't merge unexported (private) fields and will do recursively any exported field.
+func Merge(dst, src interface{}, opts ...func(*Config)) error {
+ return merge(dst, src, opts...)
+}
+
+// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
+// non-empty src attribute values.
+// Deprecated: use Merge(…) with WithOverride
+func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
+ return merge(dst, src, append(opts, WithOverride)...)
+}
+
+// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
+func WithTransformers(transformers Transformers) func(*Config) {
+ return func(config *Config) {
+ config.Transformers = transformers
+ }
+}
+
+// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
+func WithOverride(config *Config) {
+ config.Overwrite = true
+}
+
+// WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values.
+func WithOverwriteWithEmptyValue(config *Config) {
+ config.Overwrite = true
+ config.overwriteWithEmptyValue = true
+}
+
+// WithOverrideEmptySlice will make merge override empty dst slice with empty src slice.
+func WithOverrideEmptySlice(config *Config) {
+ config.overwriteSliceWithEmptyValue = true
+}
+
+// WithoutDereference prevents dereferencing pointers when evaluating whether they are empty
+// (i.e. a non-nil pointer is never considered empty).
+func WithoutDereference(config *Config) {
+ config.ShouldNotDereference = true
+}
+
+// WithAppendSlice will make merge append slices instead of overwriting it.
+func WithAppendSlice(config *Config) {
+ config.AppendSlice = true
+}
+
+// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
+func WithTypeCheck(config *Config) {
+ config.TypeCheck = true
+}
+
+// WithSliceDeepCopy will merge slice element one by one with Overwrite flag.
+func WithSliceDeepCopy(config *Config) {
+ config.sliceDeepCopy = true
+ config.Overwrite = true
+}
+
+func merge(dst, src interface{}, opts ...func(*Config)) error {
+ if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
+ return ErrNonPointerArgument
+ }
+ var (
+ vDst, vSrc reflect.Value
+ err error
+ )
+
+ config := &Config{}
+
+ for _, opt := range opts {
+ opt(config)
+ }
+
+ if vDst, vSrc, err = resolveValues(dst, src); err != nil {
+ return err
+ }
+ if vDst.Type() != vSrc.Type() {
+ return ErrDifferentArgumentsTypes
+ }
+ return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
+}
+
+// IsReflectNil is the reflect value provided nil
+func isReflectNil(v reflect.Value) bool {
+ k := v.Kind()
+ switch k {
+ case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr:
+ // Both interface and slice are nil if first word is 0.
+ // Both are always bigger than a word; assume flagIndir.
+ return v.IsNil()
+ default:
+ return false
+ }
+}
diff --git a/vendor/dario.cat/mergo/mergo.go b/vendor/dario.cat/mergo/mergo.go
new file mode 100644
index 00000000..0a721e2d
--- /dev/null
+++ b/vendor/dario.cat/mergo/mergo.go
@@ -0,0 +1,81 @@
+// Copyright 2013 Dario Castañé. All rights reserved.
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Based on src/pkg/reflect/deepequal.go from official
+// golang's stdlib.
+
+package mergo
+
+import (
+ "errors"
+ "reflect"
+)
+
+// Errors reported by Mergo when it finds invalid arguments.
+var (
+ ErrNilArguments = errors.New("src and dst must not be nil")
+ ErrDifferentArgumentsTypes = errors.New("src and dst must be of same type")
+ ErrNotSupported = errors.New("only structs, maps, and slices are supported")
+ ErrExpectedMapAsDestination = errors.New("dst was expected to be a map")
+ ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct")
+ ErrNonPointerArgument = errors.New("dst must be a pointer")
+)
+
+// During deepMerge, must keep track of checks that are
+// in progress. The comparison algorithm assumes that all
+// checks in progress are true when it reencounters them.
+// Visited are stored in a map indexed by 17 * a1 + a2;
+type visit struct {
+ typ reflect.Type
+ next *visit
+ ptr uintptr
+}
+
+// From src/pkg/encoding/json/encode.go.
+func isEmptyValue(v reflect.Value, shouldDereference bool) bool {
+ switch v.Kind() {
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ return v.Len() == 0
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return v.Float() == 0
+ case reflect.Interface, reflect.Ptr:
+ if v.IsNil() {
+ return true
+ }
+ if shouldDereference {
+ return isEmptyValue(v.Elem(), shouldDereference)
+ }
+ return false
+ case reflect.Func:
+ return v.IsNil()
+ case reflect.Invalid:
+ return true
+ }
+ return false
+}
+
+func resolveValues(dst, src interface{}) (vDst, vSrc reflect.Value, err error) {
+ if dst == nil || src == nil {
+ err = ErrNilArguments
+ return
+ }
+ vDst = reflect.ValueOf(dst).Elem()
+ if vDst.Kind() != reflect.Struct && vDst.Kind() != reflect.Map && vDst.Kind() != reflect.Slice {
+ err = ErrNotSupported
+ return
+ }
+ vSrc = reflect.ValueOf(src)
+ // We check if vSrc is a pointer to dereference it.
+ if vSrc.Kind() == reflect.Ptr {
+ vSrc = vSrc.Elem()
+ }
+ return
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/LICENSE b/vendor/github.com/Azure/go-ansiterm/LICENSE
new file mode 100644
index 00000000..e3d9a64d
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Microsoft Corporation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/Azure/go-ansiterm/README.md b/vendor/github.com/Azure/go-ansiterm/README.md
new file mode 100644
index 00000000..261c041e
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/README.md
@@ -0,0 +1,12 @@
+# go-ansiterm
+
+This is a cross platform Ansi Terminal Emulation library. It reads a stream of Ansi characters and produces the appropriate function calls. The results of the function calls are platform dependent.
+
+For example the parser might receive "ESC, [, A" as a stream of three characters. This is the code for Cursor Up (http://www.vt100.net/docs/vt510-rm/CUU). The parser then calls the cursor up function (CUU()) on an event handler. The event handler determines what platform specific work must be done to cause the cursor to move up one position.
+
+The parser (parser.go) is a partial implementation of this state machine (http://vt100.net/emu/vt500_parser.png). There are also two event handler implementations, one for tests (test_event_handler.go) to validate that the expected events are being produced and called, the other is a Windows implementation (winterm/win_event_handler.go).
+
+See parser_test.go for examples exercising the state machine and generating appropriate function calls.
+
+-----
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
diff --git a/vendor/github.com/Azure/go-ansiterm/SECURITY.md b/vendor/github.com/Azure/go-ansiterm/SECURITY.md
new file mode 100644
index 00000000..e138ec5d
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/SECURITY.md
@@ -0,0 +1,41 @@
+
+
+## Security
+
+Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
+
+If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
+
+## Reporting Security Issues
+
+**Please do not report security vulnerabilities through public GitHub issues.**
+
+Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
+
+If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
+
+You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
+
+Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
+
+ * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
+ * Full paths of source file(s) related to the manifestation of the issue
+ * The location of the affected source code (tag/branch/commit or direct URL)
+ * Any special configuration required to reproduce the issue
+ * Step-by-step instructions to reproduce the issue
+ * Proof-of-concept or exploit code (if possible)
+ * Impact of the issue, including how an attacker might exploit the issue
+
+This information will help us triage your report more quickly.
+
+If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
+
+## Preferred Languages
+
+We prefer all communications to be in English.
+
+## Policy
+
+Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
+
+
diff --git a/vendor/github.com/Azure/go-ansiterm/constants.go b/vendor/github.com/Azure/go-ansiterm/constants.go
new file mode 100644
index 00000000..96504a33
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/constants.go
@@ -0,0 +1,188 @@
+package ansiterm
+
+const LogEnv = "DEBUG_TERMINAL"
+
+// ANSI constants
+// References:
+// -- http://www.ecma-international.org/publications/standards/Ecma-048.htm
+// -- http://man7.org/linux/man-pages/man4/console_codes.4.html
+// -- http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html
+// -- http://en.wikipedia.org/wiki/ANSI_escape_code
+// -- http://vt100.net/emu/dec_ansi_parser
+// -- http://vt100.net/emu/vt500_parser.svg
+// -- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+// -- http://www.inwap.com/pdp10/ansicode.txt
+const (
+ // ECMA-48 Set Graphics Rendition
+ // Note:
+ // -- Constants leading with an underscore (e.g., _ANSI_xxx) are unsupported or reserved
+ // -- Fonts could possibly be supported via SetCurrentConsoleFontEx
+ // -- Windows does not expose the per-window cursor (i.e., caret) blink times
+ ANSI_SGR_RESET = 0
+ ANSI_SGR_BOLD = 1
+ ANSI_SGR_DIM = 2
+ _ANSI_SGR_ITALIC = 3
+ ANSI_SGR_UNDERLINE = 4
+ _ANSI_SGR_BLINKSLOW = 5
+ _ANSI_SGR_BLINKFAST = 6
+ ANSI_SGR_REVERSE = 7
+ _ANSI_SGR_INVISIBLE = 8
+ _ANSI_SGR_LINETHROUGH = 9
+ _ANSI_SGR_FONT_00 = 10
+ _ANSI_SGR_FONT_01 = 11
+ _ANSI_SGR_FONT_02 = 12
+ _ANSI_SGR_FONT_03 = 13
+ _ANSI_SGR_FONT_04 = 14
+ _ANSI_SGR_FONT_05 = 15
+ _ANSI_SGR_FONT_06 = 16
+ _ANSI_SGR_FONT_07 = 17
+ _ANSI_SGR_FONT_08 = 18
+ _ANSI_SGR_FONT_09 = 19
+ _ANSI_SGR_FONT_10 = 20
+ _ANSI_SGR_DOUBLEUNDERLINE = 21
+ ANSI_SGR_BOLD_DIM_OFF = 22
+ _ANSI_SGR_ITALIC_OFF = 23
+ ANSI_SGR_UNDERLINE_OFF = 24
+ _ANSI_SGR_BLINK_OFF = 25
+ _ANSI_SGR_RESERVED_00 = 26
+ ANSI_SGR_REVERSE_OFF = 27
+ _ANSI_SGR_INVISIBLE_OFF = 28
+ _ANSI_SGR_LINETHROUGH_OFF = 29
+ ANSI_SGR_FOREGROUND_BLACK = 30
+ ANSI_SGR_FOREGROUND_RED = 31
+ ANSI_SGR_FOREGROUND_GREEN = 32
+ ANSI_SGR_FOREGROUND_YELLOW = 33
+ ANSI_SGR_FOREGROUND_BLUE = 34
+ ANSI_SGR_FOREGROUND_MAGENTA = 35
+ ANSI_SGR_FOREGROUND_CYAN = 36
+ ANSI_SGR_FOREGROUND_WHITE = 37
+ _ANSI_SGR_RESERVED_01 = 38
+ ANSI_SGR_FOREGROUND_DEFAULT = 39
+ ANSI_SGR_BACKGROUND_BLACK = 40
+ ANSI_SGR_BACKGROUND_RED = 41
+ ANSI_SGR_BACKGROUND_GREEN = 42
+ ANSI_SGR_BACKGROUND_YELLOW = 43
+ ANSI_SGR_BACKGROUND_BLUE = 44
+ ANSI_SGR_BACKGROUND_MAGENTA = 45
+ ANSI_SGR_BACKGROUND_CYAN = 46
+ ANSI_SGR_BACKGROUND_WHITE = 47
+ _ANSI_SGR_RESERVED_02 = 48
+ ANSI_SGR_BACKGROUND_DEFAULT = 49
+ // 50 - 65: Unsupported
+
+ ANSI_MAX_CMD_LENGTH = 4096
+
+ MAX_INPUT_EVENTS = 128
+ DEFAULT_WIDTH = 80
+ DEFAULT_HEIGHT = 24
+
+ ANSI_BEL = 0x07
+ ANSI_BACKSPACE = 0x08
+ ANSI_TAB = 0x09
+ ANSI_LINE_FEED = 0x0A
+ ANSI_VERTICAL_TAB = 0x0B
+ ANSI_FORM_FEED = 0x0C
+ ANSI_CARRIAGE_RETURN = 0x0D
+ ANSI_ESCAPE_PRIMARY = 0x1B
+ ANSI_ESCAPE_SECONDARY = 0x5B
+ ANSI_OSC_STRING_ENTRY = 0x5D
+ ANSI_COMMAND_FIRST = 0x40
+ ANSI_COMMAND_LAST = 0x7E
+ DCS_ENTRY = 0x90
+ CSI_ENTRY = 0x9B
+ OSC_STRING = 0x9D
+ ANSI_PARAMETER_SEP = ";"
+ ANSI_CMD_G0 = '('
+ ANSI_CMD_G1 = ')'
+ ANSI_CMD_G2 = '*'
+ ANSI_CMD_G3 = '+'
+ ANSI_CMD_DECPNM = '>'
+ ANSI_CMD_DECPAM = '='
+ ANSI_CMD_OSC = ']'
+ ANSI_CMD_STR_TERM = '\\'
+
+ KEY_CONTROL_PARAM_2 = ";2"
+ KEY_CONTROL_PARAM_3 = ";3"
+ KEY_CONTROL_PARAM_4 = ";4"
+ KEY_CONTROL_PARAM_5 = ";5"
+ KEY_CONTROL_PARAM_6 = ";6"
+ KEY_CONTROL_PARAM_7 = ";7"
+ KEY_CONTROL_PARAM_8 = ";8"
+ KEY_ESC_CSI = "\x1B["
+ KEY_ESC_N = "\x1BN"
+ KEY_ESC_O = "\x1BO"
+
+ FILL_CHARACTER = ' '
+)
+
+func getByteRange(start byte, end byte) []byte {
+ bytes := make([]byte, 0, 32)
+ for i := start; i <= end; i++ {
+ bytes = append(bytes, byte(i))
+ }
+
+ return bytes
+}
+
+var toGroundBytes = getToGroundBytes()
+var executors = getExecuteBytes()
+
+// SPACE 20+A0 hex Always and everywhere a blank space
+// Intermediate 20-2F hex !"#$%&'()*+,-./
+var intermeds = getByteRange(0x20, 0x2F)
+
+// Parameters 30-3F hex 0123456789:;<=>?
+// CSI Parameters 30-39, 3B hex 0123456789;
+var csiParams = getByteRange(0x30, 0x3F)
+
+var csiCollectables = append(getByteRange(0x30, 0x39), getByteRange(0x3B, 0x3F)...)
+
+// Uppercase 40-5F hex @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
+var upperCase = getByteRange(0x40, 0x5F)
+
+// Lowercase 60-7E hex `abcdefghijlkmnopqrstuvwxyz{|}~
+var lowerCase = getByteRange(0x60, 0x7E)
+
+// Alphabetics 40-7E hex (all of upper and lower case)
+var alphabetics = append(upperCase, lowerCase...)
+
+var printables = getByteRange(0x20, 0x7F)
+
+var escapeIntermediateToGroundBytes = getByteRange(0x30, 0x7E)
+var escapeToGroundBytes = getEscapeToGroundBytes()
+
+// See http://www.vt100.net/emu/vt500_parser.png for description of the complex
+// byte ranges below
+
+func getEscapeToGroundBytes() []byte {
+ escapeToGroundBytes := getByteRange(0x30, 0x4F)
+ escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x51, 0x57)...)
+ escapeToGroundBytes = append(escapeToGroundBytes, 0x59)
+ escapeToGroundBytes = append(escapeToGroundBytes, 0x5A)
+ escapeToGroundBytes = append(escapeToGroundBytes, 0x5C)
+ escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x60, 0x7E)...)
+ return escapeToGroundBytes
+}
+
+func getExecuteBytes() []byte {
+ executeBytes := getByteRange(0x00, 0x17)
+ executeBytes = append(executeBytes, 0x19)
+ executeBytes = append(executeBytes, getByteRange(0x1C, 0x1F)...)
+ return executeBytes
+}
+
+func getToGroundBytes() []byte {
+ groundBytes := []byte{0x18}
+ groundBytes = append(groundBytes, 0x1A)
+ groundBytes = append(groundBytes, getByteRange(0x80, 0x8F)...)
+ groundBytes = append(groundBytes, getByteRange(0x91, 0x97)...)
+ groundBytes = append(groundBytes, 0x99)
+ groundBytes = append(groundBytes, 0x9A)
+ groundBytes = append(groundBytes, 0x9C)
+ return groundBytes
+}
+
+// Delete 7F hex Always and everywhere ignored
+// C1 Control 80-9F hex 32 additional control characters
+// G1 Displayable A1-FE hex 94 additional displayable characters
+// Special A0+FF hex Same as SPACE and DELETE
diff --git a/vendor/github.com/Azure/go-ansiterm/context.go b/vendor/github.com/Azure/go-ansiterm/context.go
new file mode 100644
index 00000000..8d66e777
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/context.go
@@ -0,0 +1,7 @@
+package ansiterm
+
+type ansiContext struct {
+ currentChar byte
+ paramBuffer []byte
+ interBuffer []byte
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/csi_entry_state.go b/vendor/github.com/Azure/go-ansiterm/csi_entry_state.go
new file mode 100644
index 00000000..bcbe00d0
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/csi_entry_state.go
@@ -0,0 +1,49 @@
+package ansiterm
+
+type csiEntryState struct {
+ baseState
+}
+
+func (csiState csiEntryState) Handle(b byte) (s state, e error) {
+ csiState.parser.logf("CsiEntry::Handle %#x", b)
+
+ nextState, err := csiState.baseState.Handle(b)
+ if nextState != nil || err != nil {
+ return nextState, err
+ }
+
+ switch {
+ case sliceContains(alphabetics, b):
+ return csiState.parser.ground, nil
+ case sliceContains(csiCollectables, b):
+ return csiState.parser.csiParam, nil
+ case sliceContains(executors, b):
+ return csiState, csiState.parser.execute()
+ }
+
+ return csiState, nil
+}
+
+func (csiState csiEntryState) Transition(s state) error {
+ csiState.parser.logf("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name())
+ csiState.baseState.Transition(s)
+
+ switch s {
+ case csiState.parser.ground:
+ return csiState.parser.csiDispatch()
+ case csiState.parser.csiParam:
+ switch {
+ case sliceContains(csiParams, csiState.parser.context.currentChar):
+ csiState.parser.collectParam()
+ case sliceContains(intermeds, csiState.parser.context.currentChar):
+ csiState.parser.collectInter()
+ }
+ }
+
+ return nil
+}
+
+func (csiState csiEntryState) Enter() error {
+ csiState.parser.clear()
+ return nil
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/csi_param_state.go b/vendor/github.com/Azure/go-ansiterm/csi_param_state.go
new file mode 100644
index 00000000..7ed5e01c
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/csi_param_state.go
@@ -0,0 +1,38 @@
+package ansiterm
+
+type csiParamState struct {
+ baseState
+}
+
+func (csiState csiParamState) Handle(b byte) (s state, e error) {
+ csiState.parser.logf("CsiParam::Handle %#x", b)
+
+ nextState, err := csiState.baseState.Handle(b)
+ if nextState != nil || err != nil {
+ return nextState, err
+ }
+
+ switch {
+ case sliceContains(alphabetics, b):
+ return csiState.parser.ground, nil
+ case sliceContains(csiCollectables, b):
+ csiState.parser.collectParam()
+ return csiState, nil
+ case sliceContains(executors, b):
+ return csiState, csiState.parser.execute()
+ }
+
+ return csiState, nil
+}
+
+func (csiState csiParamState) Transition(s state) error {
+ csiState.parser.logf("CsiParam::Transition %s --> %s", csiState.Name(), s.Name())
+ csiState.baseState.Transition(s)
+
+ switch s {
+ case csiState.parser.ground:
+ return csiState.parser.csiDispatch()
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go b/vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go
new file mode 100644
index 00000000..1c719db9
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go
@@ -0,0 +1,36 @@
+package ansiterm
+
+type escapeIntermediateState struct {
+ baseState
+}
+
+func (escState escapeIntermediateState) Handle(b byte) (s state, e error) {
+ escState.parser.logf("escapeIntermediateState::Handle %#x", b)
+ nextState, err := escState.baseState.Handle(b)
+ if nextState != nil || err != nil {
+ return nextState, err
+ }
+
+ switch {
+ case sliceContains(intermeds, b):
+ return escState, escState.parser.collectInter()
+ case sliceContains(executors, b):
+ return escState, escState.parser.execute()
+ case sliceContains(escapeIntermediateToGroundBytes, b):
+ return escState.parser.ground, nil
+ }
+
+ return escState, nil
+}
+
+func (escState escapeIntermediateState) Transition(s state) error {
+ escState.parser.logf("escapeIntermediateState::Transition %s --> %s", escState.Name(), s.Name())
+ escState.baseState.Transition(s)
+
+ switch s {
+ case escState.parser.ground:
+ return escState.parser.escDispatch()
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/escape_state.go b/vendor/github.com/Azure/go-ansiterm/escape_state.go
new file mode 100644
index 00000000..6390abd2
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/escape_state.go
@@ -0,0 +1,47 @@
+package ansiterm
+
+type escapeState struct {
+ baseState
+}
+
+func (escState escapeState) Handle(b byte) (s state, e error) {
+ escState.parser.logf("escapeState::Handle %#x", b)
+ nextState, err := escState.baseState.Handle(b)
+ if nextState != nil || err != nil {
+ return nextState, err
+ }
+
+ switch {
+ case b == ANSI_ESCAPE_SECONDARY:
+ return escState.parser.csiEntry, nil
+ case b == ANSI_OSC_STRING_ENTRY:
+ return escState.parser.oscString, nil
+ case sliceContains(executors, b):
+ return escState, escState.parser.execute()
+ case sliceContains(escapeToGroundBytes, b):
+ return escState.parser.ground, nil
+ case sliceContains(intermeds, b):
+ return escState.parser.escapeIntermediate, nil
+ }
+
+ return escState, nil
+}
+
+func (escState escapeState) Transition(s state) error {
+ escState.parser.logf("Escape::Transition %s --> %s", escState.Name(), s.Name())
+ escState.baseState.Transition(s)
+
+ switch s {
+ case escState.parser.ground:
+ return escState.parser.escDispatch()
+ case escState.parser.escapeIntermediate:
+ return escState.parser.collectInter()
+ }
+
+ return nil
+}
+
+func (escState escapeState) Enter() error {
+ escState.parser.clear()
+ return nil
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/event_handler.go b/vendor/github.com/Azure/go-ansiterm/event_handler.go
new file mode 100644
index 00000000..98087b38
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/event_handler.go
@@ -0,0 +1,90 @@
+package ansiterm
+
+type AnsiEventHandler interface {
+ // Print
+ Print(b byte) error
+
+ // Execute C0 commands
+ Execute(b byte) error
+
+ // CUrsor Up
+ CUU(int) error
+
+ // CUrsor Down
+ CUD(int) error
+
+ // CUrsor Forward
+ CUF(int) error
+
+ // CUrsor Backward
+ CUB(int) error
+
+ // Cursor to Next Line
+ CNL(int) error
+
+ // Cursor to Previous Line
+ CPL(int) error
+
+ // Cursor Horizontal position Absolute
+ CHA(int) error
+
+ // Vertical line Position Absolute
+ VPA(int) error
+
+ // CUrsor Position
+ CUP(int, int) error
+
+ // Horizontal and Vertical Position (depends on PUM)
+ HVP(int, int) error
+
+ // Text Cursor Enable Mode
+ DECTCEM(bool) error
+
+ // Origin Mode
+ DECOM(bool) error
+
+ // 132 Column Mode
+ DECCOLM(bool) error
+
+ // Erase in Display
+ ED(int) error
+
+ // Erase in Line
+ EL(int) error
+
+ // Insert Line
+ IL(int) error
+
+ // Delete Line
+ DL(int) error
+
+ // Insert Character
+ ICH(int) error
+
+ // Delete Character
+ DCH(int) error
+
+ // Set Graphics Rendition
+ SGR([]int) error
+
+ // Pan Down
+ SU(int) error
+
+ // Pan Up
+ SD(int) error
+
+ // Device Attributes
+ DA([]string) error
+
+ // Set Top and Bottom Margins
+ DECSTBM(int, int) error
+
+ // Index
+ IND() error
+
+ // Reverse Index
+ RI() error
+
+ // Flush updates from previous commands
+ Flush() error
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/ground_state.go b/vendor/github.com/Azure/go-ansiterm/ground_state.go
new file mode 100644
index 00000000..52451e94
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/ground_state.go
@@ -0,0 +1,24 @@
+package ansiterm
+
+type groundState struct {
+ baseState
+}
+
+func (gs groundState) Handle(b byte) (s state, e error) {
+ gs.parser.context.currentChar = b
+
+ nextState, err := gs.baseState.Handle(b)
+ if nextState != nil || err != nil {
+ return nextState, err
+ }
+
+ switch {
+ case sliceContains(printables, b):
+ return gs, gs.parser.print()
+
+ case sliceContains(executors, b):
+ return gs, gs.parser.execute()
+ }
+
+ return gs, nil
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/osc_string_state.go b/vendor/github.com/Azure/go-ansiterm/osc_string_state.go
new file mode 100644
index 00000000..194d5e9c
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/osc_string_state.go
@@ -0,0 +1,23 @@
+package ansiterm
+
+type oscStringState struct {
+ baseState
+}
+
+func (oscState oscStringState) Handle(b byte) (s state, e error) {
+ oscState.parser.logf("OscString::Handle %#x", b)
+ nextState, err := oscState.baseState.Handle(b)
+ if nextState != nil || err != nil {
+ return nextState, err
+ }
+
+ // There are several control characters and sequences which can
+ // terminate an OSC string. Most of them are handled by the baseState
+ // handler. The ANSI_BEL character is a special case which behaves as a
+ // terminator only for an OSC string.
+ if b == ANSI_BEL {
+ return oscState.parser.ground, nil
+ }
+
+ return oscState, nil
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/parser.go b/vendor/github.com/Azure/go-ansiterm/parser.go
new file mode 100644
index 00000000..03cec7ad
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/parser.go
@@ -0,0 +1,151 @@
+package ansiterm
+
+import (
+ "errors"
+ "log"
+ "os"
+)
+
+type AnsiParser struct {
+ currState state
+ eventHandler AnsiEventHandler
+ context *ansiContext
+ csiEntry state
+ csiParam state
+ dcsEntry state
+ escape state
+ escapeIntermediate state
+ error state
+ ground state
+ oscString state
+ stateMap []state
+
+ logf func(string, ...interface{})
+}
+
+type Option func(*AnsiParser)
+
+func WithLogf(f func(string, ...interface{})) Option {
+ return func(ap *AnsiParser) {
+ ap.logf = f
+ }
+}
+
+func CreateParser(initialState string, evtHandler AnsiEventHandler, opts ...Option) *AnsiParser {
+ ap := &AnsiParser{
+ eventHandler: evtHandler,
+ context: &ansiContext{},
+ }
+ for _, o := range opts {
+ o(ap)
+ }
+
+ if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" {
+ logFile, _ := os.Create("ansiParser.log")
+ logger := log.New(logFile, "", log.LstdFlags)
+ if ap.logf != nil {
+ l := ap.logf
+ ap.logf = func(s string, v ...interface{}) {
+ l(s, v...)
+ logger.Printf(s, v...)
+ }
+ } else {
+ ap.logf = logger.Printf
+ }
+ }
+
+ if ap.logf == nil {
+ ap.logf = func(string, ...interface{}) {}
+ }
+
+ ap.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: ap}}
+ ap.csiParam = csiParamState{baseState{name: "CsiParam", parser: ap}}
+ ap.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: ap}}
+ ap.escape = escapeState{baseState{name: "Escape", parser: ap}}
+ ap.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: ap}}
+ ap.error = errorState{baseState{name: "Error", parser: ap}}
+ ap.ground = groundState{baseState{name: "Ground", parser: ap}}
+ ap.oscString = oscStringState{baseState{name: "OscString", parser: ap}}
+
+ ap.stateMap = []state{
+ ap.csiEntry,
+ ap.csiParam,
+ ap.dcsEntry,
+ ap.escape,
+ ap.escapeIntermediate,
+ ap.error,
+ ap.ground,
+ ap.oscString,
+ }
+
+ ap.currState = getState(initialState, ap.stateMap)
+
+ ap.logf("CreateParser: parser %p", ap)
+ return ap
+}
+
+func getState(name string, states []state) state {
+ for _, el := range states {
+ if el.Name() == name {
+ return el
+ }
+ }
+
+ return nil
+}
+
+func (ap *AnsiParser) Parse(bytes []byte) (int, error) {
+ for i, b := range bytes {
+ if err := ap.handle(b); err != nil {
+ return i, err
+ }
+ }
+
+ return len(bytes), ap.eventHandler.Flush()
+}
+
+func (ap *AnsiParser) handle(b byte) error {
+ ap.context.currentChar = b
+ newState, err := ap.currState.Handle(b)
+ if err != nil {
+ return err
+ }
+
+ if newState == nil {
+ ap.logf("WARNING: newState is nil")
+ return errors.New("New state of 'nil' is invalid.")
+ }
+
+ if newState != ap.currState {
+ if err := ap.changeState(newState); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (ap *AnsiParser) changeState(newState state) error {
+ ap.logf("ChangeState %s --> %s", ap.currState.Name(), newState.Name())
+
+ // Exit old state
+ if err := ap.currState.Exit(); err != nil {
+ ap.logf("Exit state '%s' failed with : '%v'", ap.currState.Name(), err)
+ return err
+ }
+
+ // Perform transition action
+ if err := ap.currState.Transition(newState); err != nil {
+ ap.logf("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err)
+ return err
+ }
+
+ // Enter new state
+ if err := newState.Enter(); err != nil {
+ ap.logf("Enter state '%s' failed with: '%v'", newState.Name(), err)
+ return err
+ }
+
+ ap.currState = newState
+ return nil
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go b/vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go
new file mode 100644
index 00000000..de0a1f9c
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go
@@ -0,0 +1,99 @@
+package ansiterm
+
+import (
+ "strconv"
+)
+
+func parseParams(bytes []byte) ([]string, error) {
+ paramBuff := make([]byte, 0, 0)
+ params := []string{}
+
+ for _, v := range bytes {
+ if v == ';' {
+ if len(paramBuff) > 0 {
+ // Completed parameter, append it to the list
+ s := string(paramBuff)
+ params = append(params, s)
+ paramBuff = make([]byte, 0, 0)
+ }
+ } else {
+ paramBuff = append(paramBuff, v)
+ }
+ }
+
+ // Last parameter may not be terminated with ';'
+ if len(paramBuff) > 0 {
+ s := string(paramBuff)
+ params = append(params, s)
+ }
+
+ return params, nil
+}
+
+func parseCmd(context ansiContext) (string, error) {
+ return string(context.currentChar), nil
+}
+
+func getInt(params []string, dflt int) int {
+ i := getInts(params, 1, dflt)[0]
+ return i
+}
+
+func getInts(params []string, minCount int, dflt int) []int {
+ ints := []int{}
+
+ for _, v := range params {
+ i, _ := strconv.Atoi(v)
+ // Zero is mapped to the default value in VT100.
+ if i == 0 {
+ i = dflt
+ }
+ ints = append(ints, i)
+ }
+
+ if len(ints) < minCount {
+ remaining := minCount - len(ints)
+ for i := 0; i < remaining; i++ {
+ ints = append(ints, dflt)
+ }
+ }
+
+ return ints
+}
+
+func (ap *AnsiParser) modeDispatch(param string, set bool) error {
+ switch param {
+ case "?3":
+ return ap.eventHandler.DECCOLM(set)
+ case "?6":
+ return ap.eventHandler.DECOM(set)
+ case "?25":
+ return ap.eventHandler.DECTCEM(set)
+ }
+ return nil
+}
+
+func (ap *AnsiParser) hDispatch(params []string) error {
+ if len(params) == 1 {
+ return ap.modeDispatch(params[0], true)
+ }
+
+ return nil
+}
+
+func (ap *AnsiParser) lDispatch(params []string) error {
+ if len(params) == 1 {
+ return ap.modeDispatch(params[0], false)
+ }
+
+ return nil
+}
+
+func getEraseParam(params []string) int {
+ param := getInt(params, 0)
+ if param < 0 || 3 < param {
+ param = 0
+ }
+
+ return param
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/parser_actions.go b/vendor/github.com/Azure/go-ansiterm/parser_actions.go
new file mode 100644
index 00000000..0bb5e51e
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/parser_actions.go
@@ -0,0 +1,119 @@
+package ansiterm
+
+func (ap *AnsiParser) collectParam() error {
+ currChar := ap.context.currentChar
+ ap.logf("collectParam %#x", currChar)
+ ap.context.paramBuffer = append(ap.context.paramBuffer, currChar)
+ return nil
+}
+
+func (ap *AnsiParser) collectInter() error {
+ currChar := ap.context.currentChar
+ ap.logf("collectInter %#x", currChar)
+ ap.context.paramBuffer = append(ap.context.interBuffer, currChar)
+ return nil
+}
+
+func (ap *AnsiParser) escDispatch() error {
+ cmd, _ := parseCmd(*ap.context)
+ intermeds := ap.context.interBuffer
+ ap.logf("escDispatch currentChar: %#x", ap.context.currentChar)
+ ap.logf("escDispatch: %v(%v)", cmd, intermeds)
+
+ switch cmd {
+ case "D": // IND
+ return ap.eventHandler.IND()
+ case "E": // NEL, equivalent to CRLF
+ err := ap.eventHandler.Execute(ANSI_CARRIAGE_RETURN)
+ if err == nil {
+ err = ap.eventHandler.Execute(ANSI_LINE_FEED)
+ }
+ return err
+ case "M": // RI
+ return ap.eventHandler.RI()
+ }
+
+ return nil
+}
+
+func (ap *AnsiParser) csiDispatch() error {
+ cmd, _ := parseCmd(*ap.context)
+ params, _ := parseParams(ap.context.paramBuffer)
+ ap.logf("Parsed params: %v with length: %d", params, len(params))
+
+ ap.logf("csiDispatch: %v(%v)", cmd, params)
+
+ switch cmd {
+ case "@":
+ return ap.eventHandler.ICH(getInt(params, 1))
+ case "A":
+ return ap.eventHandler.CUU(getInt(params, 1))
+ case "B":
+ return ap.eventHandler.CUD(getInt(params, 1))
+ case "C":
+ return ap.eventHandler.CUF(getInt(params, 1))
+ case "D":
+ return ap.eventHandler.CUB(getInt(params, 1))
+ case "E":
+ return ap.eventHandler.CNL(getInt(params, 1))
+ case "F":
+ return ap.eventHandler.CPL(getInt(params, 1))
+ case "G":
+ return ap.eventHandler.CHA(getInt(params, 1))
+ case "H":
+ ints := getInts(params, 2, 1)
+ x, y := ints[0], ints[1]
+ return ap.eventHandler.CUP(x, y)
+ case "J":
+ param := getEraseParam(params)
+ return ap.eventHandler.ED(param)
+ case "K":
+ param := getEraseParam(params)
+ return ap.eventHandler.EL(param)
+ case "L":
+ return ap.eventHandler.IL(getInt(params, 1))
+ case "M":
+ return ap.eventHandler.DL(getInt(params, 1))
+ case "P":
+ return ap.eventHandler.DCH(getInt(params, 1))
+ case "S":
+ return ap.eventHandler.SU(getInt(params, 1))
+ case "T":
+ return ap.eventHandler.SD(getInt(params, 1))
+ case "c":
+ return ap.eventHandler.DA(params)
+ case "d":
+ return ap.eventHandler.VPA(getInt(params, 1))
+ case "f":
+ ints := getInts(params, 2, 1)
+ x, y := ints[0], ints[1]
+ return ap.eventHandler.HVP(x, y)
+ case "h":
+ return ap.hDispatch(params)
+ case "l":
+ return ap.lDispatch(params)
+ case "m":
+ return ap.eventHandler.SGR(getInts(params, 1, 0))
+ case "r":
+ ints := getInts(params, 2, 1)
+ top, bottom := ints[0], ints[1]
+ return ap.eventHandler.DECSTBM(top, bottom)
+ default:
+ ap.logf("ERROR: Unsupported CSI command: '%s', with full context: %v", cmd, ap.context)
+ return nil
+ }
+
+}
+
+func (ap *AnsiParser) print() error {
+ return ap.eventHandler.Print(ap.context.currentChar)
+}
+
+func (ap *AnsiParser) clear() error {
+ ap.context = &ansiContext{}
+ return nil
+}
+
+func (ap *AnsiParser) execute() error {
+ return ap.eventHandler.Execute(ap.context.currentChar)
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/states.go b/vendor/github.com/Azure/go-ansiterm/states.go
new file mode 100644
index 00000000..f2ea1fcd
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/states.go
@@ -0,0 +1,71 @@
+package ansiterm
+
+type stateID int
+
+type state interface {
+ Enter() error
+ Exit() error
+ Handle(byte) (state, error)
+ Name() string
+ Transition(state) error
+}
+
+type baseState struct {
+ name string
+ parser *AnsiParser
+}
+
+func (base baseState) Enter() error {
+ return nil
+}
+
+func (base baseState) Exit() error {
+ return nil
+}
+
+func (base baseState) Handle(b byte) (s state, e error) {
+
+ switch {
+ case b == CSI_ENTRY:
+ return base.parser.csiEntry, nil
+ case b == DCS_ENTRY:
+ return base.parser.dcsEntry, nil
+ case b == ANSI_ESCAPE_PRIMARY:
+ return base.parser.escape, nil
+ case b == OSC_STRING:
+ return base.parser.oscString, nil
+ case sliceContains(toGroundBytes, b):
+ return base.parser.ground, nil
+ }
+
+ return nil, nil
+}
+
+func (base baseState) Name() string {
+ return base.name
+}
+
+func (base baseState) Transition(s state) error {
+ if s == base.parser.ground {
+ execBytes := []byte{0x18}
+ execBytes = append(execBytes, 0x1A)
+ execBytes = append(execBytes, getByteRange(0x80, 0x8F)...)
+ execBytes = append(execBytes, getByteRange(0x91, 0x97)...)
+ execBytes = append(execBytes, 0x99)
+ execBytes = append(execBytes, 0x9A)
+
+ if sliceContains(execBytes, base.parser.context.currentChar) {
+ return base.parser.execute()
+ }
+ }
+
+ return nil
+}
+
+type dcsEntryState struct {
+ baseState
+}
+
+type errorState struct {
+ baseState
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/utilities.go b/vendor/github.com/Azure/go-ansiterm/utilities.go
new file mode 100644
index 00000000..39211449
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/utilities.go
@@ -0,0 +1,21 @@
+package ansiterm
+
+import (
+ "strconv"
+)
+
+func sliceContains(bytes []byte, b byte) bool {
+ for _, v := range bytes {
+ if v == b {
+ return true
+ }
+ }
+
+ return false
+}
+
+func convertBytesToInteger(bytes []byte) int {
+ s := string(bytes)
+ i, _ := strconv.Atoi(s)
+ return i
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go b/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go
new file mode 100644
index 00000000..5599082a
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go
@@ -0,0 +1,196 @@
+// +build windows
+
+package winterm
+
+import (
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+ "syscall"
+
+ "github.com/Azure/go-ansiterm"
+ windows "golang.org/x/sys/windows"
+)
+
+// Windows keyboard constants
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx.
+const (
+ VK_PRIOR = 0x21 // PAGE UP key
+ VK_NEXT = 0x22 // PAGE DOWN key
+ VK_END = 0x23 // END key
+ VK_HOME = 0x24 // HOME key
+ VK_LEFT = 0x25 // LEFT ARROW key
+ VK_UP = 0x26 // UP ARROW key
+ VK_RIGHT = 0x27 // RIGHT ARROW key
+ VK_DOWN = 0x28 // DOWN ARROW key
+ VK_SELECT = 0x29 // SELECT key
+ VK_PRINT = 0x2A // PRINT key
+ VK_EXECUTE = 0x2B // EXECUTE key
+ VK_SNAPSHOT = 0x2C // PRINT SCREEN key
+ VK_INSERT = 0x2D // INS key
+ VK_DELETE = 0x2E // DEL key
+ VK_HELP = 0x2F // HELP key
+ VK_F1 = 0x70 // F1 key
+ VK_F2 = 0x71 // F2 key
+ VK_F3 = 0x72 // F3 key
+ VK_F4 = 0x73 // F4 key
+ VK_F5 = 0x74 // F5 key
+ VK_F6 = 0x75 // F6 key
+ VK_F7 = 0x76 // F7 key
+ VK_F8 = 0x77 // F8 key
+ VK_F9 = 0x78 // F9 key
+ VK_F10 = 0x79 // F10 key
+ VK_F11 = 0x7A // F11 key
+ VK_F12 = 0x7B // F12 key
+
+ RIGHT_ALT_PRESSED = 0x0001
+ LEFT_ALT_PRESSED = 0x0002
+ RIGHT_CTRL_PRESSED = 0x0004
+ LEFT_CTRL_PRESSED = 0x0008
+ SHIFT_PRESSED = 0x0010
+ NUMLOCK_ON = 0x0020
+ SCROLLLOCK_ON = 0x0040
+ CAPSLOCK_ON = 0x0080
+ ENHANCED_KEY = 0x0100
+)
+
+type ansiCommand struct {
+ CommandBytes []byte
+ Command string
+ Parameters []string
+ IsSpecial bool
+}
+
+func newAnsiCommand(command []byte) *ansiCommand {
+
+ if isCharacterSelectionCmdChar(command[1]) {
+ // Is Character Set Selection commands
+ return &ansiCommand{
+ CommandBytes: command,
+ Command: string(command),
+ IsSpecial: true,
+ }
+ }
+
+ // last char is command character
+ lastCharIndex := len(command) - 1
+
+ ac := &ansiCommand{
+ CommandBytes: command,
+ Command: string(command[lastCharIndex]),
+ IsSpecial: false,
+ }
+
+ // more than a single escape
+ if lastCharIndex != 0 {
+ start := 1
+ // skip if double char escape sequence
+ if command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_ESCAPE_SECONDARY {
+ start++
+ }
+ // convert this to GetNextParam method
+ ac.Parameters = strings.Split(string(command[start:lastCharIndex]), ansiterm.ANSI_PARAMETER_SEP)
+ }
+
+ return ac
+}
+
+func (ac *ansiCommand) paramAsSHORT(index int, defaultValue int16) int16 {
+ if index < 0 || index >= len(ac.Parameters) {
+ return defaultValue
+ }
+
+ param, err := strconv.ParseInt(ac.Parameters[index], 10, 16)
+ if err != nil {
+ return defaultValue
+ }
+
+ return int16(param)
+}
+
+func (ac *ansiCommand) String() string {
+ return fmt.Sprintf("0x%v \"%v\" (\"%v\")",
+ bytesToHex(ac.CommandBytes),
+ ac.Command,
+ strings.Join(ac.Parameters, "\",\""))
+}
+
+// isAnsiCommandChar returns true if the passed byte falls within the range of ANSI commands.
+// See http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html.
+func isAnsiCommandChar(b byte) bool {
+ switch {
+ case ansiterm.ANSI_COMMAND_FIRST <= b && b <= ansiterm.ANSI_COMMAND_LAST && b != ansiterm.ANSI_ESCAPE_SECONDARY:
+ return true
+ case b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_OSC || b == ansiterm.ANSI_CMD_DECPAM || b == ansiterm.ANSI_CMD_DECPNM:
+ // non-CSI escape sequence terminator
+ return true
+ case b == ansiterm.ANSI_CMD_STR_TERM || b == ansiterm.ANSI_BEL:
+ // String escape sequence terminator
+ return true
+ }
+ return false
+}
+
+func isXtermOscSequence(command []byte, current byte) bool {
+ return (len(command) >= 2 && command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_CMD_OSC && current != ansiterm.ANSI_BEL)
+}
+
+func isCharacterSelectionCmdChar(b byte) bool {
+ return (b == ansiterm.ANSI_CMD_G0 || b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_G2 || b == ansiterm.ANSI_CMD_G3)
+}
+
+// bytesToHex converts a slice of bytes to a human-readable string.
+func bytesToHex(b []byte) string {
+ hex := make([]string, len(b))
+ for i, ch := range b {
+ hex[i] = fmt.Sprintf("%X", ch)
+ }
+ return strings.Join(hex, "")
+}
+
+// ensureInRange adjusts the passed value, if necessary, to ensure it is within
+// the passed min / max range.
+func ensureInRange(n int16, min int16, max int16) int16 {
+ if n < min {
+ return min
+ } else if n > max {
+ return max
+ } else {
+ return n
+ }
+}
+
+func GetStdFile(nFile int) (*os.File, uintptr) {
+ var file *os.File
+
+ // syscall uses negative numbers
+ // windows package uses very big uint32
+ // Keep these switches split so we don't have to convert ints too much.
+ switch uint32(nFile) {
+ case windows.STD_INPUT_HANDLE:
+ file = os.Stdin
+ case windows.STD_OUTPUT_HANDLE:
+ file = os.Stdout
+ case windows.STD_ERROR_HANDLE:
+ file = os.Stderr
+ default:
+ switch nFile {
+ case syscall.STD_INPUT_HANDLE:
+ file = os.Stdin
+ case syscall.STD_OUTPUT_HANDLE:
+ file = os.Stdout
+ case syscall.STD_ERROR_HANDLE:
+ file = os.Stderr
+ default:
+ panic(fmt.Errorf("Invalid standard handle identifier: %v", nFile))
+ }
+ }
+
+ fd, err := syscall.GetStdHandle(nFile)
+ if err != nil {
+ panic(fmt.Errorf("Invalid standard handle identifier: %v -- %v", nFile, err))
+ }
+
+ return file, uintptr(fd)
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/api.go b/vendor/github.com/Azure/go-ansiterm/winterm/api.go
new file mode 100644
index 00000000..6055e33b
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/winterm/api.go
@@ -0,0 +1,327 @@
+// +build windows
+
+package winterm
+
+import (
+ "fmt"
+ "syscall"
+ "unsafe"
+)
+
+//===========================================================================================================
+// IMPORTANT NOTE:
+//
+// The methods below make extensive use of the "unsafe" package to obtain the required pointers.
+// Beginning in Go 1.3, the garbage collector may release local variables (e.g., incoming arguments, stack
+// variables) the pointers reference *before* the API completes.
+//
+// As a result, in those cases, the code must hint that the variables remain in active by invoking the
+// dummy method "use" (see below). Newer versions of Go are planned to change the mechanism to no longer
+// require unsafe pointers.
+//
+// If you add or modify methods, ENSURE protection of local variables through the "use" builtin to inform
+// the garbage collector the variables remain in use if:
+//
+// -- The value is not a pointer (e.g., int32, struct)
+// -- The value is not referenced by the method after passing the pointer to Windows
+//
+// See http://golang.org/doc/go1.3.
+//===========================================================================================================
+
+var (
+ kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
+
+ getConsoleCursorInfoProc = kernel32DLL.NewProc("GetConsoleCursorInfo")
+ setConsoleCursorInfoProc = kernel32DLL.NewProc("SetConsoleCursorInfo")
+ setConsoleCursorPositionProc = kernel32DLL.NewProc("SetConsoleCursorPosition")
+ setConsoleModeProc = kernel32DLL.NewProc("SetConsoleMode")
+ getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
+ setConsoleScreenBufferSizeProc = kernel32DLL.NewProc("SetConsoleScreenBufferSize")
+ scrollConsoleScreenBufferProc = kernel32DLL.NewProc("ScrollConsoleScreenBufferA")
+ setConsoleTextAttributeProc = kernel32DLL.NewProc("SetConsoleTextAttribute")
+ setConsoleWindowInfoProc = kernel32DLL.NewProc("SetConsoleWindowInfo")
+ writeConsoleOutputProc = kernel32DLL.NewProc("WriteConsoleOutputW")
+ readConsoleInputProc = kernel32DLL.NewProc("ReadConsoleInputW")
+ waitForSingleObjectProc = kernel32DLL.NewProc("WaitForSingleObject")
+)
+
+// Windows Console constants
+const (
+ // Console modes
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
+ ENABLE_PROCESSED_INPUT = 0x0001
+ ENABLE_LINE_INPUT = 0x0002
+ ENABLE_ECHO_INPUT = 0x0004
+ ENABLE_WINDOW_INPUT = 0x0008
+ ENABLE_MOUSE_INPUT = 0x0010
+ ENABLE_INSERT_MODE = 0x0020
+ ENABLE_QUICK_EDIT_MODE = 0x0040
+ ENABLE_EXTENDED_FLAGS = 0x0080
+ ENABLE_AUTO_POSITION = 0x0100
+ ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200
+
+ ENABLE_PROCESSED_OUTPUT = 0x0001
+ ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
+ ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
+ DISABLE_NEWLINE_AUTO_RETURN = 0x0008
+ ENABLE_LVB_GRID_WORLDWIDE = 0x0010
+
+ // Character attributes
+ // Note:
+ // -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan).
+ // Clearing all foreground or background colors results in black; setting all creates white.
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes.
+ FOREGROUND_BLUE uint16 = 0x0001
+ FOREGROUND_GREEN uint16 = 0x0002
+ FOREGROUND_RED uint16 = 0x0004
+ FOREGROUND_INTENSITY uint16 = 0x0008
+ FOREGROUND_MASK uint16 = 0x000F
+
+ BACKGROUND_BLUE uint16 = 0x0010
+ BACKGROUND_GREEN uint16 = 0x0020
+ BACKGROUND_RED uint16 = 0x0040
+ BACKGROUND_INTENSITY uint16 = 0x0080
+ BACKGROUND_MASK uint16 = 0x00F0
+
+ COMMON_LVB_MASK uint16 = 0xFF00
+ COMMON_LVB_REVERSE_VIDEO uint16 = 0x4000
+ COMMON_LVB_UNDERSCORE uint16 = 0x8000
+
+ // Input event types
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
+ KEY_EVENT = 0x0001
+ MOUSE_EVENT = 0x0002
+ WINDOW_BUFFER_SIZE_EVENT = 0x0004
+ MENU_EVENT = 0x0008
+ FOCUS_EVENT = 0x0010
+
+ // WaitForSingleObject return codes
+ WAIT_ABANDONED = 0x00000080
+ WAIT_FAILED = 0xFFFFFFFF
+ WAIT_SIGNALED = 0x0000000
+ WAIT_TIMEOUT = 0x00000102
+
+ // WaitForSingleObject wait duration
+ WAIT_INFINITE = 0xFFFFFFFF
+ WAIT_ONE_SECOND = 1000
+ WAIT_HALF_SECOND = 500
+ WAIT_QUARTER_SECOND = 250
+)
+
+// Windows API Console types
+// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682101(v=vs.85).aspx for Console specific types (e.g., COORD)
+// -- See https://msdn.microsoft.com/en-us/library/aa296569(v=vs.60).aspx for comments on alignment
+type (
+ CHAR_INFO struct {
+ UnicodeChar uint16
+ Attributes uint16
+ }
+
+ CONSOLE_CURSOR_INFO struct {
+ Size uint32
+ Visible int32
+ }
+
+ CONSOLE_SCREEN_BUFFER_INFO struct {
+ Size COORD
+ CursorPosition COORD
+ Attributes uint16
+ Window SMALL_RECT
+ MaximumWindowSize COORD
+ }
+
+ COORD struct {
+ X int16
+ Y int16
+ }
+
+ SMALL_RECT struct {
+ Left int16
+ Top int16
+ Right int16
+ Bottom int16
+ }
+
+ // INPUT_RECORD is a C/C++ union of which KEY_EVENT_RECORD is one case, it is also the largest
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
+ INPUT_RECORD struct {
+ EventType uint16
+ KeyEvent KEY_EVENT_RECORD
+ }
+
+ KEY_EVENT_RECORD struct {
+ KeyDown int32
+ RepeatCount uint16
+ VirtualKeyCode uint16
+ VirtualScanCode uint16
+ UnicodeChar uint16
+ ControlKeyState uint32
+ }
+
+ WINDOW_BUFFER_SIZE struct {
+ Size COORD
+ }
+)
+
+// boolToBOOL converts a Go bool into a Windows int32.
+func boolToBOOL(f bool) int32 {
+ if f {
+ return int32(1)
+ } else {
+ return int32(0)
+ }
+}
+
+// GetConsoleCursorInfo retrieves information about the size and visiblity of the console cursor.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683163(v=vs.85).aspx.
+func GetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error {
+ r1, r2, err := getConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)
+ return checkError(r1, r2, err)
+}
+
+// SetConsoleCursorInfo sets the size and visiblity of the console cursor.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686019(v=vs.85).aspx.
+func SetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error {
+ r1, r2, err := setConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)
+ return checkError(r1, r2, err)
+}
+
+// SetConsoleCursorPosition location of the console cursor.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx.
+func SetConsoleCursorPosition(handle uintptr, coord COORD) error {
+ r1, r2, err := setConsoleCursorPositionProc.Call(handle, coordToPointer(coord))
+ use(coord)
+ return checkError(r1, r2, err)
+}
+
+// GetConsoleMode gets the console mode for given file descriptor
+// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx.
+func GetConsoleMode(handle uintptr) (mode uint32, err error) {
+ err = syscall.GetConsoleMode(syscall.Handle(handle), &mode)
+ return mode, err
+}
+
+// SetConsoleMode sets the console mode for given file descriptor
+// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
+func SetConsoleMode(handle uintptr, mode uint32) error {
+ r1, r2, err := setConsoleModeProc.Call(handle, uintptr(mode), 0)
+ use(mode)
+ return checkError(r1, r2, err)
+}
+
+// GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer.
+// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx.
+func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
+ info := CONSOLE_SCREEN_BUFFER_INFO{}
+ err := checkError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0))
+ if err != nil {
+ return nil, err
+ }
+ return &info, nil
+}
+
+func ScrollConsoleScreenBuffer(handle uintptr, scrollRect SMALL_RECT, clipRect SMALL_RECT, destOrigin COORD, char CHAR_INFO) error {
+ r1, r2, err := scrollConsoleScreenBufferProc.Call(handle, uintptr(unsafe.Pointer(&scrollRect)), uintptr(unsafe.Pointer(&clipRect)), coordToPointer(destOrigin), uintptr(unsafe.Pointer(&char)))
+ use(scrollRect)
+ use(clipRect)
+ use(destOrigin)
+ use(char)
+ return checkError(r1, r2, err)
+}
+
+// SetConsoleScreenBufferSize sets the size of the console screen buffer.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686044(v=vs.85).aspx.
+func SetConsoleScreenBufferSize(handle uintptr, coord COORD) error {
+ r1, r2, err := setConsoleScreenBufferSizeProc.Call(handle, coordToPointer(coord))
+ use(coord)
+ return checkError(r1, r2, err)
+}
+
+// SetConsoleTextAttribute sets the attributes of characters written to the
+// console screen buffer by the WriteFile or WriteConsole function.
+// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx.
+func SetConsoleTextAttribute(handle uintptr, attribute uint16) error {
+ r1, r2, err := setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0)
+ use(attribute)
+ return checkError(r1, r2, err)
+}
+
+// SetConsoleWindowInfo sets the size and position of the console screen buffer's window.
+// Note that the size and location must be within and no larger than the backing console screen buffer.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85).aspx.
+func SetConsoleWindowInfo(handle uintptr, isAbsolute bool, rect SMALL_RECT) error {
+ r1, r2, err := setConsoleWindowInfoProc.Call(handle, uintptr(boolToBOOL(isAbsolute)), uintptr(unsafe.Pointer(&rect)))
+ use(isAbsolute)
+ use(rect)
+ return checkError(r1, r2, err)
+}
+
+// WriteConsoleOutput writes the CHAR_INFOs from the provided buffer to the active console buffer.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687404(v=vs.85).aspx.
+func WriteConsoleOutput(handle uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) error {
+ r1, r2, err := writeConsoleOutputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), coordToPointer(bufferSize), coordToPointer(bufferCoord), uintptr(unsafe.Pointer(writeRegion)))
+ use(buffer)
+ use(bufferSize)
+ use(bufferCoord)
+ return checkError(r1, r2, err)
+}
+
+// ReadConsoleInput reads (and removes) data from the console input buffer.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx.
+func ReadConsoleInput(handle uintptr, buffer []INPUT_RECORD, count *uint32) error {
+ r1, r2, err := readConsoleInputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(count)))
+ use(buffer)
+ return checkError(r1, r2, err)
+}
+
+// WaitForSingleObject waits for the passed handle to be signaled.
+// It returns true if the handle was signaled; false otherwise.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx.
+func WaitForSingleObject(handle uintptr, msWait uint32) (bool, error) {
+ r1, _, err := waitForSingleObjectProc.Call(handle, uintptr(uint32(msWait)))
+ switch r1 {
+ case WAIT_ABANDONED, WAIT_TIMEOUT:
+ return false, nil
+ case WAIT_SIGNALED:
+ return true, nil
+ }
+ use(msWait)
+ return false, err
+}
+
+// String helpers
+func (info CONSOLE_SCREEN_BUFFER_INFO) String() string {
+ return fmt.Sprintf("Size(%v) Cursor(%v) Window(%v) Max(%v)", info.Size, info.CursorPosition, info.Window, info.MaximumWindowSize)
+}
+
+func (coord COORD) String() string {
+ return fmt.Sprintf("%v,%v", coord.X, coord.Y)
+}
+
+func (rect SMALL_RECT) String() string {
+ return fmt.Sprintf("(%v,%v),(%v,%v)", rect.Left, rect.Top, rect.Right, rect.Bottom)
+}
+
+// checkError evaluates the results of a Windows API call and returns the error if it failed.
+func checkError(r1, r2 uintptr, err error) error {
+ // Windows APIs return non-zero to indicate success
+ if r1 != 0 {
+ return nil
+ }
+
+ // Return the error if provided, otherwise default to EINVAL
+ if err != nil {
+ return err
+ }
+ return syscall.EINVAL
+}
+
+// coordToPointer converts a COORD into a uintptr (by fooling the type system).
+func coordToPointer(c COORD) uintptr {
+ // Note: This code assumes the two SHORTs are correctly laid out; the "cast" to uint32 is just to get a pointer to pass.
+ return uintptr(*((*uint32)(unsafe.Pointer(&c))))
+}
+
+// use is a no-op, but the compiler cannot see that it is.
+// Calling use(p) ensures that p is kept live until that point.
+func use(p interface{}) {}
diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go b/vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go
new file mode 100644
index 00000000..cbec8f72
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go
@@ -0,0 +1,100 @@
+// +build windows
+
+package winterm
+
+import "github.com/Azure/go-ansiterm"
+
+const (
+ FOREGROUND_COLOR_MASK = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
+ BACKGROUND_COLOR_MASK = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
+)
+
+// collectAnsiIntoWindowsAttributes modifies the passed Windows text mode flags to reflect the
+// request represented by the passed ANSI mode.
+func collectAnsiIntoWindowsAttributes(windowsMode uint16, inverted bool, baseMode uint16, ansiMode int16) (uint16, bool) {
+ switch ansiMode {
+
+ // Mode styles
+ case ansiterm.ANSI_SGR_BOLD:
+ windowsMode = windowsMode | FOREGROUND_INTENSITY
+
+ case ansiterm.ANSI_SGR_DIM, ansiterm.ANSI_SGR_BOLD_DIM_OFF:
+ windowsMode &^= FOREGROUND_INTENSITY
+
+ case ansiterm.ANSI_SGR_UNDERLINE:
+ windowsMode = windowsMode | COMMON_LVB_UNDERSCORE
+
+ case ansiterm.ANSI_SGR_REVERSE:
+ inverted = true
+
+ case ansiterm.ANSI_SGR_REVERSE_OFF:
+ inverted = false
+
+ case ansiterm.ANSI_SGR_UNDERLINE_OFF:
+ windowsMode &^= COMMON_LVB_UNDERSCORE
+
+ // Foreground colors
+ case ansiterm.ANSI_SGR_FOREGROUND_DEFAULT:
+ windowsMode = (windowsMode &^ FOREGROUND_MASK) | (baseMode & FOREGROUND_MASK)
+
+ case ansiterm.ANSI_SGR_FOREGROUND_BLACK:
+ windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK)
+
+ case ansiterm.ANSI_SGR_FOREGROUND_RED:
+ windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED
+
+ case ansiterm.ANSI_SGR_FOREGROUND_GREEN:
+ windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN
+
+ case ansiterm.ANSI_SGR_FOREGROUND_YELLOW:
+ windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN
+
+ case ansiterm.ANSI_SGR_FOREGROUND_BLUE:
+ windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_BLUE
+
+ case ansiterm.ANSI_SGR_FOREGROUND_MAGENTA:
+ windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_BLUE
+
+ case ansiterm.ANSI_SGR_FOREGROUND_CYAN:
+ windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN | FOREGROUND_BLUE
+
+ case ansiterm.ANSI_SGR_FOREGROUND_WHITE:
+ windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
+
+ // Background colors
+ case ansiterm.ANSI_SGR_BACKGROUND_DEFAULT:
+ // Black with no intensity
+ windowsMode = (windowsMode &^ BACKGROUND_MASK) | (baseMode & BACKGROUND_MASK)
+
+ case ansiterm.ANSI_SGR_BACKGROUND_BLACK:
+ windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK)
+
+ case ansiterm.ANSI_SGR_BACKGROUND_RED:
+ windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED
+
+ case ansiterm.ANSI_SGR_BACKGROUND_GREEN:
+ windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN
+
+ case ansiterm.ANSI_SGR_BACKGROUND_YELLOW:
+ windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN
+
+ case ansiterm.ANSI_SGR_BACKGROUND_BLUE:
+ windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_BLUE
+
+ case ansiterm.ANSI_SGR_BACKGROUND_MAGENTA:
+ windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_BLUE
+
+ case ansiterm.ANSI_SGR_BACKGROUND_CYAN:
+ windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN | BACKGROUND_BLUE
+
+ case ansiterm.ANSI_SGR_BACKGROUND_WHITE:
+ windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
+ }
+
+ return windowsMode, inverted
+}
+
+// invertAttributes inverts the foreground and background colors of a Windows attributes value
+func invertAttributes(windowsMode uint16) uint16 {
+ return (COMMON_LVB_MASK & windowsMode) | ((FOREGROUND_MASK & windowsMode) << 4) | ((BACKGROUND_MASK & windowsMode) >> 4)
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go b/vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go
new file mode 100644
index 00000000..3ee06ea7
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go
@@ -0,0 +1,101 @@
+// +build windows
+
+package winterm
+
+const (
+ horizontal = iota
+ vertical
+)
+
+func (h *windowsAnsiEventHandler) getCursorWindow(info *CONSOLE_SCREEN_BUFFER_INFO) SMALL_RECT {
+ if h.originMode {
+ sr := h.effectiveSr(info.Window)
+ return SMALL_RECT{
+ Top: sr.top,
+ Bottom: sr.bottom,
+ Left: 0,
+ Right: info.Size.X - 1,
+ }
+ } else {
+ return SMALL_RECT{
+ Top: info.Window.Top,
+ Bottom: info.Window.Bottom,
+ Left: 0,
+ Right: info.Size.X - 1,
+ }
+ }
+}
+
+// setCursorPosition sets the cursor to the specified position, bounded to the screen size
+func (h *windowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL_RECT) error {
+ position.X = ensureInRange(position.X, window.Left, window.Right)
+ position.Y = ensureInRange(position.Y, window.Top, window.Bottom)
+ err := SetConsoleCursorPosition(h.fd, position)
+ if err != nil {
+ return err
+ }
+ h.logf("Cursor position set: (%d, %d)", position.X, position.Y)
+ return err
+}
+
+func (h *windowsAnsiEventHandler) moveCursorVertical(param int) error {
+ return h.moveCursor(vertical, param)
+}
+
+func (h *windowsAnsiEventHandler) moveCursorHorizontal(param int) error {
+ return h.moveCursor(horizontal, param)
+}
+
+func (h *windowsAnsiEventHandler) moveCursor(moveMode int, param int) error {
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+
+ position := info.CursorPosition
+ switch moveMode {
+ case horizontal:
+ position.X += int16(param)
+ case vertical:
+ position.Y += int16(param)
+ }
+
+ if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (h *windowsAnsiEventHandler) moveCursorLine(param int) error {
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+
+ position := info.CursorPosition
+ position.X = 0
+ position.Y += int16(param)
+
+ if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (h *windowsAnsiEventHandler) moveCursorColumn(param int) error {
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+
+ position := info.CursorPosition
+ position.X = int16(param) - 1
+
+ if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go b/vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go
new file mode 100644
index 00000000..244b5fa2
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go
@@ -0,0 +1,84 @@
+// +build windows
+
+package winterm
+
+import "github.com/Azure/go-ansiterm"
+
+func (h *windowsAnsiEventHandler) clearRange(attributes uint16, fromCoord COORD, toCoord COORD) error {
+ // Ignore an invalid (negative area) request
+ if toCoord.Y < fromCoord.Y {
+ return nil
+ }
+
+ var err error
+
+ var coordStart = COORD{}
+ var coordEnd = COORD{}
+
+ xCurrent, yCurrent := fromCoord.X, fromCoord.Y
+ xEnd, yEnd := toCoord.X, toCoord.Y
+
+ // Clear any partial initial line
+ if xCurrent > 0 {
+ coordStart.X, coordStart.Y = xCurrent, yCurrent
+ coordEnd.X, coordEnd.Y = xEnd, yCurrent
+
+ err = h.clearRect(attributes, coordStart, coordEnd)
+ if err != nil {
+ return err
+ }
+
+ xCurrent = 0
+ yCurrent += 1
+ }
+
+ // Clear intervening rectangular section
+ if yCurrent < yEnd {
+ coordStart.X, coordStart.Y = xCurrent, yCurrent
+ coordEnd.X, coordEnd.Y = xEnd, yEnd-1
+
+ err = h.clearRect(attributes, coordStart, coordEnd)
+ if err != nil {
+ return err
+ }
+
+ xCurrent = 0
+ yCurrent = yEnd
+ }
+
+ // Clear remaining partial ending line
+ coordStart.X, coordStart.Y = xCurrent, yCurrent
+ coordEnd.X, coordEnd.Y = xEnd, yEnd
+
+ err = h.clearRect(attributes, coordStart, coordEnd)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (h *windowsAnsiEventHandler) clearRect(attributes uint16, fromCoord COORD, toCoord COORD) error {
+ region := SMALL_RECT{Top: fromCoord.Y, Left: fromCoord.X, Bottom: toCoord.Y, Right: toCoord.X}
+ width := toCoord.X - fromCoord.X + 1
+ height := toCoord.Y - fromCoord.Y + 1
+ size := uint32(width) * uint32(height)
+
+ if size <= 0 {
+ return nil
+ }
+
+ buffer := make([]CHAR_INFO, size)
+
+ char := CHAR_INFO{ansiterm.FILL_CHARACTER, attributes}
+ for i := 0; i < int(size); i++ {
+ buffer[i] = char
+ }
+
+ err := WriteConsoleOutput(h.fd, buffer, COORD{X: width, Y: height}, COORD{X: 0, Y: 0}, ®ion)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go b/vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go
new file mode 100644
index 00000000..2d27fa1d
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go
@@ -0,0 +1,118 @@
+// +build windows
+
+package winterm
+
+// effectiveSr gets the current effective scroll region in buffer coordinates
+func (h *windowsAnsiEventHandler) effectiveSr(window SMALL_RECT) scrollRegion {
+ top := addInRange(window.Top, h.sr.top, window.Top, window.Bottom)
+ bottom := addInRange(window.Top, h.sr.bottom, window.Top, window.Bottom)
+ if top >= bottom {
+ top = window.Top
+ bottom = window.Bottom
+ }
+ return scrollRegion{top: top, bottom: bottom}
+}
+
+func (h *windowsAnsiEventHandler) scrollUp(param int) error {
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+
+ sr := h.effectiveSr(info.Window)
+ return h.scroll(param, sr, info)
+}
+
+func (h *windowsAnsiEventHandler) scrollDown(param int) error {
+ return h.scrollUp(-param)
+}
+
+func (h *windowsAnsiEventHandler) deleteLines(param int) error {
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+
+ start := info.CursorPosition.Y
+ sr := h.effectiveSr(info.Window)
+ // Lines cannot be inserted or deleted outside the scrolling region.
+ if start >= sr.top && start <= sr.bottom {
+ sr.top = start
+ return h.scroll(param, sr, info)
+ } else {
+ return nil
+ }
+}
+
+func (h *windowsAnsiEventHandler) insertLines(param int) error {
+ return h.deleteLines(-param)
+}
+
+// scroll scrolls the provided scroll region by param lines. The scroll region is in buffer coordinates.
+func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSOLE_SCREEN_BUFFER_INFO) error {
+ h.logf("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom)
+ h.logf("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom)
+
+ // Copy from and clip to the scroll region (full buffer width)
+ scrollRect := SMALL_RECT{
+ Top: sr.top,
+ Bottom: sr.bottom,
+ Left: 0,
+ Right: info.Size.X - 1,
+ }
+
+ // Origin to which area should be copied
+ destOrigin := COORD{
+ X: 0,
+ Y: sr.top - int16(param),
+ }
+
+ char := CHAR_INFO{
+ UnicodeChar: ' ',
+ Attributes: h.attributes,
+ }
+
+ if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (h *windowsAnsiEventHandler) deleteCharacters(param int) error {
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+ return h.scrollLine(param, info.CursorPosition, info)
+}
+
+func (h *windowsAnsiEventHandler) insertCharacters(param int) error {
+ return h.deleteCharacters(-param)
+}
+
+// scrollLine scrolls a line horizontally starting at the provided position by a number of columns.
+func (h *windowsAnsiEventHandler) scrollLine(columns int, position COORD, info *CONSOLE_SCREEN_BUFFER_INFO) error {
+ // Copy from and clip to the scroll region (full buffer width)
+ scrollRect := SMALL_RECT{
+ Top: position.Y,
+ Bottom: position.Y,
+ Left: position.X,
+ Right: info.Size.X - 1,
+ }
+
+ // Origin to which area should be copied
+ destOrigin := COORD{
+ X: position.X - int16(columns),
+ Y: position.Y,
+ }
+
+ char := CHAR_INFO{
+ UnicodeChar: ' ',
+ Attributes: h.attributes,
+ }
+
+ if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/utilities.go b/vendor/github.com/Azure/go-ansiterm/winterm/utilities.go
new file mode 100644
index 00000000..afa7635d
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/winterm/utilities.go
@@ -0,0 +1,9 @@
+// +build windows
+
+package winterm
+
+// AddInRange increments a value by the passed quantity while ensuring the values
+// always remain within the supplied min / max range.
+func addInRange(n int16, increment int16, min int16, max int16) int16 {
+ return ensureInRange(n+increment, min, max)
+}
diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go b/vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go
new file mode 100644
index 00000000..2d40fb75
--- /dev/null
+++ b/vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go
@@ -0,0 +1,743 @@
+// +build windows
+
+package winterm
+
+import (
+ "bytes"
+ "log"
+ "os"
+ "strconv"
+
+ "github.com/Azure/go-ansiterm"
+)
+
+type windowsAnsiEventHandler struct {
+ fd uintptr
+ file *os.File
+ infoReset *CONSOLE_SCREEN_BUFFER_INFO
+ sr scrollRegion
+ buffer bytes.Buffer
+ attributes uint16
+ inverted bool
+ wrapNext bool
+ drewMarginByte bool
+ originMode bool
+ marginByte byte
+ curInfo *CONSOLE_SCREEN_BUFFER_INFO
+ curPos COORD
+ logf func(string, ...interface{})
+}
+
+type Option func(*windowsAnsiEventHandler)
+
+func WithLogf(f func(string, ...interface{})) Option {
+ return func(w *windowsAnsiEventHandler) {
+ w.logf = f
+ }
+}
+
+func CreateWinEventHandler(fd uintptr, file *os.File, opts ...Option) ansiterm.AnsiEventHandler {
+ infoReset, err := GetConsoleScreenBufferInfo(fd)
+ if err != nil {
+ return nil
+ }
+
+ h := &windowsAnsiEventHandler{
+ fd: fd,
+ file: file,
+ infoReset: infoReset,
+ attributes: infoReset.Attributes,
+ }
+ for _, o := range opts {
+ o(h)
+ }
+
+ if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
+ logFile, _ := os.Create("winEventHandler.log")
+ logger := log.New(logFile, "", log.LstdFlags)
+ if h.logf != nil {
+ l := h.logf
+ h.logf = func(s string, v ...interface{}) {
+ l(s, v...)
+ logger.Printf(s, v...)
+ }
+ } else {
+ h.logf = logger.Printf
+ }
+ }
+
+ if h.logf == nil {
+ h.logf = func(string, ...interface{}) {}
+ }
+
+ return h
+}
+
+type scrollRegion struct {
+ top int16
+ bottom int16
+}
+
+// simulateLF simulates a LF or CR+LF by scrolling if necessary to handle the
+// current cursor position and scroll region settings, in which case it returns
+// true. If no special handling is necessary, then it does nothing and returns
+// false.
+//
+// In the false case, the caller should ensure that a carriage return
+// and line feed are inserted or that the text is otherwise wrapped.
+func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
+ if h.wrapNext {
+ if err := h.Flush(); err != nil {
+ return false, err
+ }
+ h.clearWrap()
+ }
+ pos, info, err := h.getCurrentInfo()
+ if err != nil {
+ return false, err
+ }
+ sr := h.effectiveSr(info.Window)
+ if pos.Y == sr.bottom {
+ // Scrolling is necessary. Let Windows automatically scroll if the scrolling region
+ // is the full window.
+ if sr.top == info.Window.Top && sr.bottom == info.Window.Bottom {
+ if includeCR {
+ pos.X = 0
+ h.updatePos(pos)
+ }
+ return false, nil
+ }
+
+ // A custom scroll region is active. Scroll the window manually to simulate
+ // the LF.
+ if err := h.Flush(); err != nil {
+ return false, err
+ }
+ h.logf("Simulating LF inside scroll region")
+ if err := h.scrollUp(1); err != nil {
+ return false, err
+ }
+ if includeCR {
+ pos.X = 0
+ if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
+ return false, err
+ }
+ }
+ return true, nil
+
+ } else if pos.Y < info.Window.Bottom {
+ // Let Windows handle the LF.
+ pos.Y++
+ if includeCR {
+ pos.X = 0
+ }
+ h.updatePos(pos)
+ return false, nil
+ } else {
+ // The cursor is at the bottom of the screen but outside the scroll
+ // region. Skip the LF.
+ h.logf("Simulating LF outside scroll region")
+ if includeCR {
+ if err := h.Flush(); err != nil {
+ return false, err
+ }
+ pos.X = 0
+ if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
+ return false, err
+ }
+ }
+ return true, nil
+ }
+}
+
+// executeLF executes a LF without a CR.
+func (h *windowsAnsiEventHandler) executeLF() error {
+ handled, err := h.simulateLF(false)
+ if err != nil {
+ return err
+ }
+ if !handled {
+ // Windows LF will reset the cursor column position. Write the LF
+ // and restore the cursor position.
+ pos, _, err := h.getCurrentInfo()
+ if err != nil {
+ return err
+ }
+ h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED)
+ if pos.X != 0 {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("Resetting cursor position for LF without CR")
+ if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func (h *windowsAnsiEventHandler) Print(b byte) error {
+ if h.wrapNext {
+ h.buffer.WriteByte(h.marginByte)
+ h.clearWrap()
+ if _, err := h.simulateLF(true); err != nil {
+ return err
+ }
+ }
+ pos, info, err := h.getCurrentInfo()
+ if err != nil {
+ return err
+ }
+ if pos.X == info.Size.X-1 {
+ h.wrapNext = true
+ h.marginByte = b
+ } else {
+ pos.X++
+ h.updatePos(pos)
+ h.buffer.WriteByte(b)
+ }
+ return nil
+}
+
+func (h *windowsAnsiEventHandler) Execute(b byte) error {
+ switch b {
+ case ansiterm.ANSI_TAB:
+ h.logf("Execute(TAB)")
+ // Move to the next tab stop, but preserve auto-wrap if already set.
+ if !h.wrapNext {
+ pos, info, err := h.getCurrentInfo()
+ if err != nil {
+ return err
+ }
+ pos.X = (pos.X + 8) - pos.X%8
+ if pos.X >= info.Size.X {
+ pos.X = info.Size.X - 1
+ }
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
+ return err
+ }
+ }
+ return nil
+
+ case ansiterm.ANSI_BEL:
+ h.buffer.WriteByte(ansiterm.ANSI_BEL)
+ return nil
+
+ case ansiterm.ANSI_BACKSPACE:
+ if h.wrapNext {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.clearWrap()
+ }
+ pos, _, err := h.getCurrentInfo()
+ if err != nil {
+ return err
+ }
+ if pos.X > 0 {
+ pos.X--
+ h.updatePos(pos)
+ h.buffer.WriteByte(ansiterm.ANSI_BACKSPACE)
+ }
+ return nil
+
+ case ansiterm.ANSI_VERTICAL_TAB, ansiterm.ANSI_FORM_FEED:
+ // Treat as true LF.
+ return h.executeLF()
+
+ case ansiterm.ANSI_LINE_FEED:
+ // Simulate a CR and LF for now since there is no way in go-ansiterm
+ // to tell if the LF should include CR (and more things break when it's
+ // missing than when it's incorrectly added).
+ handled, err := h.simulateLF(true)
+ if handled || err != nil {
+ return err
+ }
+ return h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED)
+
+ case ansiterm.ANSI_CARRIAGE_RETURN:
+ if h.wrapNext {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.clearWrap()
+ }
+ pos, _, err := h.getCurrentInfo()
+ if err != nil {
+ return err
+ }
+ if pos.X != 0 {
+ pos.X = 0
+ h.updatePos(pos)
+ h.buffer.WriteByte(ansiterm.ANSI_CARRIAGE_RETURN)
+ }
+ return nil
+
+ default:
+ return nil
+ }
+}
+
+func (h *windowsAnsiEventHandler) CUU(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("CUU: [%v]", []string{strconv.Itoa(param)})
+ h.clearWrap()
+ return h.moveCursorVertical(-param)
+}
+
+func (h *windowsAnsiEventHandler) CUD(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("CUD: [%v]", []string{strconv.Itoa(param)})
+ h.clearWrap()
+ return h.moveCursorVertical(param)
+}
+
+func (h *windowsAnsiEventHandler) CUF(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("CUF: [%v]", []string{strconv.Itoa(param)})
+ h.clearWrap()
+ return h.moveCursorHorizontal(param)
+}
+
+func (h *windowsAnsiEventHandler) CUB(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("CUB: [%v]", []string{strconv.Itoa(param)})
+ h.clearWrap()
+ return h.moveCursorHorizontal(-param)
+}
+
+func (h *windowsAnsiEventHandler) CNL(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("CNL: [%v]", []string{strconv.Itoa(param)})
+ h.clearWrap()
+ return h.moveCursorLine(param)
+}
+
+func (h *windowsAnsiEventHandler) CPL(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("CPL: [%v]", []string{strconv.Itoa(param)})
+ h.clearWrap()
+ return h.moveCursorLine(-param)
+}
+
+func (h *windowsAnsiEventHandler) CHA(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("CHA: [%v]", []string{strconv.Itoa(param)})
+ h.clearWrap()
+ return h.moveCursorColumn(param)
+}
+
+func (h *windowsAnsiEventHandler) VPA(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("VPA: [[%d]]", param)
+ h.clearWrap()
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+ window := h.getCursorWindow(info)
+ position := info.CursorPosition
+ position.Y = window.Top + int16(param) - 1
+ return h.setCursorPosition(position, window)
+}
+
+func (h *windowsAnsiEventHandler) CUP(row int, col int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("CUP: [[%d %d]]", row, col)
+ h.clearWrap()
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+
+ window := h.getCursorWindow(info)
+ position := COORD{window.Left + int16(col) - 1, window.Top + int16(row) - 1}
+ return h.setCursorPosition(position, window)
+}
+
+func (h *windowsAnsiEventHandler) HVP(row int, col int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("HVP: [[%d %d]]", row, col)
+ h.clearWrap()
+ return h.CUP(row, col)
+}
+
+func (h *windowsAnsiEventHandler) DECTCEM(visible bool) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("DECTCEM: [%v]", []string{strconv.FormatBool(visible)})
+ h.clearWrap()
+ return nil
+}
+
+func (h *windowsAnsiEventHandler) DECOM(enable bool) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("DECOM: [%v]", []string{strconv.FormatBool(enable)})
+ h.clearWrap()
+ h.originMode = enable
+ return h.CUP(1, 1)
+}
+
+func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("DECCOLM: [%v]", []string{strconv.FormatBool(use132)})
+ h.clearWrap()
+ if err := h.ED(2); err != nil {
+ return err
+ }
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+ targetWidth := int16(80)
+ if use132 {
+ targetWidth = 132
+ }
+ if info.Size.X < targetWidth {
+ if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
+ h.logf("set buffer failed: %v", err)
+ return err
+ }
+ }
+ window := info.Window
+ window.Left = 0
+ window.Right = targetWidth - 1
+ if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
+ h.logf("set window failed: %v", err)
+ return err
+ }
+ if info.Size.X > targetWidth {
+ if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
+ h.logf("set buffer failed: %v", err)
+ return err
+ }
+ }
+ return SetConsoleCursorPosition(h.fd, COORD{0, 0})
+}
+
+func (h *windowsAnsiEventHandler) ED(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("ED: [%v]", []string{strconv.Itoa(param)})
+ h.clearWrap()
+
+ // [J -- Erases from the cursor to the end of the screen, including the cursor position.
+ // [1J -- Erases from the beginning of the screen to the cursor, including the cursor position.
+ // [2J -- Erases the complete display. The cursor does not move.
+ // Notes:
+ // -- Clearing the entire buffer, versus just the Window, works best for Windows Consoles
+
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+
+ var start COORD
+ var end COORD
+
+ switch param {
+ case 0:
+ start = info.CursorPosition
+ end = COORD{info.Size.X - 1, info.Size.Y - 1}
+
+ case 1:
+ start = COORD{0, 0}
+ end = info.CursorPosition
+
+ case 2:
+ start = COORD{0, 0}
+ end = COORD{info.Size.X - 1, info.Size.Y - 1}
+ }
+
+ err = h.clearRange(h.attributes, start, end)
+ if err != nil {
+ return err
+ }
+
+ // If the whole buffer was cleared, move the window to the top while preserving
+ // the window-relative cursor position.
+ if param == 2 {
+ pos := info.CursorPosition
+ window := info.Window
+ pos.Y -= window.Top
+ window.Bottom -= window.Top
+ window.Top = 0
+ if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
+ return err
+ }
+ if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (h *windowsAnsiEventHandler) EL(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("EL: [%v]", strconv.Itoa(param))
+ h.clearWrap()
+
+ // [K -- Erases from the cursor to the end of the line, including the cursor position.
+ // [1K -- Erases from the beginning of the line to the cursor, including the cursor position.
+ // [2K -- Erases the complete line.
+
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+
+ var start COORD
+ var end COORD
+
+ switch param {
+ case 0:
+ start = info.CursorPosition
+ end = COORD{info.Size.X, info.CursorPosition.Y}
+
+ case 1:
+ start = COORD{0, info.CursorPosition.Y}
+ end = info.CursorPosition
+
+ case 2:
+ start = COORD{0, info.CursorPosition.Y}
+ end = COORD{info.Size.X, info.CursorPosition.Y}
+ }
+
+ err = h.clearRange(h.attributes, start, end)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (h *windowsAnsiEventHandler) IL(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("IL: [%v]", strconv.Itoa(param))
+ h.clearWrap()
+ return h.insertLines(param)
+}
+
+func (h *windowsAnsiEventHandler) DL(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("DL: [%v]", strconv.Itoa(param))
+ h.clearWrap()
+ return h.deleteLines(param)
+}
+
+func (h *windowsAnsiEventHandler) ICH(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("ICH: [%v]", strconv.Itoa(param))
+ h.clearWrap()
+ return h.insertCharacters(param)
+}
+
+func (h *windowsAnsiEventHandler) DCH(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("DCH: [%v]", strconv.Itoa(param))
+ h.clearWrap()
+ return h.deleteCharacters(param)
+}
+
+func (h *windowsAnsiEventHandler) SGR(params []int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ strings := []string{}
+ for _, v := range params {
+ strings = append(strings, strconv.Itoa(v))
+ }
+
+ h.logf("SGR: [%v]", strings)
+
+ if len(params) <= 0 {
+ h.attributes = h.infoReset.Attributes
+ h.inverted = false
+ } else {
+ for _, attr := range params {
+
+ if attr == ansiterm.ANSI_SGR_RESET {
+ h.attributes = h.infoReset.Attributes
+ h.inverted = false
+ continue
+ }
+
+ h.attributes, h.inverted = collectAnsiIntoWindowsAttributes(h.attributes, h.inverted, h.infoReset.Attributes, int16(attr))
+ }
+ }
+
+ attributes := h.attributes
+ if h.inverted {
+ attributes = invertAttributes(attributes)
+ }
+ err := SetConsoleTextAttribute(h.fd, attributes)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (h *windowsAnsiEventHandler) SU(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("SU: [%v]", []string{strconv.Itoa(param)})
+ h.clearWrap()
+ return h.scrollUp(param)
+}
+
+func (h *windowsAnsiEventHandler) SD(param int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("SD: [%v]", []string{strconv.Itoa(param)})
+ h.clearWrap()
+ return h.scrollDown(param)
+}
+
+func (h *windowsAnsiEventHandler) DA(params []string) error {
+ h.logf("DA: [%v]", params)
+ // DA cannot be implemented because it must send data on the VT100 input stream,
+ // which is not available to go-ansiterm.
+ return nil
+}
+
+func (h *windowsAnsiEventHandler) DECSTBM(top int, bottom int) error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("DECSTBM: [%d, %d]", top, bottom)
+
+ // Windows is 0 indexed, Linux is 1 indexed
+ h.sr.top = int16(top - 1)
+ h.sr.bottom = int16(bottom - 1)
+
+ // This command also moves the cursor to the origin.
+ h.clearWrap()
+ return h.CUP(1, 1)
+}
+
+func (h *windowsAnsiEventHandler) RI() error {
+ if err := h.Flush(); err != nil {
+ return err
+ }
+ h.logf("RI: []")
+ h.clearWrap()
+
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+
+ sr := h.effectiveSr(info.Window)
+ if info.CursorPosition.Y == sr.top {
+ return h.scrollDown(1)
+ }
+
+ return h.moveCursorVertical(-1)
+}
+
+func (h *windowsAnsiEventHandler) IND() error {
+ h.logf("IND: []")
+ return h.executeLF()
+}
+
+func (h *windowsAnsiEventHandler) Flush() error {
+ h.curInfo = nil
+ if h.buffer.Len() > 0 {
+ h.logf("Flush: [%s]", h.buffer.Bytes())
+ if _, err := h.buffer.WriteTo(h.file); err != nil {
+ return err
+ }
+ }
+
+ if h.wrapNext && !h.drewMarginByte {
+ h.logf("Flush: drawing margin byte '%c'", h.marginByte)
+
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return err
+ }
+
+ charInfo := []CHAR_INFO{{UnicodeChar: uint16(h.marginByte), Attributes: info.Attributes}}
+ size := COORD{1, 1}
+ position := COORD{0, 0}
+ region := SMALL_RECT{Left: info.CursorPosition.X, Top: info.CursorPosition.Y, Right: info.CursorPosition.X, Bottom: info.CursorPosition.Y}
+ if err := WriteConsoleOutput(h.fd, charInfo, size, position, ®ion); err != nil {
+ return err
+ }
+ h.drewMarginByte = true
+ }
+ return nil
+}
+
+// cacheConsoleInfo ensures that the current console screen information has been queried
+// since the last call to Flush(). It must be called before accessing h.curInfo or h.curPos.
+func (h *windowsAnsiEventHandler) getCurrentInfo() (COORD, *CONSOLE_SCREEN_BUFFER_INFO, error) {
+ if h.curInfo == nil {
+ info, err := GetConsoleScreenBufferInfo(h.fd)
+ if err != nil {
+ return COORD{}, nil, err
+ }
+ h.curInfo = info
+ h.curPos = info.CursorPosition
+ }
+ return h.curPos, h.curInfo, nil
+}
+
+func (h *windowsAnsiEventHandler) updatePos(pos COORD) {
+ if h.curInfo == nil {
+ panic("failed to call getCurrentInfo before calling updatePos")
+ }
+ h.curPos = pos
+}
+
+// clearWrap clears the state where the cursor is in the margin
+// waiting for the next character before wrapping the line. This must
+// be done before most operations that act on the cursor.
+func (h *windowsAnsiEventHandler) clearWrap() {
+ h.wrapNext = false
+ h.drewMarginByte = false
+}
diff --git a/vendor/github.com/BurntSushi/toml/.gitignore b/vendor/github.com/BurntSushi/toml/.gitignore
new file mode 100644
index 00000000..fe79e3ad
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/.gitignore
@@ -0,0 +1,2 @@
+/toml.test
+/toml-test
diff --git a/vendor/github.com/BurntSushi/toml/COPYING b/vendor/github.com/BurntSushi/toml/COPYING
new file mode 100644
index 00000000..01b57432
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/COPYING
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 TOML authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/BurntSushi/toml/README.md b/vendor/github.com/BurntSushi/toml/README.md
new file mode 100644
index 00000000..235496ee
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/README.md
@@ -0,0 +1,120 @@
+TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
+reflection interface similar to Go's standard library `json` and `xml` packages.
+
+Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0).
+
+Documentation: https://pkg.go.dev/github.com/BurntSushi/toml
+
+See the [releases page](https://github.com/BurntSushi/toml/releases) for a
+changelog; this information is also in the git tag annotations (e.g. `git show
+v0.4.0`).
+
+This library requires Go 1.18 or newer; add it to your go.mod with:
+
+ % go get github.com/BurntSushi/toml@latest
+
+It also comes with a TOML validator CLI tool:
+
+ % go install github.com/BurntSushi/toml/cmd/tomlv@latest
+ % tomlv some-toml-file.toml
+
+### Examples
+For the simplest example, consider some TOML file as just a list of keys and
+values:
+
+```toml
+Age = 25
+Cats = [ "Cauchy", "Plato" ]
+Pi = 3.14
+Perfection = [ 6, 28, 496, 8128 ]
+DOB = 1987-07-05T05:45:00Z
+```
+
+Which can be decoded with:
+
+```go
+type Config struct {
+ Age int
+ Cats []string
+ Pi float64
+ Perfection []int
+ DOB time.Time
+}
+
+var conf Config
+_, err := toml.Decode(tomlData, &conf)
+```
+
+You can also use struct tags if your struct field name doesn't map to a TOML key
+value directly:
+
+```toml
+some_key_NAME = "wat"
+```
+
+```go
+type TOML struct {
+ ObscureKey string `toml:"some_key_NAME"`
+}
+```
+
+Beware that like other decoders **only exported fields** are considered when
+encoding and decoding; private fields are silently ignored.
+
+### Using the `Marshaler` and `encoding.TextUnmarshaler` interfaces
+Here's an example that automatically parses values in a `mail.Address`:
+
+```toml
+contacts = [
+ "Donald Duck ",
+ "Scrooge McDuck ",
+]
+```
+
+Can be decoded with:
+
+```go
+// Create address type which satisfies the encoding.TextUnmarshaler interface.
+type address struct {
+ *mail.Address
+}
+
+func (a *address) UnmarshalText(text []byte) error {
+ var err error
+ a.Address, err = mail.ParseAddress(string(text))
+ return err
+}
+
+// Decode it.
+func decode() {
+ blob := `
+ contacts = [
+ "Donald Duck ",
+ "Scrooge McDuck ",
+ ]
+ `
+
+ var contacts struct {
+ Contacts []address
+ }
+
+ _, err := toml.Decode(blob, &contacts)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, c := range contacts.Contacts {
+ fmt.Printf("%#v\n", c.Address)
+ }
+
+ // Output:
+ // &mail.Address{Name:"Donald Duck", Address:"donald@duckburg.com"}
+ // &mail.Address{Name:"Scrooge McDuck", Address:"scrooge@duckburg.com"}
+}
+```
+
+To target TOML specifically you can implement `UnmarshalTOML` TOML interface in
+a similar way.
+
+### More complex usage
+See the [`_example/`](/_example) directory for a more complex example.
diff --git a/vendor/github.com/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go
new file mode 100644
index 00000000..3fa516ca
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/decode.go
@@ -0,0 +1,638 @@
+package toml
+
+import (
+ "bytes"
+ "encoding"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/fs"
+ "math"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Unmarshaler is the interface implemented by objects that can unmarshal a
+// TOML description of themselves.
+type Unmarshaler interface {
+ UnmarshalTOML(any) error
+}
+
+// Unmarshal decodes the contents of data in TOML format into a pointer v.
+//
+// See [Decoder] for a description of the decoding process.
+func Unmarshal(data []byte, v any) error {
+ _, err := NewDecoder(bytes.NewReader(data)).Decode(v)
+ return err
+}
+
+// Decode the TOML data in to the pointer v.
+//
+// See [Decoder] for a description of the decoding process.
+func Decode(data string, v any) (MetaData, error) {
+ return NewDecoder(strings.NewReader(data)).Decode(v)
+}
+
+// DecodeFile reads the contents of a file and decodes it with [Decode].
+func DecodeFile(path string, v any) (MetaData, error) {
+ fp, err := os.Open(path)
+ if err != nil {
+ return MetaData{}, err
+ }
+ defer fp.Close()
+ return NewDecoder(fp).Decode(v)
+}
+
+// DecodeFS reads the contents of a file from [fs.FS] and decodes it with
+// [Decode].
+func DecodeFS(fsys fs.FS, path string, v any) (MetaData, error) {
+ fp, err := fsys.Open(path)
+ if err != nil {
+ return MetaData{}, err
+ }
+ defer fp.Close()
+ return NewDecoder(fp).Decode(v)
+}
+
+// Primitive is a TOML value that hasn't been decoded into a Go value.
+//
+// This type can be used for any value, which will cause decoding to be delayed.
+// You can use [PrimitiveDecode] to "manually" decode these values.
+//
+// NOTE: The underlying representation of a `Primitive` value is subject to
+// change. Do not rely on it.
+//
+// NOTE: Primitive values are still parsed, so using them will only avoid the
+// overhead of reflection. They can be useful when you don't know the exact type
+// of TOML data until runtime.
+type Primitive struct {
+ undecoded any
+ context Key
+}
+
+// The significand precision for float32 and float64 is 24 and 53 bits; this is
+// the range a natural number can be stored in a float without loss of data.
+const (
+ maxSafeFloat32Int = 16777215 // 2^24-1
+ maxSafeFloat64Int = int64(9007199254740991) // 2^53-1
+)
+
+// Decoder decodes TOML data.
+//
+// TOML tables correspond to Go structs or maps; they can be used
+// interchangeably, but structs offer better type safety.
+//
+// TOML table arrays correspond to either a slice of structs or a slice of maps.
+//
+// TOML datetimes correspond to [time.Time]. Local datetimes are parsed in the
+// local timezone.
+//
+// [time.Duration] types are treated as nanoseconds if the TOML value is an
+// integer, or they're parsed with time.ParseDuration() if they're strings.
+//
+// All other TOML types (float, string, int, bool and array) correspond to the
+// obvious Go types.
+//
+// An exception to the above rules is if a type implements the TextUnmarshaler
+// interface, in which case any primitive TOML value (floats, strings, integers,
+// booleans, datetimes) will be converted to a []byte and given to the value's
+// UnmarshalText method. See the Unmarshaler example for a demonstration with
+// email addresses.
+//
+// # Key mapping
+//
+// TOML keys can map to either keys in a Go map or field names in a Go struct.
+// The special `toml` struct tag can be used to map TOML keys to struct fields
+// that don't match the key name exactly (see the example). A case insensitive
+// match to struct names will be tried if an exact match can't be found.
+//
+// The mapping between TOML values and Go values is loose. That is, there may
+// exist TOML values that cannot be placed into your representation, and there
+// may be parts of your representation that do not correspond to TOML values.
+// This loose mapping can be made stricter by using the IsDefined and/or
+// Undecoded methods on the MetaData returned.
+//
+// This decoder does not handle cyclic types. Decode will not terminate if a
+// cyclic type is passed.
+type Decoder struct {
+ r io.Reader
+}
+
+// NewDecoder creates a new Decoder.
+func NewDecoder(r io.Reader) *Decoder {
+ return &Decoder{r: r}
+}
+
+var (
+ unmarshalToml = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
+ unmarshalText = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
+ primitiveType = reflect.TypeOf((*Primitive)(nil)).Elem()
+)
+
+// Decode TOML data in to the pointer `v`.
+func (dec *Decoder) Decode(v any) (MetaData, error) {
+ rv := reflect.ValueOf(v)
+ if rv.Kind() != reflect.Ptr {
+ s := "%q"
+ if reflect.TypeOf(v) == nil {
+ s = "%v"
+ }
+
+ return MetaData{}, fmt.Errorf("toml: cannot decode to non-pointer "+s, reflect.TypeOf(v))
+ }
+ if rv.IsNil() {
+ return MetaData{}, fmt.Errorf("toml: cannot decode to nil value of %q", reflect.TypeOf(v))
+ }
+
+ // Check if this is a supported type: struct, map, any, or something that
+ // implements UnmarshalTOML or UnmarshalText.
+ rv = indirect(rv)
+ rt := rv.Type()
+ if rv.Kind() != reflect.Struct && rv.Kind() != reflect.Map &&
+ !(rv.Kind() == reflect.Interface && rv.NumMethod() == 0) &&
+ !rt.Implements(unmarshalToml) && !rt.Implements(unmarshalText) {
+ return MetaData{}, fmt.Errorf("toml: cannot decode to type %s", rt)
+ }
+
+ // TODO: parser should read from io.Reader? Or at the very least, make it
+ // read from []byte rather than string
+ data, err := io.ReadAll(dec.r)
+ if err != nil {
+ return MetaData{}, err
+ }
+
+ p, err := parse(string(data))
+ if err != nil {
+ return MetaData{}, err
+ }
+
+ md := MetaData{
+ mapping: p.mapping,
+ keyInfo: p.keyInfo,
+ keys: p.ordered,
+ decoded: make(map[string]struct{}, len(p.ordered)),
+ context: nil,
+ data: data,
+ }
+ return md, md.unify(p.mapping, rv)
+}
+
+// PrimitiveDecode is just like the other Decode* functions, except it decodes a
+// TOML value that has already been parsed. Valid primitive values can *only* be
+// obtained from values filled by the decoder functions, including this method.
+// (i.e., v may contain more [Primitive] values.)
+//
+// Meta data for primitive values is included in the meta data returned by the
+// Decode* functions with one exception: keys returned by the Undecoded method
+// will only reflect keys that were decoded. Namely, any keys hidden behind a
+// Primitive will be considered undecoded. Executing this method will update the
+// undecoded keys in the meta data. (See the example.)
+func (md *MetaData) PrimitiveDecode(primValue Primitive, v any) error {
+ md.context = primValue.context
+ defer func() { md.context = nil }()
+ return md.unify(primValue.undecoded, rvalue(v))
+}
+
+// markDecodedRecursive is a helper to mark any key under the given tmap as
+// decoded, recursing as needed
+func markDecodedRecursive(md *MetaData, tmap map[string]any) {
+ for key := range tmap {
+ md.decoded[md.context.add(key).String()] = struct{}{}
+ if tmap, ok := tmap[key].(map[string]any); ok {
+ md.context = append(md.context, key)
+ markDecodedRecursive(md, tmap)
+ md.context = md.context[0 : len(md.context)-1]
+ }
+ }
+}
+
+// unify performs a sort of type unification based on the structure of `rv`,
+// which is the client representation.
+//
+// Any type mismatch produces an error. Finding a type that we don't know
+// how to handle produces an unsupported type error.
+func (md *MetaData) unify(data any, rv reflect.Value) error {
+ // Special case. Look for a `Primitive` value.
+ // TODO: #76 would make this superfluous after implemented.
+ if rv.Type() == primitiveType {
+ // Save the undecoded data and the key context into the primitive
+ // value.
+ context := make(Key, len(md.context))
+ copy(context, md.context)
+ rv.Set(reflect.ValueOf(Primitive{
+ undecoded: data,
+ context: context,
+ }))
+ return nil
+ }
+
+ rvi := rv.Interface()
+ if v, ok := rvi.(Unmarshaler); ok {
+ err := v.UnmarshalTOML(data)
+ if err != nil {
+ return md.parseErr(err)
+ }
+ // Assume the Unmarshaler decoded everything, so mark all keys under
+ // this table as decoded.
+ if tmap, ok := data.(map[string]any); ok {
+ markDecodedRecursive(md, tmap)
+ }
+ if aot, ok := data.([]map[string]any); ok {
+ for _, tmap := range aot {
+ markDecodedRecursive(md, tmap)
+ }
+ }
+ return nil
+ }
+ if v, ok := rvi.(encoding.TextUnmarshaler); ok {
+ return md.unifyText(data, v)
+ }
+
+ // TODO:
+ // The behavior here is incorrect whenever a Go type satisfies the
+ // encoding.TextUnmarshaler interface but also corresponds to a TOML hash or
+ // array. In particular, the unmarshaler should only be applied to primitive
+ // TOML values. But at this point, it will be applied to all kinds of values
+ // and produce an incorrect error whenever those values are hashes or arrays
+ // (including arrays of tables).
+
+ k := rv.Kind()
+
+ if k >= reflect.Int && k <= reflect.Uint64 {
+ return md.unifyInt(data, rv)
+ }
+ switch k {
+ case reflect.Struct:
+ return md.unifyStruct(data, rv)
+ case reflect.Map:
+ return md.unifyMap(data, rv)
+ case reflect.Array:
+ return md.unifyArray(data, rv)
+ case reflect.Slice:
+ return md.unifySlice(data, rv)
+ case reflect.String:
+ return md.unifyString(data, rv)
+ case reflect.Bool:
+ return md.unifyBool(data, rv)
+ case reflect.Interface:
+ if rv.NumMethod() > 0 { /// Only empty interfaces are supported.
+ return md.e("unsupported type %s", rv.Type())
+ }
+ return md.unifyAnything(data, rv)
+ case reflect.Float32, reflect.Float64:
+ return md.unifyFloat64(data, rv)
+ }
+ return md.e("unsupported type %s", rv.Kind())
+}
+
+func (md *MetaData) unifyStruct(mapping any, rv reflect.Value) error {
+ tmap, ok := mapping.(map[string]any)
+ if !ok {
+ if mapping == nil {
+ return nil
+ }
+ return md.e("type mismatch for %s: expected table but found %s", rv.Type().String(), fmtType(mapping))
+ }
+
+ for key, datum := range tmap {
+ var f *field
+ fields := cachedTypeFields(rv.Type())
+ for i := range fields {
+ ff := &fields[i]
+ if ff.name == key {
+ f = ff
+ break
+ }
+ if f == nil && strings.EqualFold(ff.name, key) {
+ f = ff
+ }
+ }
+ if f != nil {
+ subv := rv
+ for _, i := range f.index {
+ subv = indirect(subv.Field(i))
+ }
+
+ if isUnifiable(subv) {
+ md.decoded[md.context.add(key).String()] = struct{}{}
+ md.context = append(md.context, key)
+
+ err := md.unify(datum, subv)
+ if err != nil {
+ return err
+ }
+ md.context = md.context[0 : len(md.context)-1]
+ } else if f.name != "" {
+ return md.e("cannot write unexported field %s.%s", rv.Type().String(), f.name)
+ }
+ }
+ }
+ return nil
+}
+
+func (md *MetaData) unifyMap(mapping any, rv reflect.Value) error {
+ keyType := rv.Type().Key().Kind()
+ if keyType != reflect.String && keyType != reflect.Interface {
+ return fmt.Errorf("toml: cannot decode to a map with non-string key type (%s in %q)",
+ keyType, rv.Type())
+ }
+
+ tmap, ok := mapping.(map[string]any)
+ if !ok {
+ if tmap == nil {
+ return nil
+ }
+ return md.badtype("map", mapping)
+ }
+ if rv.IsNil() {
+ rv.Set(reflect.MakeMap(rv.Type()))
+ }
+ for k, v := range tmap {
+ md.decoded[md.context.add(k).String()] = struct{}{}
+ md.context = append(md.context, k)
+
+ rvval := reflect.Indirect(reflect.New(rv.Type().Elem()))
+
+ err := md.unify(v, indirect(rvval))
+ if err != nil {
+ return err
+ }
+ md.context = md.context[0 : len(md.context)-1]
+
+ rvkey := indirect(reflect.New(rv.Type().Key()))
+
+ switch keyType {
+ case reflect.Interface:
+ rvkey.Set(reflect.ValueOf(k))
+ case reflect.String:
+ rvkey.SetString(k)
+ }
+
+ rv.SetMapIndex(rvkey, rvval)
+ }
+ return nil
+}
+
+func (md *MetaData) unifyArray(data any, rv reflect.Value) error {
+ datav := reflect.ValueOf(data)
+ if datav.Kind() != reflect.Slice {
+ if !datav.IsValid() {
+ return nil
+ }
+ return md.badtype("slice", data)
+ }
+ if l := datav.Len(); l != rv.Len() {
+ return md.e("expected array length %d; got TOML array of length %d", rv.Len(), l)
+ }
+ return md.unifySliceArray(datav, rv)
+}
+
+func (md *MetaData) unifySlice(data any, rv reflect.Value) error {
+ datav := reflect.ValueOf(data)
+ if datav.Kind() != reflect.Slice {
+ if !datav.IsValid() {
+ return nil
+ }
+ return md.badtype("slice", data)
+ }
+ n := datav.Len()
+ if rv.IsNil() || rv.Cap() < n {
+ rv.Set(reflect.MakeSlice(rv.Type(), n, n))
+ }
+ rv.SetLen(n)
+ return md.unifySliceArray(datav, rv)
+}
+
+func (md *MetaData) unifySliceArray(data, rv reflect.Value) error {
+ l := data.Len()
+ for i := 0; i < l; i++ {
+ err := md.unify(data.Index(i).Interface(), indirect(rv.Index(i)))
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (md *MetaData) unifyString(data any, rv reflect.Value) error {
+ _, ok := rv.Interface().(json.Number)
+ if ok {
+ if i, ok := data.(int64); ok {
+ rv.SetString(strconv.FormatInt(i, 10))
+ } else if f, ok := data.(float64); ok {
+ rv.SetString(strconv.FormatFloat(f, 'f', -1, 64))
+ } else {
+ return md.badtype("string", data)
+ }
+ return nil
+ }
+
+ if s, ok := data.(string); ok {
+ rv.SetString(s)
+ return nil
+ }
+ return md.badtype("string", data)
+}
+
+func (md *MetaData) unifyFloat64(data any, rv reflect.Value) error {
+ rvk := rv.Kind()
+
+ if num, ok := data.(float64); ok {
+ switch rvk {
+ case reflect.Float32:
+ if num < -math.MaxFloat32 || num > math.MaxFloat32 {
+ return md.parseErr(errParseRange{i: num, size: rvk.String()})
+ }
+ fallthrough
+ case reflect.Float64:
+ rv.SetFloat(num)
+ default:
+ panic("bug")
+ }
+ return nil
+ }
+
+ if num, ok := data.(int64); ok {
+ if (rvk == reflect.Float32 && (num < -maxSafeFloat32Int || num > maxSafeFloat32Int)) ||
+ (rvk == reflect.Float64 && (num < -maxSafeFloat64Int || num > maxSafeFloat64Int)) {
+ return md.parseErr(errUnsafeFloat{i: num, size: rvk.String()})
+ }
+ rv.SetFloat(float64(num))
+ return nil
+ }
+
+ return md.badtype("float", data)
+}
+
+func (md *MetaData) unifyInt(data any, rv reflect.Value) error {
+ _, ok := rv.Interface().(time.Duration)
+ if ok {
+ // Parse as string duration, and fall back to regular integer parsing
+ // (as nanosecond) if this is not a string.
+ if s, ok := data.(string); ok {
+ dur, err := time.ParseDuration(s)
+ if err != nil {
+ return md.parseErr(errParseDuration{s})
+ }
+ rv.SetInt(int64(dur))
+ return nil
+ }
+ }
+
+ num, ok := data.(int64)
+ if !ok {
+ return md.badtype("integer", data)
+ }
+
+ rvk := rv.Kind()
+ switch {
+ case rvk >= reflect.Int && rvk <= reflect.Int64:
+ if (rvk == reflect.Int8 && (num < math.MinInt8 || num > math.MaxInt8)) ||
+ (rvk == reflect.Int16 && (num < math.MinInt16 || num > math.MaxInt16)) ||
+ (rvk == reflect.Int32 && (num < math.MinInt32 || num > math.MaxInt32)) {
+ return md.parseErr(errParseRange{i: num, size: rvk.String()})
+ }
+ rv.SetInt(num)
+ case rvk >= reflect.Uint && rvk <= reflect.Uint64:
+ unum := uint64(num)
+ if rvk == reflect.Uint8 && (num < 0 || unum > math.MaxUint8) ||
+ rvk == reflect.Uint16 && (num < 0 || unum > math.MaxUint16) ||
+ rvk == reflect.Uint32 && (num < 0 || unum > math.MaxUint32) {
+ return md.parseErr(errParseRange{i: num, size: rvk.String()})
+ }
+ rv.SetUint(unum)
+ default:
+ panic("unreachable")
+ }
+ return nil
+}
+
+func (md *MetaData) unifyBool(data any, rv reflect.Value) error {
+ if b, ok := data.(bool); ok {
+ rv.SetBool(b)
+ return nil
+ }
+ return md.badtype("boolean", data)
+}
+
+func (md *MetaData) unifyAnything(data any, rv reflect.Value) error {
+ rv.Set(reflect.ValueOf(data))
+ return nil
+}
+
+func (md *MetaData) unifyText(data any, v encoding.TextUnmarshaler) error {
+ var s string
+ switch sdata := data.(type) {
+ case Marshaler:
+ text, err := sdata.MarshalTOML()
+ if err != nil {
+ return err
+ }
+ s = string(text)
+ case encoding.TextMarshaler:
+ text, err := sdata.MarshalText()
+ if err != nil {
+ return err
+ }
+ s = string(text)
+ case fmt.Stringer:
+ s = sdata.String()
+ case string:
+ s = sdata
+ case bool:
+ s = fmt.Sprintf("%v", sdata)
+ case int64:
+ s = fmt.Sprintf("%d", sdata)
+ case float64:
+ s = fmt.Sprintf("%f", sdata)
+ default:
+ return md.badtype("primitive (string-like)", data)
+ }
+ if err := v.UnmarshalText([]byte(s)); err != nil {
+ return md.parseErr(err)
+ }
+ return nil
+}
+
+func (md *MetaData) badtype(dst string, data any) error {
+ return md.e("incompatible types: TOML value has type %s; destination has type %s", fmtType(data), dst)
+}
+
+func (md *MetaData) parseErr(err error) error {
+ k := md.context.String()
+ d := string(md.data)
+ return ParseError{
+ Message: err.Error(),
+ err: err,
+ LastKey: k,
+ Position: md.keyInfo[k].pos.withCol(d),
+ Line: md.keyInfo[k].pos.Line,
+ input: d,
+ }
+}
+
+func (md *MetaData) e(format string, args ...any) error {
+ f := "toml: "
+ if len(md.context) > 0 {
+ f = fmt.Sprintf("toml: (last key %q): ", md.context)
+ p := md.keyInfo[md.context.String()].pos
+ if p.Line > 0 {
+ f = fmt.Sprintf("toml: line %d (last key %q): ", p.Line, md.context)
+ }
+ }
+ return fmt.Errorf(f+format, args...)
+}
+
+// rvalue returns a reflect.Value of `v`. All pointers are resolved.
+func rvalue(v any) reflect.Value {
+ return indirect(reflect.ValueOf(v))
+}
+
+// indirect returns the value pointed to by a pointer.
+//
+// Pointers are followed until the value is not a pointer. New values are
+// allocated for each nil pointer.
+//
+// An exception to this rule is if the value satisfies an interface of interest
+// to us (like encoding.TextUnmarshaler).
+func indirect(v reflect.Value) reflect.Value {
+ if v.Kind() != reflect.Ptr {
+ if v.CanSet() {
+ pv := v.Addr()
+ pvi := pv.Interface()
+ if _, ok := pvi.(encoding.TextUnmarshaler); ok {
+ return pv
+ }
+ if _, ok := pvi.(Unmarshaler); ok {
+ return pv
+ }
+ }
+ return v
+ }
+ if v.IsNil() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ return indirect(reflect.Indirect(v))
+}
+
+func isUnifiable(rv reflect.Value) bool {
+ if rv.CanSet() {
+ return true
+ }
+ rvi := rv.Interface()
+ if _, ok := rvi.(encoding.TextUnmarshaler); ok {
+ return true
+ }
+ if _, ok := rvi.(Unmarshaler); ok {
+ return true
+ }
+ return false
+}
+
+// fmt %T with "interface {}" replaced with "any", which is far more readable.
+func fmtType(t any) string {
+ return strings.ReplaceAll(fmt.Sprintf("%T", t), "interface {}", "any")
+}
diff --git a/vendor/github.com/BurntSushi/toml/deprecated.go b/vendor/github.com/BurntSushi/toml/deprecated.go
new file mode 100644
index 00000000..155709a8
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/deprecated.go
@@ -0,0 +1,29 @@
+package toml
+
+import (
+ "encoding"
+ "io"
+)
+
+// TextMarshaler is an alias for encoding.TextMarshaler.
+//
+// Deprecated: use encoding.TextMarshaler
+type TextMarshaler encoding.TextMarshaler
+
+// TextUnmarshaler is an alias for encoding.TextUnmarshaler.
+//
+// Deprecated: use encoding.TextUnmarshaler
+type TextUnmarshaler encoding.TextUnmarshaler
+
+// DecodeReader is an alias for NewDecoder(r).Decode(v).
+//
+// Deprecated: use NewDecoder(reader).Decode(&value).
+func DecodeReader(r io.Reader, v any) (MetaData, error) { return NewDecoder(r).Decode(v) }
+
+// PrimitiveDecode is an alias for MetaData.PrimitiveDecode().
+//
+// Deprecated: use MetaData.PrimitiveDecode.
+func PrimitiveDecode(primValue Primitive, v any) error {
+ md := MetaData{decoded: make(map[string]struct{})}
+ return md.unify(primValue.undecoded, rvalue(v))
+}
diff --git a/vendor/github.com/BurntSushi/toml/doc.go b/vendor/github.com/BurntSushi/toml/doc.go
new file mode 100644
index 00000000..82c90a90
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/doc.go
@@ -0,0 +1,8 @@
+// Package toml implements decoding and encoding of TOML files.
+//
+// This package supports TOML v1.0.0, as specified at https://toml.io
+//
+// The github.com/BurntSushi/toml/cmd/tomlv package implements a TOML validator,
+// and can be used to verify if TOML document is valid. It can also be used to
+// print the type of each key.
+package toml
diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go
new file mode 100644
index 00000000..ac196e7d
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/encode.go
@@ -0,0 +1,776 @@
+package toml
+
+import (
+ "bufio"
+ "bytes"
+ "encoding"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "math"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/BurntSushi/toml/internal"
+)
+
+type tomlEncodeError struct{ error }
+
+var (
+ errArrayNilElement = errors.New("toml: cannot encode array with nil element")
+ errNonString = errors.New("toml: cannot encode a map with non-string key type")
+ errNoKey = errors.New("toml: top-level values must be Go maps or structs")
+ errAnything = errors.New("") // used in testing
+)
+
+var dblQuotedReplacer = strings.NewReplacer(
+ "\"", "\\\"",
+ "\\", "\\\\",
+ "\x00", `\u0000`,
+ "\x01", `\u0001`,
+ "\x02", `\u0002`,
+ "\x03", `\u0003`,
+ "\x04", `\u0004`,
+ "\x05", `\u0005`,
+ "\x06", `\u0006`,
+ "\x07", `\u0007`,
+ "\b", `\b`,
+ "\t", `\t`,
+ "\n", `\n`,
+ "\x0b", `\u000b`,
+ "\f", `\f`,
+ "\r", `\r`,
+ "\x0e", `\u000e`,
+ "\x0f", `\u000f`,
+ "\x10", `\u0010`,
+ "\x11", `\u0011`,
+ "\x12", `\u0012`,
+ "\x13", `\u0013`,
+ "\x14", `\u0014`,
+ "\x15", `\u0015`,
+ "\x16", `\u0016`,
+ "\x17", `\u0017`,
+ "\x18", `\u0018`,
+ "\x19", `\u0019`,
+ "\x1a", `\u001a`,
+ "\x1b", `\u001b`,
+ "\x1c", `\u001c`,
+ "\x1d", `\u001d`,
+ "\x1e", `\u001e`,
+ "\x1f", `\u001f`,
+ "\x7f", `\u007f`,
+)
+
+var (
+ marshalToml = reflect.TypeOf((*Marshaler)(nil)).Elem()
+ marshalText = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
+ timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
+)
+
+// Marshaler is the interface implemented by types that can marshal themselves
+// into valid TOML.
+type Marshaler interface {
+ MarshalTOML() ([]byte, error)
+}
+
+// Marshal returns a TOML representation of the Go value.
+//
+// See [Encoder] for a description of the encoding process.
+func Marshal(v any) ([]byte, error) {
+ buff := new(bytes.Buffer)
+ if err := NewEncoder(buff).Encode(v); err != nil {
+ return nil, err
+ }
+ return buff.Bytes(), nil
+}
+
+// Encoder encodes a Go to a TOML document.
+//
+// The mapping between Go values and TOML values should be precisely the same as
+// for [Decode].
+//
+// time.Time is encoded as a RFC 3339 string, and time.Duration as its string
+// representation.
+//
+// The [Marshaler] and [encoding.TextMarshaler] interfaces are supported to
+// encoding the value as custom TOML.
+//
+// If you want to write arbitrary binary data then you will need to use
+// something like base64 since TOML does not have any binary types.
+//
+// When encoding TOML hashes (Go maps or structs), keys without any sub-hashes
+// are encoded first.
+//
+// Go maps will be sorted alphabetically by key for deterministic output.
+//
+// The toml struct tag can be used to provide the key name; if omitted the
+// struct field name will be used. If the "omitempty" option is present the
+// following value will be skipped:
+//
+// - arrays, slices, maps, and string with len of 0
+// - struct with all zero values
+// - bool false
+//
+// If omitzero is given all int and float types with a value of 0 will be
+// skipped.
+//
+// Encoding Go values without a corresponding TOML representation will return an
+// error. Examples of this includes maps with non-string keys, slices with nil
+// elements, embedded non-struct types, and nested slices containing maps or
+// structs. (e.g. [][]map[string]string is not allowed but []map[string]string
+// is okay, as is []map[string][]string).
+//
+// NOTE: only exported keys are encoded due to the use of reflection. Unexported
+// keys are silently discarded.
+type Encoder struct {
+ Indent string // string for a single indentation level; default is two spaces.
+ hasWritten bool // written any output to w yet?
+ w *bufio.Writer
+}
+
+// NewEncoder create a new Encoder.
+func NewEncoder(w io.Writer) *Encoder {
+ return &Encoder{w: bufio.NewWriter(w), Indent: " "}
+}
+
+// Encode writes a TOML representation of the Go value to the [Encoder]'s writer.
+//
+// An error is returned if the value given cannot be encoded to a valid TOML
+// document.
+func (enc *Encoder) Encode(v any) error {
+ rv := eindirect(reflect.ValueOf(v))
+ err := enc.safeEncode(Key([]string{}), rv)
+ if err != nil {
+ return err
+ }
+ return enc.w.Flush()
+}
+
+func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ if terr, ok := r.(tomlEncodeError); ok {
+ err = terr.error
+ return
+ }
+ panic(r)
+ }
+ }()
+ enc.encode(key, rv)
+ return nil
+}
+
+func (enc *Encoder) encode(key Key, rv reflect.Value) {
+ // If we can marshal the type to text, then we use that. This prevents the
+ // encoder for handling these types as generic structs (or whatever the
+ // underlying type of a TextMarshaler is).
+ switch {
+ case isMarshaler(rv):
+ enc.writeKeyValue(key, rv, false)
+ return
+ case rv.Type() == primitiveType: // TODO: #76 would make this superfluous after implemented.
+ enc.encode(key, reflect.ValueOf(rv.Interface().(Primitive).undecoded))
+ return
+ }
+
+ k := rv.Kind()
+ switch k {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
+ reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
+ reflect.Uint64,
+ reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
+ enc.writeKeyValue(key, rv, false)
+ case reflect.Array, reflect.Slice:
+ if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
+ enc.eArrayOfTables(key, rv)
+ } else {
+ enc.writeKeyValue(key, rv, false)
+ }
+ case reflect.Interface:
+ if rv.IsNil() {
+ return
+ }
+ enc.encode(key, rv.Elem())
+ case reflect.Map:
+ if rv.IsNil() {
+ return
+ }
+ enc.eTable(key, rv)
+ case reflect.Ptr:
+ if rv.IsNil() {
+ return
+ }
+ enc.encode(key, rv.Elem())
+ case reflect.Struct:
+ enc.eTable(key, rv)
+ default:
+ encPanic(fmt.Errorf("unsupported type for key '%s': %s", key, k))
+ }
+}
+
+// eElement encodes any value that can be an array element.
+func (enc *Encoder) eElement(rv reflect.Value) {
+ switch v := rv.Interface().(type) {
+ case time.Time: // Using TextMarshaler adds extra quotes, which we don't want.
+ format := time.RFC3339Nano
+ switch v.Location() {
+ case internal.LocalDatetime:
+ format = "2006-01-02T15:04:05.999999999"
+ case internal.LocalDate:
+ format = "2006-01-02"
+ case internal.LocalTime:
+ format = "15:04:05.999999999"
+ }
+ switch v.Location() {
+ default:
+ enc.wf(v.Format(format))
+ case internal.LocalDatetime, internal.LocalDate, internal.LocalTime:
+ enc.wf(v.In(time.UTC).Format(format))
+ }
+ return
+ case Marshaler:
+ s, err := v.MarshalTOML()
+ if err != nil {
+ encPanic(err)
+ }
+ if s == nil {
+ encPanic(errors.New("MarshalTOML returned nil and no error"))
+ }
+ enc.w.Write(s)
+ return
+ case encoding.TextMarshaler:
+ s, err := v.MarshalText()
+ if err != nil {
+ encPanic(err)
+ }
+ if s == nil {
+ encPanic(errors.New("MarshalText returned nil and no error"))
+ }
+ enc.writeQuoted(string(s))
+ return
+ case time.Duration:
+ enc.writeQuoted(v.String())
+ return
+ case json.Number:
+ n, _ := rv.Interface().(json.Number)
+
+ if n == "" { /// Useful zero value.
+ enc.w.WriteByte('0')
+ return
+ } else if v, err := n.Int64(); err == nil {
+ enc.eElement(reflect.ValueOf(v))
+ return
+ } else if v, err := n.Float64(); err == nil {
+ enc.eElement(reflect.ValueOf(v))
+ return
+ }
+ encPanic(fmt.Errorf("unable to convert %q to int64 or float64", n))
+ }
+
+ switch rv.Kind() {
+ case reflect.Ptr:
+ enc.eElement(rv.Elem())
+ return
+ case reflect.String:
+ enc.writeQuoted(rv.String())
+ case reflect.Bool:
+ enc.wf(strconv.FormatBool(rv.Bool()))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ enc.wf(strconv.FormatInt(rv.Int(), 10))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ enc.wf(strconv.FormatUint(rv.Uint(), 10))
+ case reflect.Float32:
+ f := rv.Float()
+ if math.IsNaN(f) {
+ if math.Signbit(f) {
+ enc.wf("-")
+ }
+ enc.wf("nan")
+ } else if math.IsInf(f, 0) {
+ if math.Signbit(f) {
+ enc.wf("-")
+ }
+ enc.wf("inf")
+ } else {
+ enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 32)))
+ }
+ case reflect.Float64:
+ f := rv.Float()
+ if math.IsNaN(f) {
+ if math.Signbit(f) {
+ enc.wf("-")
+ }
+ enc.wf("nan")
+ } else if math.IsInf(f, 0) {
+ if math.Signbit(f) {
+ enc.wf("-")
+ }
+ enc.wf("inf")
+ } else {
+ enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 64)))
+ }
+ case reflect.Array, reflect.Slice:
+ enc.eArrayOrSliceElement(rv)
+ case reflect.Struct:
+ enc.eStruct(nil, rv, true)
+ case reflect.Map:
+ enc.eMap(nil, rv, true)
+ case reflect.Interface:
+ enc.eElement(rv.Elem())
+ default:
+ encPanic(fmt.Errorf("unexpected type: %s", fmtType(rv.Interface())))
+ }
+}
+
+// By the TOML spec, all floats must have a decimal with at least one number on
+// either side.
+func floatAddDecimal(fstr string) string {
+ if !strings.Contains(fstr, ".") {
+ return fstr + ".0"
+ }
+ return fstr
+}
+
+func (enc *Encoder) writeQuoted(s string) {
+ enc.wf("\"%s\"", dblQuotedReplacer.Replace(s))
+}
+
+func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
+ length := rv.Len()
+ enc.wf("[")
+ for i := 0; i < length; i++ {
+ elem := eindirect(rv.Index(i))
+ enc.eElement(elem)
+ if i != length-1 {
+ enc.wf(", ")
+ }
+ }
+ enc.wf("]")
+}
+
+func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
+ if len(key) == 0 {
+ encPanic(errNoKey)
+ }
+ for i := 0; i < rv.Len(); i++ {
+ trv := eindirect(rv.Index(i))
+ if isNil(trv) {
+ continue
+ }
+ enc.newline()
+ enc.wf("%s[[%s]]", enc.indentStr(key), key)
+ enc.newline()
+ enc.eMapOrStruct(key, trv, false)
+ }
+}
+
+func (enc *Encoder) eTable(key Key, rv reflect.Value) {
+ if len(key) == 1 {
+ // Output an extra newline between top-level tables.
+ // (The newline isn't written if nothing else has been written though.)
+ enc.newline()
+ }
+ if len(key) > 0 {
+ enc.wf("%s[%s]", enc.indentStr(key), key)
+ enc.newline()
+ }
+ enc.eMapOrStruct(key, rv, false)
+}
+
+func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value, inline bool) {
+ switch rv.Kind() {
+ case reflect.Map:
+ enc.eMap(key, rv, inline)
+ case reflect.Struct:
+ enc.eStruct(key, rv, inline)
+ default:
+ // Should never happen?
+ panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
+ }
+}
+
+func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
+ rt := rv.Type()
+ if rt.Key().Kind() != reflect.String {
+ encPanic(errNonString)
+ }
+
+ // Sort keys so that we have deterministic output. And write keys directly
+ // underneath this key first, before writing sub-structs or sub-maps.
+ var mapKeysDirect, mapKeysSub []reflect.Value
+ for _, mapKey := range rv.MapKeys() {
+ if typeIsTable(tomlTypeOfGo(eindirect(rv.MapIndex(mapKey)))) {
+ mapKeysSub = append(mapKeysSub, mapKey)
+ } else {
+ mapKeysDirect = append(mapKeysDirect, mapKey)
+ }
+ }
+
+ writeMapKeys := func(mapKeys []reflect.Value, trailC bool) {
+ sort.Slice(mapKeys, func(i, j int) bool { return mapKeys[i].String() < mapKeys[j].String() })
+ for i, mapKey := range mapKeys {
+ val := eindirect(rv.MapIndex(mapKey))
+ if isNil(val) {
+ continue
+ }
+
+ if inline {
+ enc.writeKeyValue(Key{mapKey.String()}, val, true)
+ if trailC || i != len(mapKeys)-1 {
+ enc.wf(", ")
+ }
+ } else {
+ enc.encode(key.add(mapKey.String()), val)
+ }
+ }
+ }
+
+ if inline {
+ enc.wf("{")
+ }
+ writeMapKeys(mapKeysDirect, len(mapKeysSub) > 0)
+ writeMapKeys(mapKeysSub, false)
+ if inline {
+ enc.wf("}")
+ }
+}
+
+func pointerTo(t reflect.Type) reflect.Type {
+ if t.Kind() == reflect.Ptr {
+ return pointerTo(t.Elem())
+ }
+ return t
+}
+
+func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
+ // Write keys for fields directly under this key first, because if we write
+ // a field that creates a new table then all keys under it will be in that
+ // table (not the one we're writing here).
+ //
+ // Fields is a [][]int: for fieldsDirect this always has one entry (the
+ // struct index). For fieldsSub it contains two entries: the parent field
+ // index from tv, and the field indexes for the fields of the sub.
+ var (
+ rt = rv.Type()
+ fieldsDirect, fieldsSub [][]int
+ addFields func(rt reflect.Type, rv reflect.Value, start []int)
+ )
+ addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
+ for i := 0; i < rt.NumField(); i++ {
+ f := rt.Field(i)
+ isEmbed := f.Anonymous && pointerTo(f.Type).Kind() == reflect.Struct
+ if f.PkgPath != "" && !isEmbed { /// Skip unexported fields.
+ continue
+ }
+ opts := getOptions(f.Tag)
+ if opts.skip {
+ continue
+ }
+
+ frv := eindirect(rv.Field(i))
+
+ // Need to make a copy because ... ehm, I don't know why... I guess
+ // allocating a new array can cause it to fail(?)
+ //
+ // Done for: https://github.com/BurntSushi/toml/issues/430
+ // Previously only on 32bit for: https://github.com/BurntSushi/toml/issues/314
+ copyStart := make([]int, len(start))
+ copy(copyStart, start)
+ start = copyStart
+
+ // Treat anonymous struct fields with tag names as though they are
+ // not anonymous, like encoding/json does.
+ //
+ // Non-struct anonymous fields use the normal encoding logic.
+ if isEmbed {
+ if getOptions(f.Tag).name == "" && frv.Kind() == reflect.Struct {
+ addFields(frv.Type(), frv, append(start, f.Index...))
+ continue
+ }
+ }
+
+ if typeIsTable(tomlTypeOfGo(frv)) {
+ fieldsSub = append(fieldsSub, append(start, f.Index...))
+ } else {
+ fieldsDirect = append(fieldsDirect, append(start, f.Index...))
+ }
+ }
+ }
+ addFields(rt, rv, nil)
+
+ writeFields := func(fields [][]int, totalFields int) {
+ for _, fieldIndex := range fields {
+ fieldType := rt.FieldByIndex(fieldIndex)
+ fieldVal := rv.FieldByIndex(fieldIndex)
+
+ opts := getOptions(fieldType.Tag)
+ if opts.skip {
+ continue
+ }
+ if opts.omitempty && isEmpty(fieldVal) {
+ continue
+ }
+
+ fieldVal = eindirect(fieldVal)
+
+ if isNil(fieldVal) { /// Don't write anything for nil fields.
+ continue
+ }
+
+ keyName := fieldType.Name
+ if opts.name != "" {
+ keyName = opts.name
+ }
+
+ if opts.omitzero && isZero(fieldVal) {
+ continue
+ }
+
+ if inline {
+ enc.writeKeyValue(Key{keyName}, fieldVal, true)
+ if fieldIndex[0] != totalFields-1 {
+ enc.wf(", ")
+ }
+ } else {
+ enc.encode(key.add(keyName), fieldVal)
+ }
+ }
+ }
+
+ if inline {
+ enc.wf("{")
+ }
+
+ l := len(fieldsDirect) + len(fieldsSub)
+ writeFields(fieldsDirect, l)
+ writeFields(fieldsSub, l)
+ if inline {
+ enc.wf("}")
+ }
+}
+
+// tomlTypeOfGo returns the TOML type name of the Go value's type.
+//
+// It is used to determine whether the types of array elements are mixed (which
+// is forbidden). If the Go value is nil, then it is illegal for it to be an
+// array element, and valueIsNil is returned as true.
+//
+// The type may be `nil`, which means no concrete TOML type could be found.
+func tomlTypeOfGo(rv reflect.Value) tomlType {
+ if isNil(rv) || !rv.IsValid() {
+ return nil
+ }
+
+ if rv.Kind() == reflect.Struct {
+ if rv.Type() == timeType {
+ return tomlDatetime
+ }
+ if isMarshaler(rv) {
+ return tomlString
+ }
+ return tomlHash
+ }
+
+ if isMarshaler(rv) {
+ return tomlString
+ }
+
+ switch rv.Kind() {
+ case reflect.Bool:
+ return tomlBool
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
+ reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
+ reflect.Uint64:
+ return tomlInteger
+ case reflect.Float32, reflect.Float64:
+ return tomlFloat
+ case reflect.Array, reflect.Slice:
+ if isTableArray(rv) {
+ return tomlArrayHash
+ }
+ return tomlArray
+ case reflect.Ptr, reflect.Interface:
+ return tomlTypeOfGo(rv.Elem())
+ case reflect.String:
+ return tomlString
+ case reflect.Map:
+ return tomlHash
+ default:
+ encPanic(errors.New("unsupported type: " + rv.Kind().String()))
+ panic("unreachable")
+ }
+}
+
+func isMarshaler(rv reflect.Value) bool {
+ return rv.Type().Implements(marshalText) || rv.Type().Implements(marshalToml)
+}
+
+// isTableArray reports if all entries in the array or slice are a table.
+func isTableArray(arr reflect.Value) bool {
+ if isNil(arr) || !arr.IsValid() || arr.Len() == 0 {
+ return false
+ }
+
+ ret := true
+ for i := 0; i < arr.Len(); i++ {
+ tt := tomlTypeOfGo(eindirect(arr.Index(i)))
+ // Don't allow nil.
+ if tt == nil {
+ encPanic(errArrayNilElement)
+ }
+
+ if ret && !typeEqual(tomlHash, tt) {
+ ret = false
+ }
+ }
+ return ret
+}
+
+type tagOptions struct {
+ skip bool // "-"
+ name string
+ omitempty bool
+ omitzero bool
+}
+
+func getOptions(tag reflect.StructTag) tagOptions {
+ t := tag.Get("toml")
+ if t == "-" {
+ return tagOptions{skip: true}
+ }
+ var opts tagOptions
+ parts := strings.Split(t, ",")
+ opts.name = parts[0]
+ for _, s := range parts[1:] {
+ switch s {
+ case "omitempty":
+ opts.omitempty = true
+ case "omitzero":
+ opts.omitzero = true
+ }
+ }
+ return opts
+}
+
+func isZero(rv reflect.Value) bool {
+ switch rv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return rv.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return rv.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return rv.Float() == 0.0
+ }
+ return false
+}
+
+func isEmpty(rv reflect.Value) bool {
+ switch rv.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
+ return rv.Len() == 0
+ case reflect.Struct:
+ if rv.Type().Comparable() {
+ return reflect.Zero(rv.Type()).Interface() == rv.Interface()
+ }
+ // Need to also check if all the fields are empty, otherwise something
+ // like this with uncomparable types will always return true:
+ //
+ // type a struct{ field b }
+ // type b struct{ s []string }
+ // s := a{field: b{s: []string{"AAA"}}}
+ for i := 0; i < rv.NumField(); i++ {
+ if !isEmpty(rv.Field(i)) {
+ return false
+ }
+ }
+ return true
+ case reflect.Bool:
+ return !rv.Bool()
+ case reflect.Ptr:
+ return rv.IsNil()
+ }
+ return false
+}
+
+func (enc *Encoder) newline() {
+ if enc.hasWritten {
+ enc.wf("\n")
+ }
+}
+
+// Write a key/value pair:
+//
+// key =
+//
+// This is also used for "k = v" in inline tables; so something like this will
+// be written in three calls:
+//
+// ┌───────────────────┐
+// │ ┌───┐ ┌────┐│
+// v v v v vv
+// key = {k = 1, k2 = 2}
+func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) {
+ /// Marshaler used on top-level document; call eElement() to just call
+ /// Marshal{TOML,Text}.
+ if len(key) == 0 {
+ enc.eElement(val)
+ return
+ }
+ enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
+ enc.eElement(val)
+ if !inline {
+ enc.newline()
+ }
+}
+
+func (enc *Encoder) wf(format string, v ...any) {
+ _, err := fmt.Fprintf(enc.w, format, v...)
+ if err != nil {
+ encPanic(err)
+ }
+ enc.hasWritten = true
+}
+
+func (enc *Encoder) indentStr(key Key) string {
+ return strings.Repeat(enc.Indent, len(key)-1)
+}
+
+func encPanic(err error) {
+ panic(tomlEncodeError{err})
+}
+
+// Resolve any level of pointers to the actual value (e.g. **string → string).
+func eindirect(v reflect.Value) reflect.Value {
+ if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
+ if isMarshaler(v) {
+ return v
+ }
+ if v.CanAddr() { /// Special case for marshalers; see #358.
+ if pv := v.Addr(); isMarshaler(pv) {
+ return pv
+ }
+ }
+ return v
+ }
+
+ if v.IsNil() {
+ return v
+ }
+
+ return eindirect(v.Elem())
+}
+
+func isNil(rv reflect.Value) bool {
+ switch rv.Kind() {
+ case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+ return rv.IsNil()
+ default:
+ return false
+ }
+}
diff --git a/vendor/github.com/BurntSushi/toml/error.go b/vendor/github.com/BurntSushi/toml/error.go
new file mode 100644
index 00000000..b7077d3a
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/error.go
@@ -0,0 +1,347 @@
+package toml
+
+import (
+ "fmt"
+ "strings"
+)
+
+// ParseError is returned when there is an error parsing the TOML syntax such as
+// invalid syntax, duplicate keys, etc.
+//
+// In addition to the error message itself, you can also print detailed location
+// information with context by using [ErrorWithPosition]:
+//
+// toml: error: Key 'fruit' was already created and cannot be used as an array.
+//
+// At line 4, column 2-7:
+//
+// 2 | fruit = []
+// 3 |
+// 4 | [[fruit]] # Not allowed
+// ^^^^^
+//
+// [ErrorWithUsage] can be used to print the above with some more detailed usage
+// guidance:
+//
+// toml: error: newlines not allowed within inline tables
+//
+// At line 1, column 18:
+//
+// 1 | x = [{ key = 42 #
+// ^
+//
+// Error help:
+//
+// Inline tables must always be on a single line:
+//
+// table = {key = 42, second = 43}
+//
+// It is invalid to split them over multiple lines like so:
+//
+// # INVALID
+// table = {
+// key = 42,
+// second = 43
+// }
+//
+// Use regular for this:
+//
+// [table]
+// key = 42
+// second = 43
+type ParseError struct {
+ Message string // Short technical message.
+ Usage string // Longer message with usage guidance; may be blank.
+ Position Position // Position of the error
+ LastKey string // Last parsed key, may be blank.
+
+ // Line the error occurred.
+ //
+ // Deprecated: use [Position].
+ Line int
+
+ err error
+ input string
+}
+
+// Position of an error.
+type Position struct {
+ Line int // Line number, starting at 1.
+ Col int // Error column, starting at 1.
+ Start int // Start of error, as byte offset starting at 0.
+ Len int // Length of the error in bytes.
+}
+
+func (p Position) withCol(tomlFile string) Position {
+ var (
+ pos int
+ lines = strings.Split(tomlFile, "\n")
+ )
+ for i := range lines {
+ ll := len(lines[i]) + 1 // +1 for the removed newline
+ if pos+ll >= p.Start {
+ p.Col = p.Start - pos + 1
+ if p.Col < 1 { // Should never happen, but just in case.
+ p.Col = 1
+ }
+ break
+ }
+ pos += ll
+ }
+ return p
+}
+
+func (pe ParseError) Error() string {
+ if pe.LastKey == "" {
+ return fmt.Sprintf("toml: line %d: %s", pe.Position.Line, pe.Message)
+ }
+ return fmt.Sprintf("toml: line %d (last key %q): %s",
+ pe.Position.Line, pe.LastKey, pe.Message)
+}
+
+// ErrorWithPosition returns the error with detailed location context.
+//
+// See the documentation on [ParseError].
+func (pe ParseError) ErrorWithPosition() string {
+ if pe.input == "" { // Should never happen, but just in case.
+ return pe.Error()
+ }
+
+ // TODO: don't show control characters as literals? This may not show up
+ // well everywhere.
+
+ var (
+ lines = strings.Split(pe.input, "\n")
+ b = new(strings.Builder)
+ )
+ if pe.Position.Len == 1 {
+ fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d:\n\n",
+ pe.Message, pe.Position.Line, pe.Position.Col)
+ } else {
+ fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d-%d:\n\n",
+ pe.Message, pe.Position.Line, pe.Position.Col, pe.Position.Col+pe.Position.Len-1)
+ }
+ if pe.Position.Line > 2 {
+ fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-2, expandTab(lines[pe.Position.Line-3]))
+ }
+ if pe.Position.Line > 1 {
+ fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-1, expandTab(lines[pe.Position.Line-2]))
+ }
+
+ /// Expand tabs, so that the ^^^s are at the correct position, but leave
+ /// "column 10-13" intact. Adjusting this to the visual column would be
+ /// better, but we don't know the tabsize of the user in their editor, which
+ /// can be 8, 4, 2, or something else. We can't know. So leaving it as the
+ /// character index is probably the "most correct".
+ expanded := expandTab(lines[pe.Position.Line-1])
+ diff := len(expanded) - len(lines[pe.Position.Line-1])
+
+ fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line, expanded)
+ fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", pe.Position.Col-1+diff), strings.Repeat("^", pe.Position.Len))
+ return b.String()
+}
+
+// ErrorWithUsage returns the error with detailed location context and usage
+// guidance.
+//
+// See the documentation on [ParseError].
+func (pe ParseError) ErrorWithUsage() string {
+ m := pe.ErrorWithPosition()
+ if u, ok := pe.err.(interface{ Usage() string }); ok && u.Usage() != "" {
+ lines := strings.Split(strings.TrimSpace(u.Usage()), "\n")
+ for i := range lines {
+ if lines[i] != "" {
+ lines[i] = " " + lines[i]
+ }
+ }
+ return m + "Error help:\n\n" + strings.Join(lines, "\n") + "\n"
+ }
+ return m
+}
+
+func expandTab(s string) string {
+ var (
+ b strings.Builder
+ l int
+ fill = func(n int) string {
+ b := make([]byte, n)
+ for i := range b {
+ b[i] = ' '
+ }
+ return string(b)
+ }
+ )
+ b.Grow(len(s))
+ for _, r := range s {
+ switch r {
+ case '\t':
+ tw := 8 - l%8
+ b.WriteString(fill(tw))
+ l += tw
+ default:
+ b.WriteRune(r)
+ l += 1
+ }
+ }
+ return b.String()
+}
+
+type (
+ errLexControl struct{ r rune }
+ errLexEscape struct{ r rune }
+ errLexUTF8 struct{ b byte }
+ errParseDate struct{ v string }
+ errLexInlineTableNL struct{}
+ errLexStringNL struct{}
+ errParseRange struct {
+ i any // int or float
+ size string // "int64", "uint16", etc.
+ }
+ errUnsafeFloat struct {
+ i interface{} // float32 or float64
+ size string // "float32" or "float64"
+ }
+ errParseDuration struct{ d string }
+)
+
+func (e errLexControl) Error() string {
+ return fmt.Sprintf("TOML files cannot contain control characters: '0x%02x'", e.r)
+}
+func (e errLexControl) Usage() string { return "" }
+
+func (e errLexEscape) Error() string { return fmt.Sprintf(`invalid escape in string '\%c'`, e.r) }
+func (e errLexEscape) Usage() string { return usageEscape }
+func (e errLexUTF8) Error() string { return fmt.Sprintf("invalid UTF-8 byte: 0x%02x", e.b) }
+func (e errLexUTF8) Usage() string { return "" }
+func (e errParseDate) Error() string { return fmt.Sprintf("invalid datetime: %q", e.v) }
+func (e errParseDate) Usage() string { return usageDate }
+func (e errLexInlineTableNL) Error() string { return "newlines not allowed within inline tables" }
+func (e errLexInlineTableNL) Usage() string { return usageInlineNewline }
+func (e errLexStringNL) Error() string { return "strings cannot contain newlines" }
+func (e errLexStringNL) Usage() string { return usageStringNewline }
+func (e errParseRange) Error() string { return fmt.Sprintf("%v is out of range for %s", e.i, e.size) }
+func (e errParseRange) Usage() string { return usageIntOverflow }
+func (e errUnsafeFloat) Error() string {
+ return fmt.Sprintf("%v is out of the safe %s range", e.i, e.size)
+}
+func (e errUnsafeFloat) Usage() string { return usageUnsafeFloat }
+func (e errParseDuration) Error() string { return fmt.Sprintf("invalid duration: %q", e.d) }
+func (e errParseDuration) Usage() string { return usageDuration }
+
+const usageEscape = `
+A '\' inside a "-delimited string is interpreted as an escape character.
+
+The following escape sequences are supported:
+\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX
+
+To prevent a '\' from being recognized as an escape character, use either:
+
+- a ' or '''-delimited string; escape characters aren't processed in them; or
+- write two backslashes to get a single backslash: '\\'.
+
+If you're trying to add a Windows path (e.g. "C:\Users\martin") then using '/'
+instead of '\' will usually also work: "C:/Users/martin".
+`
+
+const usageInlineNewline = `
+Inline tables must always be on a single line:
+
+ table = {key = 42, second = 43}
+
+It is invalid to split them over multiple lines like so:
+
+ # INVALID
+ table = {
+ key = 42,
+ second = 43
+ }
+
+Use regular for this:
+
+ [table]
+ key = 42
+ second = 43
+`
+
+const usageStringNewline = `
+Strings must always be on a single line, and cannot span more than one line:
+
+ # INVALID
+ string = "Hello,
+ world!"
+
+Instead use """ or ''' to split strings over multiple lines:
+
+ string = """Hello,
+ world!"""
+`
+
+const usageIntOverflow = `
+This number is too large; this may be an error in the TOML, but it can also be a
+bug in the program that uses too small of an integer.
+
+The maximum and minimum values are:
+
+ size │ lowest │ highest
+ ───────┼────────────────┼──────────────
+ int8 │ -128 │ 127
+ int16 │ -32,768 │ 32,767
+ int32 │ -2,147,483,648 │ 2,147,483,647
+ int64 │ -9.2 × 10¹⁷ │ 9.2 × 10¹⁷
+ uint8 │ 0 │ 255
+ uint16 │ 0 │ 65,535
+ uint32 │ 0 │ 4,294,967,295
+ uint64 │ 0 │ 1.8 × 10¹⁸
+
+int refers to int32 on 32-bit systems and int64 on 64-bit systems.
+`
+
+const usageUnsafeFloat = `
+This number is outside of the "safe" range for floating point numbers; whole
+(non-fractional) numbers outside the below range can not always be represented
+accurately in a float, leading to some loss of accuracy.
+
+Explicitly mark a number as a fractional unit by adding ".0", which will incur
+some loss of accuracy; for example:
+
+ f = 2_000_000_000.0
+
+Accuracy ranges:
+
+ float32 = 16,777,215
+ float64 = 9,007,199,254,740,991
+`
+
+const usageDuration = `
+A duration must be as "number", without any spaces. Valid units are:
+
+ ns nanoseconds (billionth of a second)
+ us, µs microseconds (millionth of a second)
+ ms milliseconds (thousands of a second)
+ s seconds
+ m minutes
+ h hours
+
+You can combine multiple units; for example "5m10s" for 5 minutes and 10
+seconds.
+`
+
+const usageDate = `
+A TOML datetime must be in one of the following formats:
+
+ 2006-01-02T15:04:05Z07:00 Date and time, with timezone.
+ 2006-01-02T15:04:05 Date and time, but without timezone.
+ 2006-01-02 Date without a time or timezone.
+ 15:04:05 Just a time, without any timezone.
+
+Seconds may optionally have a fraction, up to nanosecond precision:
+
+ 15:04:05.123
+ 15:04:05.856018510
+`
+
+// TOML 1.1:
+// The seconds part in times is optional, and may be omitted:
+// 2006-01-02T15:04Z07:00
+// 2006-01-02T15:04
+// 15:04
diff --git a/vendor/github.com/BurntSushi/toml/internal/tz.go b/vendor/github.com/BurntSushi/toml/internal/tz.go
new file mode 100644
index 00000000..022f15bc
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/internal/tz.go
@@ -0,0 +1,36 @@
+package internal
+
+import "time"
+
+// Timezones used for local datetime, date, and time TOML types.
+//
+// The exact way times and dates without a timezone should be interpreted is not
+// well-defined in the TOML specification and left to the implementation. These
+// defaults to current local timezone offset of the computer, but this can be
+// changed by changing these variables before decoding.
+//
+// TODO:
+// Ideally we'd like to offer people the ability to configure the used timezone
+// by setting Decoder.Timezone and Encoder.Timezone; however, this is a bit
+// tricky: the reason we use three different variables for this is to support
+// round-tripping – without these specific TZ names we wouldn't know which
+// format to use.
+//
+// There isn't a good way to encode this right now though, and passing this sort
+// of information also ties in to various related issues such as string format
+// encoding, encoding of comments, etc.
+//
+// So, for the time being, just put this in internal until we can write a good
+// comprehensive API for doing all of this.
+//
+// The reason they're exported is because they're referred from in e.g.
+// internal/tag.
+//
+// Note that this behaviour is valid according to the TOML spec as the exact
+// behaviour is left up to implementations.
+var (
+ localOffset = func() int { _, o := time.Now().Zone(); return o }()
+ LocalDatetime = time.FixedZone("datetime-local", localOffset)
+ LocalDate = time.FixedZone("date-local", localOffset)
+ LocalTime = time.FixedZone("time-local", localOffset)
+)
diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go
new file mode 100644
index 00000000..1c3b4770
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/lex.go
@@ -0,0 +1,1272 @@
+package toml
+
+import (
+ "fmt"
+ "reflect"
+ "runtime"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+type itemType int
+
+const (
+ itemError itemType = iota
+ itemNIL // used in the parser to indicate no type
+ itemEOF
+ itemText
+ itemString
+ itemStringEsc
+ itemRawString
+ itemMultilineString
+ itemRawMultilineString
+ itemBool
+ itemInteger
+ itemFloat
+ itemDatetime
+ itemArray // the start of an array
+ itemArrayEnd
+ itemTableStart
+ itemTableEnd
+ itemArrayTableStart
+ itemArrayTableEnd
+ itemKeyStart
+ itemKeyEnd
+ itemCommentStart
+ itemInlineTableStart
+ itemInlineTableEnd
+)
+
+const eof = 0
+
+type stateFn func(lx *lexer) stateFn
+
+func (p Position) String() string {
+ return fmt.Sprintf("at line %d; start %d; length %d", p.Line, p.Start, p.Len)
+}
+
+type lexer struct {
+ input string
+ start int
+ pos int
+ line int
+ state stateFn
+ items chan item
+ tomlNext bool
+ esc bool
+
+ // Allow for backing up up to 4 runes. This is necessary because TOML
+ // contains 3-rune tokens (""" and ''').
+ prevWidths [4]int
+ nprev int // how many of prevWidths are in use
+ atEOF bool // If we emit an eof, we can still back up, but it is not OK to call next again.
+
+ // A stack of state functions used to maintain context.
+ //
+ // The idea is to reuse parts of the state machine in various places. For
+ // example, values can appear at the top level or within arbitrarily nested
+ // arrays. The last state on the stack is used after a value has been lexed.
+ // Similarly for comments.
+ stack []stateFn
+}
+
+type item struct {
+ typ itemType
+ val string
+ err error
+ pos Position
+}
+
+func (lx *lexer) nextItem() item {
+ for {
+ select {
+ case item := <-lx.items:
+ return item
+ default:
+ lx.state = lx.state(lx)
+ //fmt.Printf(" STATE %-24s current: %-10s stack: %s\n", lx.state, lx.current(), lx.stack)
+ }
+ }
+}
+
+func lex(input string, tomlNext bool) *lexer {
+ lx := &lexer{
+ input: input,
+ state: lexTop,
+ items: make(chan item, 10),
+ stack: make([]stateFn, 0, 10),
+ line: 1,
+ tomlNext: tomlNext,
+ }
+ return lx
+}
+
+func (lx *lexer) push(state stateFn) {
+ lx.stack = append(lx.stack, state)
+}
+
+func (lx *lexer) pop() stateFn {
+ if len(lx.stack) == 0 {
+ return lx.errorf("BUG in lexer: no states to pop")
+ }
+ last := lx.stack[len(lx.stack)-1]
+ lx.stack = lx.stack[0 : len(lx.stack)-1]
+ return last
+}
+
+func (lx *lexer) current() string {
+ return lx.input[lx.start:lx.pos]
+}
+
+func (lx lexer) getPos() Position {
+ p := Position{
+ Line: lx.line,
+ Start: lx.start,
+ Len: lx.pos - lx.start,
+ }
+ if p.Len <= 0 {
+ p.Len = 1
+ }
+ return p
+}
+
+func (lx *lexer) emit(typ itemType) {
+ // Needed for multiline strings ending with an incomplete UTF-8 sequence.
+ if lx.start > lx.pos {
+ lx.error(errLexUTF8{lx.input[lx.pos]})
+ return
+ }
+ lx.items <- item{typ: typ, pos: lx.getPos(), val: lx.current()}
+ lx.start = lx.pos
+}
+
+func (lx *lexer) emitTrim(typ itemType) {
+ lx.items <- item{typ: typ, pos: lx.getPos(), val: strings.TrimSpace(lx.current())}
+ lx.start = lx.pos
+}
+
+func (lx *lexer) next() (r rune) {
+ if lx.atEOF {
+ panic("BUG in lexer: next called after EOF")
+ }
+ if lx.pos >= len(lx.input) {
+ lx.atEOF = true
+ return eof
+ }
+
+ if lx.input[lx.pos] == '\n' {
+ lx.line++
+ }
+ lx.prevWidths[3] = lx.prevWidths[2]
+ lx.prevWidths[2] = lx.prevWidths[1]
+ lx.prevWidths[1] = lx.prevWidths[0]
+ if lx.nprev < 4 {
+ lx.nprev++
+ }
+
+ r, w := utf8.DecodeRuneInString(lx.input[lx.pos:])
+ if r == utf8.RuneError && w == 1 {
+ lx.error(errLexUTF8{lx.input[lx.pos]})
+ return utf8.RuneError
+ }
+
+ // Note: don't use peek() here, as this calls next().
+ if isControl(r) || (r == '\r' && (len(lx.input)-1 == lx.pos || lx.input[lx.pos+1] != '\n')) {
+ lx.errorControlChar(r)
+ return utf8.RuneError
+ }
+
+ lx.prevWidths[0] = w
+ lx.pos += w
+ return r
+}
+
+// ignore skips over the pending input before this point.
+func (lx *lexer) ignore() {
+ lx.start = lx.pos
+}
+
+// backup steps back one rune. Can be called 4 times between calls to next.
+func (lx *lexer) backup() {
+ if lx.atEOF {
+ lx.atEOF = false
+ return
+ }
+ if lx.nprev < 1 {
+ panic("BUG in lexer: backed up too far")
+ }
+ w := lx.prevWidths[0]
+ lx.prevWidths[0] = lx.prevWidths[1]
+ lx.prevWidths[1] = lx.prevWidths[2]
+ lx.prevWidths[2] = lx.prevWidths[3]
+ lx.nprev--
+
+ lx.pos -= w
+ if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' {
+ lx.line--
+ }
+}
+
+// accept consumes the next rune if it's equal to `valid`.
+func (lx *lexer) accept(valid rune) bool {
+ if lx.next() == valid {
+ return true
+ }
+ lx.backup()
+ return false
+}
+
+// peek returns but does not consume the next rune in the input.
+func (lx *lexer) peek() rune {
+ r := lx.next()
+ lx.backup()
+ return r
+}
+
+// skip ignores all input that matches the given predicate.
+func (lx *lexer) skip(pred func(rune) bool) {
+ for {
+ r := lx.next()
+ if pred(r) {
+ continue
+ }
+ lx.backup()
+ lx.ignore()
+ return
+ }
+}
+
+// error stops all lexing by emitting an error and returning `nil`.
+//
+// Note that any value that is a character is escaped if it's a special
+// character (newlines, tabs, etc.).
+func (lx *lexer) error(err error) stateFn {
+ if lx.atEOF {
+ return lx.errorPrevLine(err)
+ }
+ lx.items <- item{typ: itemError, pos: lx.getPos(), err: err}
+ return nil
+}
+
+// errorfPrevline is like error(), but sets the position to the last column of
+// the previous line.
+//
+// This is so that unexpected EOF or NL errors don't show on a new blank line.
+func (lx *lexer) errorPrevLine(err error) stateFn {
+ pos := lx.getPos()
+ pos.Line--
+ pos.Len = 1
+ pos.Start = lx.pos - 1
+ lx.items <- item{typ: itemError, pos: pos, err: err}
+ return nil
+}
+
+// errorPos is like error(), but allows explicitly setting the position.
+func (lx *lexer) errorPos(start, length int, err error) stateFn {
+ pos := lx.getPos()
+ pos.Start = start
+ pos.Len = length
+ lx.items <- item{typ: itemError, pos: pos, err: err}
+ return nil
+}
+
+// errorf is like error, and creates a new error.
+func (lx *lexer) errorf(format string, values ...any) stateFn {
+ if lx.atEOF {
+ pos := lx.getPos()
+ if lx.pos >= 1 && lx.input[lx.pos-1] == '\n' {
+ pos.Line--
+ }
+ pos.Len = 1
+ pos.Start = lx.pos - 1
+ lx.items <- item{typ: itemError, pos: pos, err: fmt.Errorf(format, values...)}
+ return nil
+ }
+ lx.items <- item{typ: itemError, pos: lx.getPos(), err: fmt.Errorf(format, values...)}
+ return nil
+}
+
+func (lx *lexer) errorControlChar(cc rune) stateFn {
+ return lx.errorPos(lx.pos-1, 1, errLexControl{cc})
+}
+
+// lexTop consumes elements at the top level of TOML data.
+func lexTop(lx *lexer) stateFn {
+ r := lx.next()
+ if isWhitespace(r) || isNL(r) {
+ return lexSkip(lx, lexTop)
+ }
+ switch r {
+ case '#':
+ lx.push(lexTop)
+ return lexCommentStart
+ case '[':
+ return lexTableStart
+ case eof:
+ if lx.pos > lx.start {
+ return lx.errorf("unexpected EOF")
+ }
+ lx.emit(itemEOF)
+ return nil
+ }
+
+ // At this point, the only valid item can be a key, so we back up
+ // and let the key lexer do the rest.
+ lx.backup()
+ lx.push(lexTopEnd)
+ return lexKeyStart
+}
+
+// lexTopEnd is entered whenever a top-level item has been consumed. (A value
+// or a table.) It must see only whitespace, and will turn back to lexTop
+// upon a newline. If it sees EOF, it will quit the lexer successfully.
+func lexTopEnd(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case r == '#':
+ // a comment will read to a newline for us.
+ lx.push(lexTop)
+ return lexCommentStart
+ case isWhitespace(r):
+ return lexTopEnd
+ case isNL(r):
+ lx.ignore()
+ return lexTop
+ case r == eof:
+ lx.emit(itemEOF)
+ return nil
+ }
+ return lx.errorf("expected a top-level item to end with a newline, comment, or EOF, but got %q instead", r)
+}
+
+// lexTable lexes the beginning of a table. Namely, it makes sure that
+// it starts with a character other than '.' and ']'.
+// It assumes that '[' has already been consumed.
+// It also handles the case that this is an item in an array of tables.
+// e.g., '[[name]]'.
+func lexTableStart(lx *lexer) stateFn {
+ if lx.peek() == '[' {
+ lx.next()
+ lx.emit(itemArrayTableStart)
+ lx.push(lexArrayTableEnd)
+ } else {
+ lx.emit(itemTableStart)
+ lx.push(lexTableEnd)
+ }
+ return lexTableNameStart
+}
+
+func lexTableEnd(lx *lexer) stateFn {
+ lx.emit(itemTableEnd)
+ return lexTopEnd
+}
+
+func lexArrayTableEnd(lx *lexer) stateFn {
+ if r := lx.next(); r != ']' {
+ return lx.errorf("expected end of table array name delimiter ']', but got %q instead", r)
+ }
+ lx.emit(itemArrayTableEnd)
+ return lexTopEnd
+}
+
+func lexTableNameStart(lx *lexer) stateFn {
+ lx.skip(isWhitespace)
+ switch r := lx.peek(); {
+ case r == ']' || r == eof:
+ return lx.errorf("unexpected end of table name (table names cannot be empty)")
+ case r == '.':
+ return lx.errorf("unexpected table separator (table names cannot be empty)")
+ case r == '"' || r == '\'':
+ lx.ignore()
+ lx.push(lexTableNameEnd)
+ return lexQuotedName
+ default:
+ lx.push(lexTableNameEnd)
+ return lexBareName
+ }
+}
+
+// lexTableNameEnd reads the end of a piece of a table name, optionally
+// consuming whitespace.
+func lexTableNameEnd(lx *lexer) stateFn {
+ lx.skip(isWhitespace)
+ switch r := lx.next(); {
+ case isWhitespace(r):
+ return lexTableNameEnd
+ case r == '.':
+ lx.ignore()
+ return lexTableNameStart
+ case r == ']':
+ return lx.pop()
+ default:
+ return lx.errorf("expected '.' or ']' to end table name, but got %q instead", r)
+ }
+}
+
+// lexBareName lexes one part of a key or table.
+//
+// It assumes that at least one valid character for the table has already been
+// read.
+//
+// Lexes only one part, e.g. only 'a' inside 'a.b'.
+func lexBareName(lx *lexer) stateFn {
+ r := lx.next()
+ if isBareKeyChar(r, lx.tomlNext) {
+ return lexBareName
+ }
+ lx.backup()
+ lx.emit(itemText)
+ return lx.pop()
+}
+
+// lexBareName lexes one part of a key or table.
+//
+// It assumes that at least one valid character for the table has already been
+// read.
+//
+// Lexes only one part, e.g. only '"a"' inside '"a".b'.
+func lexQuotedName(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case isWhitespace(r):
+ return lexSkip(lx, lexValue)
+ case r == '"':
+ lx.ignore() // ignore the '"'
+ return lexString
+ case r == '\'':
+ lx.ignore() // ignore the "'"
+ return lexRawString
+ case r == eof:
+ return lx.errorf("unexpected EOF; expected value")
+ default:
+ return lx.errorf("expected value but found %q instead", r)
+ }
+}
+
+// lexKeyStart consumes all key parts until a '='.
+func lexKeyStart(lx *lexer) stateFn {
+ lx.skip(isWhitespace)
+ switch r := lx.peek(); {
+ case r == '=' || r == eof:
+ return lx.errorf("unexpected '=': key name appears blank")
+ case r == '.':
+ return lx.errorf("unexpected '.': keys cannot start with a '.'")
+ case r == '"' || r == '\'':
+ lx.ignore()
+ fallthrough
+ default: // Bare key
+ lx.emit(itemKeyStart)
+ return lexKeyNameStart
+ }
+}
+
+func lexKeyNameStart(lx *lexer) stateFn {
+ lx.skip(isWhitespace)
+ switch r := lx.peek(); {
+ case r == '=' || r == eof:
+ return lx.errorf("unexpected '='")
+ case r == '.':
+ return lx.errorf("unexpected '.'")
+ case r == '"' || r == '\'':
+ lx.ignore()
+ lx.push(lexKeyEnd)
+ return lexQuotedName
+ default:
+ lx.push(lexKeyEnd)
+ return lexBareName
+ }
+}
+
+// lexKeyEnd consumes the end of a key and trims whitespace (up to the key
+// separator).
+func lexKeyEnd(lx *lexer) stateFn {
+ lx.skip(isWhitespace)
+ switch r := lx.next(); {
+ case isWhitespace(r):
+ return lexSkip(lx, lexKeyEnd)
+ case r == eof:
+ return lx.errorf("unexpected EOF; expected key separator '='")
+ case r == '.':
+ lx.ignore()
+ return lexKeyNameStart
+ case r == '=':
+ lx.emit(itemKeyEnd)
+ return lexSkip(lx, lexValue)
+ default:
+ if r == '\n' {
+ return lx.errorPrevLine(fmt.Errorf("expected '.' or '=', but got %q instead", r))
+ }
+ return lx.errorf("expected '.' or '=', but got %q instead", r)
+ }
+}
+
+// lexValue starts the consumption of a value anywhere a value is expected.
+// lexValue will ignore whitespace.
+// After a value is lexed, the last state on the next is popped and returned.
+func lexValue(lx *lexer) stateFn {
+ // We allow whitespace to precede a value, but NOT newlines.
+ // In array syntax, the array states are responsible for ignoring newlines.
+ r := lx.next()
+ switch {
+ case isWhitespace(r):
+ return lexSkip(lx, lexValue)
+ case isDigit(r):
+ lx.backup() // avoid an extra state and use the same as above
+ return lexNumberOrDateStart
+ }
+ switch r {
+ case '[':
+ lx.ignore()
+ lx.emit(itemArray)
+ return lexArrayValue
+ case '{':
+ lx.ignore()
+ lx.emit(itemInlineTableStart)
+ return lexInlineTableValue
+ case '"':
+ if lx.accept('"') {
+ if lx.accept('"') {
+ lx.ignore() // Ignore """
+ return lexMultilineString
+ }
+ lx.backup()
+ }
+ lx.ignore() // ignore the '"'
+ return lexString
+ case '\'':
+ if lx.accept('\'') {
+ if lx.accept('\'') {
+ lx.ignore() // Ignore """
+ return lexMultilineRawString
+ }
+ lx.backup()
+ }
+ lx.ignore() // ignore the "'"
+ return lexRawString
+ case '.': // special error case, be kind to users
+ return lx.errorf("floats must start with a digit, not '.'")
+ case 'i', 'n':
+ if (lx.accept('n') && lx.accept('f')) || (lx.accept('a') && lx.accept('n')) {
+ lx.emit(itemFloat)
+ return lx.pop()
+ }
+ case '-', '+':
+ return lexDecimalNumberStart
+ }
+ if unicode.IsLetter(r) {
+ // Be permissive here; lexBool will give a nice error if the
+ // user wrote something like
+ // x = foo
+ // (i.e. not 'true' or 'false' but is something else word-like.)
+ lx.backup()
+ return lexBool
+ }
+ if r == eof {
+ return lx.errorf("unexpected EOF; expected value")
+ }
+ if r == '\n' {
+ return lx.errorPrevLine(fmt.Errorf("expected value but found %q instead", r))
+ }
+ return lx.errorf("expected value but found %q instead", r)
+}
+
+// lexArrayValue consumes one value in an array. It assumes that '[' or ','
+// have already been consumed. All whitespace and newlines are ignored.
+func lexArrayValue(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case isWhitespace(r) || isNL(r):
+ return lexSkip(lx, lexArrayValue)
+ case r == '#':
+ lx.push(lexArrayValue)
+ return lexCommentStart
+ case r == ',':
+ return lx.errorf("unexpected comma")
+ case r == ']':
+ return lexArrayEnd
+ }
+
+ lx.backup()
+ lx.push(lexArrayValueEnd)
+ return lexValue
+}
+
+// lexArrayValueEnd consumes everything between the end of an array value and
+// the next value (or the end of the array): it ignores whitespace and newlines
+// and expects either a ',' or a ']'.
+func lexArrayValueEnd(lx *lexer) stateFn {
+ switch r := lx.next(); {
+ case isWhitespace(r) || isNL(r):
+ return lexSkip(lx, lexArrayValueEnd)
+ case r == '#':
+ lx.push(lexArrayValueEnd)
+ return lexCommentStart
+ case r == ',':
+ lx.ignore()
+ return lexArrayValue // move on to the next value
+ case r == ']':
+ return lexArrayEnd
+ default:
+ return lx.errorf("expected a comma (',') or array terminator (']'), but got %s", runeOrEOF(r))
+ }
+}
+
+// lexArrayEnd finishes the lexing of an array.
+// It assumes that a ']' has just been consumed.
+func lexArrayEnd(lx *lexer) stateFn {
+ lx.ignore()
+ lx.emit(itemArrayEnd)
+ return lx.pop()
+}
+
+// lexInlineTableValue consumes one key/value pair in an inline table.
+// It assumes that '{' or ',' have already been consumed. Whitespace is ignored.
+func lexInlineTableValue(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case isWhitespace(r):
+ return lexSkip(lx, lexInlineTableValue)
+ case isNL(r):
+ if lx.tomlNext {
+ return lexSkip(lx, lexInlineTableValue)
+ }
+ return lx.errorPrevLine(errLexInlineTableNL{})
+ case r == '#':
+ lx.push(lexInlineTableValue)
+ return lexCommentStart
+ case r == ',':
+ return lx.errorf("unexpected comma")
+ case r == '}':
+ return lexInlineTableEnd
+ }
+ lx.backup()
+ lx.push(lexInlineTableValueEnd)
+ return lexKeyStart
+}
+
+// lexInlineTableValueEnd consumes everything between the end of an inline table
+// key/value pair and the next pair (or the end of the table):
+// it ignores whitespace and expects either a ',' or a '}'.
+func lexInlineTableValueEnd(lx *lexer) stateFn {
+ switch r := lx.next(); {
+ case isWhitespace(r):
+ return lexSkip(lx, lexInlineTableValueEnd)
+ case isNL(r):
+ if lx.tomlNext {
+ return lexSkip(lx, lexInlineTableValueEnd)
+ }
+ return lx.errorPrevLine(errLexInlineTableNL{})
+ case r == '#':
+ lx.push(lexInlineTableValueEnd)
+ return lexCommentStart
+ case r == ',':
+ lx.ignore()
+ lx.skip(isWhitespace)
+ if lx.peek() == '}' {
+ if lx.tomlNext {
+ return lexInlineTableValueEnd
+ }
+ return lx.errorf("trailing comma not allowed in inline tables")
+ }
+ return lexInlineTableValue
+ case r == '}':
+ return lexInlineTableEnd
+ default:
+ return lx.errorf("expected a comma or an inline table terminator '}', but got %s instead", runeOrEOF(r))
+ }
+}
+
+func runeOrEOF(r rune) string {
+ if r == eof {
+ return "end of file"
+ }
+ return "'" + string(r) + "'"
+}
+
+// lexInlineTableEnd finishes the lexing of an inline table.
+// It assumes that a '}' has just been consumed.
+func lexInlineTableEnd(lx *lexer) stateFn {
+ lx.ignore()
+ lx.emit(itemInlineTableEnd)
+ return lx.pop()
+}
+
+// lexString consumes the inner contents of a string. It assumes that the
+// beginning '"' has already been consumed and ignored.
+func lexString(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case r == eof:
+ return lx.errorf(`unexpected EOF; expected '"'`)
+ case isNL(r):
+ return lx.errorPrevLine(errLexStringNL{})
+ case r == '\\':
+ lx.push(lexString)
+ return lexStringEscape
+ case r == '"':
+ lx.backup()
+ if lx.esc {
+ lx.esc = false
+ lx.emit(itemStringEsc)
+ } else {
+ lx.emit(itemString)
+ }
+ lx.next()
+ lx.ignore()
+ return lx.pop()
+ }
+ return lexString
+}
+
+// lexMultilineString consumes the inner contents of a string. It assumes that
+// the beginning '"""' has already been consumed and ignored.
+func lexMultilineString(lx *lexer) stateFn {
+ r := lx.next()
+ switch r {
+ default:
+ return lexMultilineString
+ case eof:
+ return lx.errorf(`unexpected EOF; expected '"""'`)
+ case '\\':
+ return lexMultilineStringEscape
+ case '"':
+ /// Found " → try to read two more "".
+ if lx.accept('"') {
+ if lx.accept('"') {
+ /// Peek ahead: the string can contain " and "", including at the
+ /// end: """str"""""
+ /// 6 or more at the end, however, is an error.
+ if lx.peek() == '"' {
+ /// Check if we already lexed 5 's; if so we have 6 now, and
+ /// that's just too many man!
+ ///
+ /// Second check is for the edge case:
+ ///
+ /// two quotes allowed.
+ /// vv
+ /// """lol \""""""
+ /// ^^ ^^^---- closing three
+ /// escaped
+ ///
+ /// But ugly, but it works
+ if strings.HasSuffix(lx.current(), `"""""`) && !strings.HasSuffix(lx.current(), `\"""""`) {
+ return lx.errorf(`unexpected '""""""'`)
+ }
+ lx.backup()
+ lx.backup()
+ return lexMultilineString
+ }
+
+ lx.backup() /// backup: don't include the """ in the item.
+ lx.backup()
+ lx.backup()
+ lx.esc = false
+ lx.emit(itemMultilineString)
+ lx.next() /// Read over ''' again and discard it.
+ lx.next()
+ lx.next()
+ lx.ignore()
+ return lx.pop()
+ }
+ lx.backup()
+ }
+ return lexMultilineString
+ }
+}
+
+// lexRawString consumes a raw string. Nothing can be escaped in such a string.
+// It assumes that the beginning "'" has already been consumed and ignored.
+func lexRawString(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ default:
+ return lexRawString
+ case r == eof:
+ return lx.errorf(`unexpected EOF; expected "'"`)
+ case isNL(r):
+ return lx.errorPrevLine(errLexStringNL{})
+ case r == '\'':
+ lx.backup()
+ lx.emit(itemRawString)
+ lx.next()
+ lx.ignore()
+ return lx.pop()
+ }
+}
+
+// lexMultilineRawString consumes a raw string. Nothing can be escaped in such a
+// string. It assumes that the beginning triple-' has already been consumed and
+// ignored.
+func lexMultilineRawString(lx *lexer) stateFn {
+ r := lx.next()
+ switch r {
+ default:
+ return lexMultilineRawString
+ case eof:
+ return lx.errorf(`unexpected EOF; expected "'''"`)
+ case '\'':
+ /// Found ' → try to read two more ''.
+ if lx.accept('\'') {
+ if lx.accept('\'') {
+ /// Peek ahead: the string can contain ' and '', including at the
+ /// end: '''str'''''
+ /// 6 or more at the end, however, is an error.
+ if lx.peek() == '\'' {
+ /// Check if we already lexed 5 's; if so we have 6 now, and
+ /// that's just too many man!
+ if strings.HasSuffix(lx.current(), "'''''") {
+ return lx.errorf(`unexpected "''''''"`)
+ }
+ lx.backup()
+ lx.backup()
+ return lexMultilineRawString
+ }
+
+ lx.backup() /// backup: don't include the ''' in the item.
+ lx.backup()
+ lx.backup()
+ lx.emit(itemRawMultilineString)
+ lx.next() /// Read over ''' again and discard it.
+ lx.next()
+ lx.next()
+ lx.ignore()
+ return lx.pop()
+ }
+ lx.backup()
+ }
+ return lexMultilineRawString
+ }
+}
+
+// lexMultilineStringEscape consumes an escaped character. It assumes that the
+// preceding '\\' has already been consumed.
+func lexMultilineStringEscape(lx *lexer) stateFn {
+ if isNL(lx.next()) { /// \ escaping newline.
+ return lexMultilineString
+ }
+ lx.backup()
+ lx.push(lexMultilineString)
+ return lexStringEscape(lx)
+}
+
+func lexStringEscape(lx *lexer) stateFn {
+ lx.esc = true
+ r := lx.next()
+ switch r {
+ case 'e':
+ if !lx.tomlNext {
+ return lx.error(errLexEscape{r})
+ }
+ fallthrough
+ case 'b':
+ fallthrough
+ case 't':
+ fallthrough
+ case 'n':
+ fallthrough
+ case 'f':
+ fallthrough
+ case 'r':
+ fallthrough
+ case '"':
+ fallthrough
+ case ' ', '\t':
+ // Inside """ .. """ strings you can use \ to escape newlines, and any
+ // amount of whitespace can be between the \ and \n.
+ fallthrough
+ case '\\':
+ return lx.pop()
+ case 'x':
+ if !lx.tomlNext {
+ return lx.error(errLexEscape{r})
+ }
+ return lexHexEscape
+ case 'u':
+ return lexShortUnicodeEscape
+ case 'U':
+ return lexLongUnicodeEscape
+ }
+ return lx.error(errLexEscape{r})
+}
+
+func lexHexEscape(lx *lexer) stateFn {
+ var r rune
+ for i := 0; i < 2; i++ {
+ r = lx.next()
+ if !isHex(r) {
+ return lx.errorf(`expected two hexadecimal digits after '\x', but got %q instead`, lx.current())
+ }
+ }
+ return lx.pop()
+}
+
+func lexShortUnicodeEscape(lx *lexer) stateFn {
+ var r rune
+ for i := 0; i < 4; i++ {
+ r = lx.next()
+ if !isHex(r) {
+ return lx.errorf(`expected four hexadecimal digits after '\u', but got %q instead`, lx.current())
+ }
+ }
+ return lx.pop()
+}
+
+func lexLongUnicodeEscape(lx *lexer) stateFn {
+ var r rune
+ for i := 0; i < 8; i++ {
+ r = lx.next()
+ if !isHex(r) {
+ return lx.errorf(`expected eight hexadecimal digits after '\U', but got %q instead`, lx.current())
+ }
+ }
+ return lx.pop()
+}
+
+// lexNumberOrDateStart processes the first character of a value which begins
+// with a digit. It exists to catch values starting with '0', so that
+// lexBaseNumberOrDate can differentiate base prefixed integers from other
+// types.
+func lexNumberOrDateStart(lx *lexer) stateFn {
+ r := lx.next()
+ switch r {
+ case '0':
+ return lexBaseNumberOrDate
+ }
+
+ if !isDigit(r) {
+ // The only way to reach this state is if the value starts
+ // with a digit, so specifically treat anything else as an
+ // error.
+ return lx.errorf("expected a digit but got %q", r)
+ }
+
+ return lexNumberOrDate
+}
+
+// lexNumberOrDate consumes either an integer, float or datetime.
+func lexNumberOrDate(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexNumberOrDate
+ }
+ switch r {
+ case '-', ':':
+ return lexDatetime
+ case '_':
+ return lexDecimalNumber
+ case '.', 'e', 'E':
+ return lexFloat
+ }
+
+ lx.backup()
+ lx.emit(itemInteger)
+ return lx.pop()
+}
+
+// lexDatetime consumes a Datetime, to a first approximation.
+// The parser validates that it matches one of the accepted formats.
+func lexDatetime(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexDatetime
+ }
+ switch r {
+ case '-', ':', 'T', 't', ' ', '.', 'Z', 'z', '+':
+ return lexDatetime
+ }
+
+ lx.backup()
+ lx.emitTrim(itemDatetime)
+ return lx.pop()
+}
+
+// lexHexInteger consumes a hexadecimal integer after seeing the '0x' prefix.
+func lexHexInteger(lx *lexer) stateFn {
+ r := lx.next()
+ if isHex(r) {
+ return lexHexInteger
+ }
+ switch r {
+ case '_':
+ return lexHexInteger
+ }
+
+ lx.backup()
+ lx.emit(itemInteger)
+ return lx.pop()
+}
+
+// lexOctalInteger consumes an octal integer after seeing the '0o' prefix.
+func lexOctalInteger(lx *lexer) stateFn {
+ r := lx.next()
+ if isOctal(r) {
+ return lexOctalInteger
+ }
+ switch r {
+ case '_':
+ return lexOctalInteger
+ }
+
+ lx.backup()
+ lx.emit(itemInteger)
+ return lx.pop()
+}
+
+// lexBinaryInteger consumes a binary integer after seeing the '0b' prefix.
+func lexBinaryInteger(lx *lexer) stateFn {
+ r := lx.next()
+ if isBinary(r) {
+ return lexBinaryInteger
+ }
+ switch r {
+ case '_':
+ return lexBinaryInteger
+ }
+
+ lx.backup()
+ lx.emit(itemInteger)
+ return lx.pop()
+}
+
+// lexDecimalNumber consumes a decimal float or integer.
+func lexDecimalNumber(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexDecimalNumber
+ }
+ switch r {
+ case '.', 'e', 'E':
+ return lexFloat
+ case '_':
+ return lexDecimalNumber
+ }
+
+ lx.backup()
+ lx.emit(itemInteger)
+ return lx.pop()
+}
+
+// lexDecimalNumber consumes the first digit of a number beginning with a sign.
+// It assumes the sign has already been consumed. Values which start with a sign
+// are only allowed to be decimal integers or floats.
+//
+// The special "nan" and "inf" values are also recognized.
+func lexDecimalNumberStart(lx *lexer) stateFn {
+ r := lx.next()
+
+ // Special error cases to give users better error messages
+ switch r {
+ case 'i':
+ if !lx.accept('n') || !lx.accept('f') {
+ return lx.errorf("invalid float: '%s'", lx.current())
+ }
+ lx.emit(itemFloat)
+ return lx.pop()
+ case 'n':
+ if !lx.accept('a') || !lx.accept('n') {
+ return lx.errorf("invalid float: '%s'", lx.current())
+ }
+ lx.emit(itemFloat)
+ return lx.pop()
+ case '0':
+ p := lx.peek()
+ switch p {
+ case 'b', 'o', 'x':
+ return lx.errorf("cannot use sign with non-decimal numbers: '%s%c'", lx.current(), p)
+ }
+ case '.':
+ return lx.errorf("floats must start with a digit, not '.'")
+ }
+
+ if isDigit(r) {
+ return lexDecimalNumber
+ }
+
+ return lx.errorf("expected a digit but got %q", r)
+}
+
+// lexBaseNumberOrDate differentiates between the possible values which
+// start with '0'. It assumes that before reaching this state, the initial '0'
+// has been consumed.
+func lexBaseNumberOrDate(lx *lexer) stateFn {
+ r := lx.next()
+ // Note: All datetimes start with at least two digits, so we don't
+ // handle date characters (':', '-', etc.) here.
+ if isDigit(r) {
+ return lexNumberOrDate
+ }
+ switch r {
+ case '_':
+ // Can only be decimal, because there can't be an underscore
+ // between the '0' and the base designator, and dates can't
+ // contain underscores.
+ return lexDecimalNumber
+ case '.', 'e', 'E':
+ return lexFloat
+ case 'b':
+ r = lx.peek()
+ if !isBinary(r) {
+ lx.errorf("not a binary number: '%s%c'", lx.current(), r)
+ }
+ return lexBinaryInteger
+ case 'o':
+ r = lx.peek()
+ if !isOctal(r) {
+ lx.errorf("not an octal number: '%s%c'", lx.current(), r)
+ }
+ return lexOctalInteger
+ case 'x':
+ r = lx.peek()
+ if !isHex(r) {
+ lx.errorf("not a hexadecimal number: '%s%c'", lx.current(), r)
+ }
+ return lexHexInteger
+ }
+
+ lx.backup()
+ lx.emit(itemInteger)
+ return lx.pop()
+}
+
+// lexFloat consumes the elements of a float. It allows any sequence of
+// float-like characters, so floats emitted by the lexer are only a first
+// approximation and must be validated by the parser.
+func lexFloat(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexFloat
+ }
+ switch r {
+ case '_', '.', '-', '+', 'e', 'E':
+ return lexFloat
+ }
+
+ lx.backup()
+ lx.emit(itemFloat)
+ return lx.pop()
+}
+
+// lexBool consumes a bool string: 'true' or 'false.
+func lexBool(lx *lexer) stateFn {
+ var rs []rune
+ for {
+ r := lx.next()
+ if !unicode.IsLetter(r) {
+ lx.backup()
+ break
+ }
+ rs = append(rs, r)
+ }
+ s := string(rs)
+ switch s {
+ case "true", "false":
+ lx.emit(itemBool)
+ return lx.pop()
+ }
+ return lx.errorf("expected value but found %q instead", s)
+}
+
+// lexCommentStart begins the lexing of a comment. It will emit
+// itemCommentStart and consume no characters, passing control to lexComment.
+func lexCommentStart(lx *lexer) stateFn {
+ lx.ignore()
+ lx.emit(itemCommentStart)
+ return lexComment
+}
+
+// lexComment lexes an entire comment. It assumes that '#' has been consumed.
+// It will consume *up to* the first newline character, and pass control
+// back to the last state on the stack.
+func lexComment(lx *lexer) stateFn {
+ switch r := lx.next(); {
+ case isNL(r) || r == eof:
+ lx.backup()
+ lx.emit(itemText)
+ return lx.pop()
+ default:
+ return lexComment
+ }
+}
+
+// lexSkip ignores all slurped input and moves on to the next state.
+func lexSkip(lx *lexer, nextState stateFn) stateFn {
+ lx.ignore()
+ return nextState
+}
+
+func (s stateFn) String() string {
+ name := runtime.FuncForPC(reflect.ValueOf(s).Pointer()).Name()
+ if i := strings.LastIndexByte(name, '.'); i > -1 {
+ name = name[i+1:]
+ }
+ if s == nil {
+ name = ""
+ }
+ return name + "()"
+}
+
+func (itype itemType) String() string {
+ switch itype {
+ case itemError:
+ return "Error"
+ case itemNIL:
+ return "NIL"
+ case itemEOF:
+ return "EOF"
+ case itemText:
+ return "Text"
+ case itemString, itemStringEsc, itemRawString, itemMultilineString, itemRawMultilineString:
+ return "String"
+ case itemBool:
+ return "Bool"
+ case itemInteger:
+ return "Integer"
+ case itemFloat:
+ return "Float"
+ case itemDatetime:
+ return "DateTime"
+ case itemTableStart:
+ return "TableStart"
+ case itemTableEnd:
+ return "TableEnd"
+ case itemKeyStart:
+ return "KeyStart"
+ case itemKeyEnd:
+ return "KeyEnd"
+ case itemArray:
+ return "Array"
+ case itemArrayEnd:
+ return "ArrayEnd"
+ case itemCommentStart:
+ return "CommentStart"
+ case itemInlineTableStart:
+ return "InlineTableStart"
+ case itemInlineTableEnd:
+ return "InlineTableEnd"
+ }
+ panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype)))
+}
+
+func (item item) String() string {
+ return fmt.Sprintf("(%s, %s)", item.typ, item.val)
+}
+
+func isWhitespace(r rune) bool { return r == '\t' || r == ' ' }
+func isNL(r rune) bool { return r == '\n' || r == '\r' }
+func isControl(r rune) bool { // Control characters except \t, \r, \n
+ switch r {
+ case '\t', '\r', '\n':
+ return false
+ default:
+ return (r >= 0x00 && r <= 0x1f) || r == 0x7f
+ }
+}
+func isDigit(r rune) bool { return r >= '0' && r <= '9' }
+func isBinary(r rune) bool { return r == '0' || r == '1' }
+func isOctal(r rune) bool { return r >= '0' && r <= '7' }
+func isHex(r rune) bool { return (r >= '0' && r <= '9') || (r|0x20 >= 'a' && r|0x20 <= 'f') }
+func isBareKeyChar(r rune, tomlNext bool) bool {
+ return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') ||
+ (r >= '0' && r <= '9') || r == '_' || r == '-'
+}
diff --git a/vendor/github.com/BurntSushi/toml/meta.go b/vendor/github.com/BurntSushi/toml/meta.go
new file mode 100644
index 00000000..0d337026
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/meta.go
@@ -0,0 +1,145 @@
+package toml
+
+import (
+ "strings"
+)
+
+// MetaData allows access to meta information about TOML data that's not
+// accessible otherwise.
+//
+// It allows checking if a key is defined in the TOML data, whether any keys
+// were undecoded, and the TOML type of a key.
+type MetaData struct {
+ context Key // Used only during decoding.
+
+ keyInfo map[string]keyInfo
+ mapping map[string]any
+ keys []Key
+ decoded map[string]struct{}
+ data []byte // Input file; for errors.
+}
+
+// IsDefined reports if the key exists in the TOML data.
+//
+// The key should be specified hierarchically, for example to access the TOML
+// key "a.b.c" you would use IsDefined("a", "b", "c"). Keys are case sensitive.
+//
+// Returns false for an empty key.
+func (md *MetaData) IsDefined(key ...string) bool {
+ if len(key) == 0 {
+ return false
+ }
+
+ var (
+ hash map[string]any
+ ok bool
+ hashOrVal any = md.mapping
+ )
+ for _, k := range key {
+ if hash, ok = hashOrVal.(map[string]any); !ok {
+ return false
+ }
+ if hashOrVal, ok = hash[k]; !ok {
+ return false
+ }
+ }
+ return true
+}
+
+// Type returns a string representation of the type of the key specified.
+//
+// Type will return the empty string if given an empty key or a key that does
+// not exist. Keys are case sensitive.
+func (md *MetaData) Type(key ...string) string {
+ if ki, ok := md.keyInfo[Key(key).String()]; ok {
+ return ki.tomlType.typeString()
+ }
+ return ""
+}
+
+// Keys returns a slice of every key in the TOML data, including key groups.
+//
+// Each key is itself a slice, where the first element is the top of the
+// hierarchy and the last is the most specific. The list will have the same
+// order as the keys appeared in the TOML data.
+//
+// All keys returned are non-empty.
+func (md *MetaData) Keys() []Key {
+ return md.keys
+}
+
+// Undecoded returns all keys that have not been decoded in the order in which
+// they appear in the original TOML document.
+//
+// This includes keys that haven't been decoded because of a [Primitive] value.
+// Once the Primitive value is decoded, the keys will be considered decoded.
+//
+// Also note that decoding into an empty interface will result in no decoding,
+// and so no keys will be considered decoded.
+//
+// In this sense, the Undecoded keys correspond to keys in the TOML document
+// that do not have a concrete type in your representation.
+func (md *MetaData) Undecoded() []Key {
+ undecoded := make([]Key, 0, len(md.keys))
+ for _, key := range md.keys {
+ if _, ok := md.decoded[key.String()]; !ok {
+ undecoded = append(undecoded, key)
+ }
+ }
+ return undecoded
+}
+
+// Key represents any TOML key, including key groups. Use [MetaData.Keys] to get
+// values of this type.
+type Key []string
+
+func (k Key) String() string {
+ // This is called quite often, so it's a bit funky to make it faster.
+ var b strings.Builder
+ b.Grow(len(k) * 25)
+outer:
+ for i, kk := range k {
+ if i > 0 {
+ b.WriteByte('.')
+ }
+ if kk == "" {
+ b.WriteString(`""`)
+ } else {
+ for _, r := range kk {
+ // "Inline" isBareKeyChar
+ if !((r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || r == '-') {
+ b.WriteByte('"')
+ b.WriteString(dblQuotedReplacer.Replace(kk))
+ b.WriteByte('"')
+ continue outer
+ }
+ }
+ b.WriteString(kk)
+ }
+ }
+ return b.String()
+}
+
+func (k Key) maybeQuoted(i int) string {
+ if k[i] == "" {
+ return `""`
+ }
+ for _, r := range k[i] {
+ if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || r == '-' {
+ continue
+ }
+ return `"` + dblQuotedReplacer.Replace(k[i]) + `"`
+ }
+ return k[i]
+}
+
+// Like append(), but only increase the cap by 1.
+func (k Key) add(piece string) Key {
+ newKey := make(Key, len(k)+1)
+ copy(newKey, k)
+ newKey[len(k)] = piece
+ return newKey
+}
+
+func (k Key) parent() Key { return k[:len(k)-1] } // all except the last piece.
+func (k Key) last() string { return k[len(k)-1] } // last piece of this key.
diff --git a/vendor/github.com/BurntSushi/toml/parse.go b/vendor/github.com/BurntSushi/toml/parse.go
new file mode 100644
index 00000000..e3ea8a9a
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/parse.go
@@ -0,0 +1,845 @@
+package toml
+
+import (
+ "fmt"
+ "math"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+ "unicode/utf8"
+
+ "github.com/BurntSushi/toml/internal"
+)
+
+type parser struct {
+ lx *lexer
+ context Key // Full key for the current hash in scope.
+ currentKey string // Base key name for everything except hashes.
+ pos Position // Current position in the TOML file.
+ tomlNext bool
+
+ ordered []Key // List of keys in the order that they appear in the TOML data.
+
+ keyInfo map[string]keyInfo // Map keyname → info about the TOML key.
+ mapping map[string]any // Map keyname → key value.
+ implicits map[string]struct{} // Record implicit keys (e.g. "key.group.names").
+}
+
+type keyInfo struct {
+ pos Position
+ tomlType tomlType
+}
+
+func parse(data string) (p *parser, err error) {
+ _, tomlNext := os.LookupEnv("BURNTSUSHI_TOML_110")
+
+ defer func() {
+ if r := recover(); r != nil {
+ if pErr, ok := r.(ParseError); ok {
+ pErr.input = data
+ err = pErr
+ return
+ }
+ panic(r)
+ }
+ }()
+
+ // Read over BOM; do this here as the lexer calls utf8.DecodeRuneInString()
+ // which mangles stuff. UTF-16 BOM isn't strictly valid, but some tools add
+ // it anyway.
+ if strings.HasPrefix(data, "\xff\xfe") || strings.HasPrefix(data, "\xfe\xff") { // UTF-16
+ data = data[2:]
+ } else if strings.HasPrefix(data, "\xef\xbb\xbf") { // UTF-8
+ data = data[3:]
+ }
+
+ // Examine first few bytes for NULL bytes; this probably means it's a UTF-16
+ // file (second byte in surrogate pair being NULL). Again, do this here to
+ // avoid having to deal with UTF-8/16 stuff in the lexer.
+ ex := 6
+ if len(data) < 6 {
+ ex = len(data)
+ }
+ if i := strings.IndexRune(data[:ex], 0); i > -1 {
+ return nil, ParseError{
+ Message: "files cannot contain NULL bytes; probably using UTF-16; TOML files must be UTF-8",
+ Position: Position{Line: 1, Col: 1, Start: i, Len: 1},
+ Line: 1,
+ input: data,
+ }
+ }
+
+ p = &parser{
+ keyInfo: make(map[string]keyInfo),
+ mapping: make(map[string]any),
+ lx: lex(data, tomlNext),
+ ordered: make([]Key, 0),
+ implicits: make(map[string]struct{}),
+ tomlNext: tomlNext,
+ }
+ for {
+ item := p.next()
+ if item.typ == itemEOF {
+ break
+ }
+ p.topLevel(item)
+ }
+
+ return p, nil
+}
+
+func (p *parser) panicErr(it item, err error) {
+ panic(ParseError{
+ Message: err.Error(),
+ err: err,
+ Position: it.pos.withCol(p.lx.input),
+ Line: it.pos.Len,
+ LastKey: p.current(),
+ })
+}
+
+func (p *parser) panicItemf(it item, format string, v ...any) {
+ panic(ParseError{
+ Message: fmt.Sprintf(format, v...),
+ Position: it.pos.withCol(p.lx.input),
+ Line: it.pos.Len,
+ LastKey: p.current(),
+ })
+}
+
+func (p *parser) panicf(format string, v ...any) {
+ panic(ParseError{
+ Message: fmt.Sprintf(format, v...),
+ Position: p.pos.withCol(p.lx.input),
+ Line: p.pos.Line,
+ LastKey: p.current(),
+ })
+}
+
+func (p *parser) next() item {
+ it := p.lx.nextItem()
+ //fmt.Printf("ITEM %-18s line %-3d │ %q\n", it.typ, it.pos.Line, it.val)
+ if it.typ == itemError {
+ if it.err != nil {
+ panic(ParseError{
+ Message: it.err.Error(),
+ err: it.err,
+ Position: it.pos.withCol(p.lx.input),
+ Line: it.pos.Line,
+ LastKey: p.current(),
+ })
+ }
+
+ p.panicItemf(it, "%s", it.val)
+ }
+ return it
+}
+
+func (p *parser) nextPos() item {
+ it := p.next()
+ p.pos = it.pos
+ return it
+}
+
+func (p *parser) bug(format string, v ...any) {
+ panic(fmt.Sprintf("BUG: "+format+"\n\n", v...))
+}
+
+func (p *parser) expect(typ itemType) item {
+ it := p.next()
+ p.assertEqual(typ, it.typ)
+ return it
+}
+
+func (p *parser) assertEqual(expected, got itemType) {
+ if expected != got {
+ p.bug("Expected '%s' but got '%s'.", expected, got)
+ }
+}
+
+func (p *parser) topLevel(item item) {
+ switch item.typ {
+ case itemCommentStart: // # ..
+ p.expect(itemText)
+ case itemTableStart: // [ .. ]
+ name := p.nextPos()
+
+ var key Key
+ for ; name.typ != itemTableEnd && name.typ != itemEOF; name = p.next() {
+ key = append(key, p.keyString(name))
+ }
+ p.assertEqual(itemTableEnd, name.typ)
+
+ p.addContext(key, false)
+ p.setType("", tomlHash, item.pos)
+ p.ordered = append(p.ordered, key)
+ case itemArrayTableStart: // [[ .. ]]
+ name := p.nextPos()
+
+ var key Key
+ for ; name.typ != itemArrayTableEnd && name.typ != itemEOF; name = p.next() {
+ key = append(key, p.keyString(name))
+ }
+ p.assertEqual(itemArrayTableEnd, name.typ)
+
+ p.addContext(key, true)
+ p.setType("", tomlArrayHash, item.pos)
+ p.ordered = append(p.ordered, key)
+ case itemKeyStart: // key = ..
+ outerContext := p.context
+ /// Read all the key parts (e.g. 'a' and 'b' in 'a.b')
+ k := p.nextPos()
+ var key Key
+ for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() {
+ key = append(key, p.keyString(k))
+ }
+ p.assertEqual(itemKeyEnd, k.typ)
+
+ /// The current key is the last part.
+ p.currentKey = key.last()
+
+ /// All the other parts (if any) are the context; need to set each part
+ /// as implicit.
+ context := key.parent()
+ for i := range context {
+ p.addImplicitContext(append(p.context, context[i:i+1]...))
+ }
+ p.ordered = append(p.ordered, p.context.add(p.currentKey))
+
+ /// Set value.
+ vItem := p.next()
+ val, typ := p.value(vItem, false)
+ p.setValue(p.currentKey, val)
+ p.setType(p.currentKey, typ, vItem.pos)
+
+ /// Remove the context we added (preserving any context from [tbl] lines).
+ p.context = outerContext
+ p.currentKey = ""
+ default:
+ p.bug("Unexpected type at top level: %s", item.typ)
+ }
+}
+
+// Gets a string for a key (or part of a key in a table name).
+func (p *parser) keyString(it item) string {
+ switch it.typ {
+ case itemText:
+ return it.val
+ case itemString, itemStringEsc, itemMultilineString,
+ itemRawString, itemRawMultilineString:
+ s, _ := p.value(it, false)
+ return s.(string)
+ default:
+ p.bug("Unexpected key type: %s", it.typ)
+ }
+ panic("unreachable")
+}
+
+var datetimeRepl = strings.NewReplacer(
+ "z", "Z",
+ "t", "T",
+ " ", "T")
+
+// value translates an expected value from the lexer into a Go value wrapped
+// as an empty interface.
+func (p *parser) value(it item, parentIsArray bool) (any, tomlType) {
+ switch it.typ {
+ case itemString:
+ return it.val, p.typeOfPrimitive(it)
+ case itemStringEsc:
+ return p.replaceEscapes(it, it.val), p.typeOfPrimitive(it)
+ case itemMultilineString:
+ return p.replaceEscapes(it, p.stripEscapedNewlines(stripFirstNewline(it.val))), p.typeOfPrimitive(it)
+ case itemRawString:
+ return it.val, p.typeOfPrimitive(it)
+ case itemRawMultilineString:
+ return stripFirstNewline(it.val), p.typeOfPrimitive(it)
+ case itemInteger:
+ return p.valueInteger(it)
+ case itemFloat:
+ return p.valueFloat(it)
+ case itemBool:
+ switch it.val {
+ case "true":
+ return true, p.typeOfPrimitive(it)
+ case "false":
+ return false, p.typeOfPrimitive(it)
+ default:
+ p.bug("Expected boolean value, but got '%s'.", it.val)
+ }
+ case itemDatetime:
+ return p.valueDatetime(it)
+ case itemArray:
+ return p.valueArray(it)
+ case itemInlineTableStart:
+ return p.valueInlineTable(it, parentIsArray)
+ default:
+ p.bug("Unexpected value type: %s", it.typ)
+ }
+ panic("unreachable")
+}
+
+func (p *parser) valueInteger(it item) (any, tomlType) {
+ if !numUnderscoresOK(it.val) {
+ p.panicItemf(it, "Invalid integer %q: underscores must be surrounded by digits", it.val)
+ }
+ if numHasLeadingZero(it.val) {
+ p.panicItemf(it, "Invalid integer %q: cannot have leading zeroes", it.val)
+ }
+
+ num, err := strconv.ParseInt(it.val, 0, 64)
+ if err != nil {
+ // Distinguish integer values. Normally, it'd be a bug if the lexer
+ // provides an invalid integer, but it's possible that the number is
+ // out of range of valid values (which the lexer cannot determine).
+ // So mark the former as a bug but the latter as a legitimate user
+ // error.
+ if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
+ p.panicErr(it, errParseRange{i: it.val, size: "int64"})
+ } else {
+ p.bug("Expected integer value, but got '%s'.", it.val)
+ }
+ }
+ return num, p.typeOfPrimitive(it)
+}
+
+func (p *parser) valueFloat(it item) (any, tomlType) {
+ parts := strings.FieldsFunc(it.val, func(r rune) bool {
+ switch r {
+ case '.', 'e', 'E':
+ return true
+ }
+ return false
+ })
+ for _, part := range parts {
+ if !numUnderscoresOK(part) {
+ p.panicItemf(it, "Invalid float %q: underscores must be surrounded by digits", it.val)
+ }
+ }
+ if len(parts) > 0 && numHasLeadingZero(parts[0]) {
+ p.panicItemf(it, "Invalid float %q: cannot have leading zeroes", it.val)
+ }
+ if !numPeriodsOK(it.val) {
+ // As a special case, numbers like '123.' or '1.e2',
+ // which are valid as far as Go/strconv are concerned,
+ // must be rejected because TOML says that a fractional
+ // part consists of '.' followed by 1+ digits.
+ p.panicItemf(it, "Invalid float %q: '.' must be followed by one or more digits", it.val)
+ }
+ val := strings.Replace(it.val, "_", "", -1)
+ signbit := false
+ if val == "+nan" || val == "-nan" {
+ signbit = val == "-nan"
+ val = "nan"
+ }
+ num, err := strconv.ParseFloat(val, 64)
+ if err != nil {
+ if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
+ p.panicErr(it, errParseRange{i: it.val, size: "float64"})
+ } else {
+ p.panicItemf(it, "Invalid float value: %q", it.val)
+ }
+ }
+ if signbit {
+ num = math.Copysign(num, -1)
+ }
+ return num, p.typeOfPrimitive(it)
+}
+
+var dtTypes = []struct {
+ fmt string
+ zone *time.Location
+ next bool
+}{
+ {time.RFC3339Nano, time.Local, false},
+ {"2006-01-02T15:04:05.999999999", internal.LocalDatetime, false},
+ {"2006-01-02", internal.LocalDate, false},
+ {"15:04:05.999999999", internal.LocalTime, false},
+
+ // tomlNext
+ {"2006-01-02T15:04Z07:00", time.Local, true},
+ {"2006-01-02T15:04", internal.LocalDatetime, true},
+ {"15:04", internal.LocalTime, true},
+}
+
+func (p *parser) valueDatetime(it item) (any, tomlType) {
+ it.val = datetimeRepl.Replace(it.val)
+ var (
+ t time.Time
+ ok bool
+ err error
+ )
+ for _, dt := range dtTypes {
+ if dt.next && !p.tomlNext {
+ continue
+ }
+ t, err = time.ParseInLocation(dt.fmt, it.val, dt.zone)
+ if err == nil {
+ if missingLeadingZero(it.val, dt.fmt) {
+ p.panicErr(it, errParseDate{it.val})
+ }
+ ok = true
+ break
+ }
+ }
+ if !ok {
+ p.panicErr(it, errParseDate{it.val})
+ }
+ return t, p.typeOfPrimitive(it)
+}
+
+// Go's time.Parse() will accept numbers without a leading zero; there isn't any
+// way to require it. https://github.com/golang/go/issues/29911
+//
+// Depend on the fact that the separators (- and :) should always be at the same
+// location.
+func missingLeadingZero(d, l string) bool {
+ for i, c := range []byte(l) {
+ if c == '.' || c == 'Z' {
+ return false
+ }
+ if (c < '0' || c > '9') && d[i] != c {
+ return true
+ }
+ }
+ return false
+}
+
+func (p *parser) valueArray(it item) (any, tomlType) {
+ p.setType(p.currentKey, tomlArray, it.pos)
+
+ var (
+ // Initialize to a non-nil slice to make it consistent with how S = []
+ // decodes into a non-nil slice inside something like struct { S
+ // []string }. See #338
+ array = make([]any, 0, 2)
+ )
+ for it = p.next(); it.typ != itemArrayEnd; it = p.next() {
+ if it.typ == itemCommentStart {
+ p.expect(itemText)
+ continue
+ }
+
+ val, typ := p.value(it, true)
+ array = append(array, val)
+
+ // XXX: type isn't used here, we need it to record the accurate type
+ // information.
+ //
+ // Not entirely sure how to best store this; could use "key[0]",
+ // "key[1]" notation, or maybe store it on the Array type?
+ _ = typ
+ }
+ return array, tomlArray
+}
+
+func (p *parser) valueInlineTable(it item, parentIsArray bool) (any, tomlType) {
+ var (
+ topHash = make(map[string]any)
+ outerContext = p.context
+ outerKey = p.currentKey
+ )
+
+ p.context = append(p.context, p.currentKey)
+ prevContext := p.context
+ p.currentKey = ""
+
+ p.addImplicit(p.context)
+ p.addContext(p.context, parentIsArray)
+
+ /// Loop over all table key/value pairs.
+ for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
+ if it.typ == itemCommentStart {
+ p.expect(itemText)
+ continue
+ }
+
+ /// Read all key parts.
+ k := p.nextPos()
+ var key Key
+ for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() {
+ key = append(key, p.keyString(k))
+ }
+ p.assertEqual(itemKeyEnd, k.typ)
+
+ /// The current key is the last part.
+ p.currentKey = key.last()
+
+ /// All the other parts (if any) are the context; need to set each part
+ /// as implicit.
+ context := key.parent()
+ for i := range context {
+ p.addImplicitContext(append(p.context, context[i:i+1]...))
+ }
+ p.ordered = append(p.ordered, p.context.add(p.currentKey))
+
+ /// Set the value.
+ val, typ := p.value(p.next(), false)
+ p.setValue(p.currentKey, val)
+ p.setType(p.currentKey, typ, it.pos)
+
+ hash := topHash
+ for _, c := range context {
+ h, ok := hash[c]
+ if !ok {
+ h = make(map[string]any)
+ hash[c] = h
+ }
+ hash, ok = h.(map[string]any)
+ if !ok {
+ p.panicf("%q is not a table", p.context)
+ }
+ }
+ hash[p.currentKey] = val
+
+ /// Restore context.
+ p.context = prevContext
+ }
+ p.context = outerContext
+ p.currentKey = outerKey
+ return topHash, tomlHash
+}
+
+// numHasLeadingZero checks if this number has leading zeroes, allowing for '0',
+// +/- signs, and base prefixes.
+func numHasLeadingZero(s string) bool {
+ if len(s) > 1 && s[0] == '0' && !(s[1] == 'b' || s[1] == 'o' || s[1] == 'x') { // Allow 0b, 0o, 0x
+ return true
+ }
+ if len(s) > 2 && (s[0] == '-' || s[0] == '+') && s[1] == '0' {
+ return true
+ }
+ return false
+}
+
+// numUnderscoresOK checks whether each underscore in s is surrounded by
+// characters that are not underscores.
+func numUnderscoresOK(s string) bool {
+ switch s {
+ case "nan", "+nan", "-nan", "inf", "-inf", "+inf":
+ return true
+ }
+ accept := false
+ for _, r := range s {
+ if r == '_' {
+ if !accept {
+ return false
+ }
+ }
+
+ // isHex is a superset of all the permissible characters surrounding an
+ // underscore.
+ accept = isHex(r)
+ }
+ return accept
+}
+
+// numPeriodsOK checks whether every period in s is followed by a digit.
+func numPeriodsOK(s string) bool {
+ period := false
+ for _, r := range s {
+ if period && !isDigit(r) {
+ return false
+ }
+ period = r == '.'
+ }
+ return !period
+}
+
+// Set the current context of the parser, where the context is either a hash or
+// an array of hashes, depending on the value of the `array` parameter.
+//
+// Establishing the context also makes sure that the key isn't a duplicate, and
+// will create implicit hashes automatically.
+func (p *parser) addContext(key Key, array bool) {
+ /// Always start at the top level and drill down for our context.
+ hashContext := p.mapping
+ keyContext := make(Key, 0, len(key)-1)
+
+ /// We only need implicit hashes for the parents.
+ for _, k := range key.parent() {
+ _, ok := hashContext[k]
+ keyContext = append(keyContext, k)
+
+ // No key? Make an implicit hash and move on.
+ if !ok {
+ p.addImplicit(keyContext)
+ hashContext[k] = make(map[string]any)
+ }
+
+ // If the hash context is actually an array of tables, then set
+ // the hash context to the last element in that array.
+ //
+ // Otherwise, it better be a table, since this MUST be a key group (by
+ // virtue of it not being the last element in a key).
+ switch t := hashContext[k].(type) {
+ case []map[string]any:
+ hashContext = t[len(t)-1]
+ case map[string]any:
+ hashContext = t
+ default:
+ p.panicf("Key '%s' was already created as a hash.", keyContext)
+ }
+ }
+
+ p.context = keyContext
+ if array {
+ // If this is the first element for this array, then allocate a new
+ // list of tables for it.
+ k := key.last()
+ if _, ok := hashContext[k]; !ok {
+ hashContext[k] = make([]map[string]any, 0, 4)
+ }
+
+ // Add a new table. But make sure the key hasn't already been used
+ // for something else.
+ if hash, ok := hashContext[k].([]map[string]any); ok {
+ hashContext[k] = append(hash, make(map[string]any))
+ } else {
+ p.panicf("Key '%s' was already created and cannot be used as an array.", key)
+ }
+ } else {
+ p.setValue(key.last(), make(map[string]any))
+ }
+ p.context = append(p.context, key.last())
+}
+
+// setValue sets the given key to the given value in the current context.
+// It will make sure that the key hasn't already been defined, account for
+// implicit key groups.
+func (p *parser) setValue(key string, value any) {
+ var (
+ tmpHash any
+ ok bool
+ hash = p.mapping
+ keyContext = make(Key, 0, len(p.context)+1)
+ )
+ for _, k := range p.context {
+ keyContext = append(keyContext, k)
+ if tmpHash, ok = hash[k]; !ok {
+ p.bug("Context for key '%s' has not been established.", keyContext)
+ }
+ switch t := tmpHash.(type) {
+ case []map[string]any:
+ // The context is a table of hashes. Pick the most recent table
+ // defined as the current hash.
+ hash = t[len(t)-1]
+ case map[string]any:
+ hash = t
+ default:
+ p.panicf("Key '%s' has already been defined.", keyContext)
+ }
+ }
+ keyContext = append(keyContext, key)
+
+ if _, ok := hash[key]; ok {
+ // Normally redefining keys isn't allowed, but the key could have been
+ // defined implicitly and it's allowed to be redefined concretely. (See
+ // the `valid/implicit-and-explicit-after.toml` in toml-test)
+ //
+ // But we have to make sure to stop marking it as an implicit. (So that
+ // another redefinition provokes an error.)
+ //
+ // Note that since it has already been defined (as a hash), we don't
+ // want to overwrite it. So our business is done.
+ if p.isArray(keyContext) {
+ p.removeImplicit(keyContext)
+ hash[key] = value
+ return
+ }
+ if p.isImplicit(keyContext) {
+ p.removeImplicit(keyContext)
+ return
+ }
+ // Otherwise, we have a concrete key trying to override a previous key,
+ // which is *always* wrong.
+ p.panicf("Key '%s' has already been defined.", keyContext)
+ }
+
+ hash[key] = value
+}
+
+// setType sets the type of a particular value at a given key. It should be
+// called immediately AFTER setValue.
+//
+// Note that if `key` is empty, then the type given will be applied to the
+// current context (which is either a table or an array of tables).
+func (p *parser) setType(key string, typ tomlType, pos Position) {
+ keyContext := make(Key, 0, len(p.context)+1)
+ keyContext = append(keyContext, p.context...)
+ if len(key) > 0 { // allow type setting for hashes
+ keyContext = append(keyContext, key)
+ }
+ // Special case to make empty keys ("" = 1) work.
+ // Without it it will set "" rather than `""`.
+ // TODO: why is this needed? And why is this only needed here?
+ if len(keyContext) == 0 {
+ keyContext = Key{""}
+ }
+ p.keyInfo[keyContext.String()] = keyInfo{tomlType: typ, pos: pos}
+}
+
+// Implicit keys need to be created when tables are implied in "a.b.c.d = 1" and
+// "[a.b.c]" (the "a", "b", and "c" hashes are never created explicitly).
+func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = struct{}{} }
+func (p *parser) removeImplicit(key Key) { delete(p.implicits, key.String()) }
+func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok }
+func (p *parser) isArray(key Key) bool { return p.keyInfo[key.String()].tomlType == tomlArray }
+func (p *parser) addImplicitContext(key Key) { p.addImplicit(key); p.addContext(key, false) }
+
+// current returns the full key name of the current context.
+func (p *parser) current() string {
+ if len(p.currentKey) == 0 {
+ return p.context.String()
+ }
+ if len(p.context) == 0 {
+ return p.currentKey
+ }
+ return fmt.Sprintf("%s.%s", p.context, p.currentKey)
+}
+
+func stripFirstNewline(s string) string {
+ if len(s) > 0 && s[0] == '\n' {
+ return s[1:]
+ }
+ if len(s) > 1 && s[0] == '\r' && s[1] == '\n' {
+ return s[2:]
+ }
+ return s
+}
+
+// stripEscapedNewlines removes whitespace after line-ending backslashes in
+// multiline strings.
+//
+// A line-ending backslash is an unescaped \ followed only by whitespace until
+// the next newline. After a line-ending backslash, all whitespace is removed
+// until the next non-whitespace character.
+func (p *parser) stripEscapedNewlines(s string) string {
+ var (
+ b strings.Builder
+ i int
+ )
+ b.Grow(len(s))
+ for {
+ ix := strings.Index(s[i:], `\`)
+ if ix < 0 {
+ b.WriteString(s)
+ return b.String()
+ }
+ i += ix
+
+ if len(s) > i+1 && s[i+1] == '\\' {
+ // Escaped backslash.
+ i += 2
+ continue
+ }
+ // Scan until the next non-whitespace.
+ j := i + 1
+ whitespaceLoop:
+ for ; j < len(s); j++ {
+ switch s[j] {
+ case ' ', '\t', '\r', '\n':
+ default:
+ break whitespaceLoop
+ }
+ }
+ if j == i+1 {
+ // Not a whitespace escape.
+ i++
+ continue
+ }
+ if !strings.Contains(s[i:j], "\n") {
+ // This is not a line-ending backslash. (It's a bad escape sequence,
+ // but we can let replaceEscapes catch it.)
+ i++
+ continue
+ }
+ b.WriteString(s[:i])
+ s = s[j:]
+ i = 0
+ }
+}
+
+func (p *parser) replaceEscapes(it item, str string) string {
+ var (
+ b strings.Builder
+ skip = 0
+ )
+ b.Grow(len(str))
+ for i, c := range str {
+ if skip > 0 {
+ skip--
+ continue
+ }
+ if c != '\\' {
+ b.WriteRune(c)
+ continue
+ }
+
+ if i >= len(str) {
+ p.bug("Escape sequence at end of string.")
+ return ""
+ }
+ switch str[i+1] {
+ default:
+ p.bug("Expected valid escape code after \\, but got %q.", str[i+1])
+ case ' ', '\t':
+ p.panicItemf(it, "invalid escape: '\\%c'", str[i+1])
+ case 'b':
+ b.WriteByte(0x08)
+ skip = 1
+ case 't':
+ b.WriteByte(0x09)
+ skip = 1
+ case 'n':
+ b.WriteByte(0x0a)
+ skip = 1
+ case 'f':
+ b.WriteByte(0x0c)
+ skip = 1
+ case 'r':
+ b.WriteByte(0x0d)
+ skip = 1
+ case 'e':
+ if p.tomlNext {
+ b.WriteByte(0x1b)
+ skip = 1
+ }
+ case '"':
+ b.WriteByte(0x22)
+ skip = 1
+ case '\\':
+ b.WriteByte(0x5c)
+ skip = 1
+ // The lexer guarantees the correct number of characters are present;
+ // don't need to check here.
+ case 'x':
+ if p.tomlNext {
+ escaped := p.asciiEscapeToUnicode(it, str[i+2:i+4])
+ b.WriteRune(escaped)
+ skip = 3
+ }
+ case 'u':
+ escaped := p.asciiEscapeToUnicode(it, str[i+2:i+6])
+ b.WriteRune(escaped)
+ skip = 5
+ case 'U':
+ escaped := p.asciiEscapeToUnicode(it, str[i+2:i+10])
+ b.WriteRune(escaped)
+ skip = 9
+ }
+ }
+ return b.String()
+}
+
+func (p *parser) asciiEscapeToUnicode(it item, s string) rune {
+ hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32)
+ if err != nil {
+ p.bug("Could not parse '%s' as a hexadecimal number, but the lexer claims it's OK: %s", s, err)
+ }
+ if !utf8.ValidRune(rune(hex)) {
+ p.panicItemf(it, "Escaped character '\\u%s' is not valid UTF-8.", s)
+ }
+ return rune(hex)
+}
diff --git a/vendor/github.com/BurntSushi/toml/type_fields.go b/vendor/github.com/BurntSushi/toml/type_fields.go
new file mode 100644
index 00000000..10c51f7e
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/type_fields.go
@@ -0,0 +1,238 @@
+package toml
+
+// Struct field handling is adapted from code in encoding/json:
+//
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the Go distribution.
+
+import (
+ "reflect"
+ "sort"
+ "sync"
+)
+
+// A field represents a single field found in a struct.
+type field struct {
+ name string // the name of the field (`toml` tag included)
+ tag bool // whether field has a `toml` tag
+ index []int // represents the depth of an anonymous field
+ typ reflect.Type // the type of the field
+}
+
+// byName sorts field by name, breaking ties with depth,
+// then breaking ties with "name came from toml tag", then
+// breaking ties with index sequence.
+type byName []field
+
+func (x byName) Len() int { return len(x) }
+func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x byName) Less(i, j int) bool {
+ if x[i].name != x[j].name {
+ return x[i].name < x[j].name
+ }
+ if len(x[i].index) != len(x[j].index) {
+ return len(x[i].index) < len(x[j].index)
+ }
+ if x[i].tag != x[j].tag {
+ return x[i].tag
+ }
+ return byIndex(x).Less(i, j)
+}
+
+// byIndex sorts field by index sequence.
+type byIndex []field
+
+func (x byIndex) Len() int { return len(x) }
+func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x byIndex) Less(i, j int) bool {
+ for k, xik := range x[i].index {
+ if k >= len(x[j].index) {
+ return false
+ }
+ if xik != x[j].index[k] {
+ return xik < x[j].index[k]
+ }
+ }
+ return len(x[i].index) < len(x[j].index)
+}
+
+// typeFields returns a list of fields that TOML should recognize for the given
+// type. The algorithm is breadth-first search over the set of structs to
+// include - the top struct and then any reachable anonymous structs.
+func typeFields(t reflect.Type) []field {
+ // Anonymous fields to explore at the current level and the next.
+ current := []field{}
+ next := []field{{typ: t}}
+
+ // Count of queued names for current level and the next.
+ var count map[reflect.Type]int
+ var nextCount map[reflect.Type]int
+
+ // Types already visited at an earlier level.
+ visited := map[reflect.Type]bool{}
+
+ // Fields found.
+ var fields []field
+
+ for len(next) > 0 {
+ current, next = next, current[:0]
+ count, nextCount = nextCount, map[reflect.Type]int{}
+
+ for _, f := range current {
+ if visited[f.typ] {
+ continue
+ }
+ visited[f.typ] = true
+
+ // Scan f.typ for fields to include.
+ for i := 0; i < f.typ.NumField(); i++ {
+ sf := f.typ.Field(i)
+ if sf.PkgPath != "" && !sf.Anonymous { // unexported
+ continue
+ }
+ opts := getOptions(sf.Tag)
+ if opts.skip {
+ continue
+ }
+ index := make([]int, len(f.index)+1)
+ copy(index, f.index)
+ index[len(f.index)] = i
+
+ ft := sf.Type
+ if ft.Name() == "" && ft.Kind() == reflect.Ptr {
+ // Follow pointer.
+ ft = ft.Elem()
+ }
+
+ // Record found field and index sequence.
+ if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
+ tagged := opts.name != ""
+ name := opts.name
+ if name == "" {
+ name = sf.Name
+ }
+ fields = append(fields, field{name, tagged, index, ft})
+ if count[f.typ] > 1 {
+ // If there were multiple instances, add a second,
+ // so that the annihilation code will see a duplicate.
+ // It only cares about the distinction between 1 or 2,
+ // so don't bother generating any more copies.
+ fields = append(fields, fields[len(fields)-1])
+ }
+ continue
+ }
+
+ // Record new anonymous struct to explore in next round.
+ nextCount[ft]++
+ if nextCount[ft] == 1 {
+ f := field{name: ft.Name(), index: index, typ: ft}
+ next = append(next, f)
+ }
+ }
+ }
+ }
+
+ sort.Sort(byName(fields))
+
+ // Delete all fields that are hidden by the Go rules for embedded fields,
+ // except that fields with TOML tags are promoted.
+
+ // The fields are sorted in primary order of name, secondary order
+ // of field index length. Loop over names; for each name, delete
+ // hidden fields by choosing the one dominant field that survives.
+ out := fields[:0]
+ for advance, i := 0, 0; i < len(fields); i += advance {
+ // One iteration per name.
+ // Find the sequence of fields with the name of this first field.
+ fi := fields[i]
+ name := fi.name
+ for advance = 1; i+advance < len(fields); advance++ {
+ fj := fields[i+advance]
+ if fj.name != name {
+ break
+ }
+ }
+ if advance == 1 { // Only one field with this name
+ out = append(out, fi)
+ continue
+ }
+ dominant, ok := dominantField(fields[i : i+advance])
+ if ok {
+ out = append(out, dominant)
+ }
+ }
+
+ fields = out
+ sort.Sort(byIndex(fields))
+
+ return fields
+}
+
+// dominantField looks through the fields, all of which are known to
+// have the same name, to find the single field that dominates the
+// others using Go's embedding rules, modified by the presence of
+// TOML tags. If there are multiple top-level fields, the boolean
+// will be false: This condition is an error in Go and we skip all
+// the fields.
+func dominantField(fields []field) (field, bool) {
+ // The fields are sorted in increasing index-length order. The winner
+ // must therefore be one with the shortest index length. Drop all
+ // longer entries, which is easy: just truncate the slice.
+ length := len(fields[0].index)
+ tagged := -1 // Index of first tagged field.
+ for i, f := range fields {
+ if len(f.index) > length {
+ fields = fields[:i]
+ break
+ }
+ if f.tag {
+ if tagged >= 0 {
+ // Multiple tagged fields at the same level: conflict.
+ // Return no field.
+ return field{}, false
+ }
+ tagged = i
+ }
+ }
+ if tagged >= 0 {
+ return fields[tagged], true
+ }
+ // All remaining fields have the same length. If there's more than one,
+ // we have a conflict (two fields named "X" at the same level) and we
+ // return no field.
+ if len(fields) > 1 {
+ return field{}, false
+ }
+ return fields[0], true
+}
+
+var fieldCache struct {
+ sync.RWMutex
+ m map[reflect.Type][]field
+}
+
+// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
+func cachedTypeFields(t reflect.Type) []field {
+ fieldCache.RLock()
+ f := fieldCache.m[t]
+ fieldCache.RUnlock()
+ if f != nil {
+ return f
+ }
+
+ // Compute fields without lock.
+ // Might duplicate effort but won't hold other computations back.
+ f = typeFields(t)
+ if f == nil {
+ f = []field{}
+ }
+
+ fieldCache.Lock()
+ if fieldCache.m == nil {
+ fieldCache.m = map[reflect.Type][]field{}
+ }
+ fieldCache.m[t] = f
+ fieldCache.Unlock()
+ return f
+}
diff --git a/vendor/github.com/BurntSushi/toml/type_toml.go b/vendor/github.com/BurntSushi/toml/type_toml.go
new file mode 100644
index 00000000..1c090d33
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/type_toml.go
@@ -0,0 +1,65 @@
+package toml
+
+// tomlType represents any Go type that corresponds to a TOML type.
+// While the first draft of the TOML spec has a simplistic type system that
+// probably doesn't need this level of sophistication, we seem to be militating
+// toward adding real composite types.
+type tomlType interface {
+ typeString() string
+}
+
+// typeEqual accepts any two types and returns true if they are equal.
+func typeEqual(t1, t2 tomlType) bool {
+ if t1 == nil || t2 == nil {
+ return false
+ }
+ return t1.typeString() == t2.typeString()
+}
+
+func typeIsTable(t tomlType) bool {
+ return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash)
+}
+
+type tomlBaseType string
+
+func (btype tomlBaseType) typeString() string { return string(btype) }
+func (btype tomlBaseType) String() string { return btype.typeString() }
+
+var (
+ tomlInteger tomlBaseType = "Integer"
+ tomlFloat tomlBaseType = "Float"
+ tomlDatetime tomlBaseType = "Datetime"
+ tomlString tomlBaseType = "String"
+ tomlBool tomlBaseType = "Bool"
+ tomlArray tomlBaseType = "Array"
+ tomlHash tomlBaseType = "Hash"
+ tomlArrayHash tomlBaseType = "ArrayHash"
+)
+
+// typeOfPrimitive returns a tomlType of any primitive value in TOML.
+// Primitive values are: Integer, Float, Datetime, String and Bool.
+//
+// Passing a lexer item other than the following will cause a BUG message
+// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime.
+func (p *parser) typeOfPrimitive(lexItem item) tomlType {
+ switch lexItem.typ {
+ case itemInteger:
+ return tomlInteger
+ case itemFloat:
+ return tomlFloat
+ case itemDatetime:
+ return tomlDatetime
+ case itemString, itemStringEsc:
+ return tomlString
+ case itemMultilineString:
+ return tomlString
+ case itemRawString:
+ return tomlString
+ case itemRawMultilineString:
+ return tomlString
+ case itemBool:
+ return tomlBool
+ }
+ p.bug("Cannot infer primitive type of lex item '%s'.", lexItem)
+ panic("unreachable")
+}
diff --git a/vendor/github.com/VividCortex/ewma/.gitignore b/vendor/github.com/VividCortex/ewma/.gitignore
new file mode 100644
index 00000000..c66769f6
--- /dev/null
+++ b/vendor/github.com/VividCortex/ewma/.gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+.*.sw?
+/coverage.txt
\ No newline at end of file
diff --git a/vendor/github.com/VividCortex/ewma/.whitesource b/vendor/github.com/VividCortex/ewma/.whitesource
new file mode 100644
index 00000000..d7eebc0c
--- /dev/null
+++ b/vendor/github.com/VividCortex/ewma/.whitesource
@@ -0,0 +1,3 @@
+{
+ "settingsInheritedFrom": "VividCortex/whitesource-config@master"
+}
\ No newline at end of file
diff --git a/vendor/github.com/VividCortex/ewma/LICENSE b/vendor/github.com/VividCortex/ewma/LICENSE
new file mode 100644
index 00000000..a78d643e
--- /dev/null
+++ b/vendor/github.com/VividCortex/ewma/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2013 VividCortex
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/VividCortex/ewma/README.md b/vendor/github.com/VividCortex/ewma/README.md
new file mode 100644
index 00000000..87b4a3c7
--- /dev/null
+++ b/vendor/github.com/VividCortex/ewma/README.md
@@ -0,0 +1,145 @@
+# EWMA
+
+[](https://godoc.org/github.com/VividCortex/ewma)
+
+[](https://codecov.io/gh/VividCortex/ewma)
+
+This repo provides Exponentially Weighted Moving Average algorithms, or EWMAs for short, [based on our
+Quantifying Abnormal Behavior talk](https://vividcortex.com/blog/2013/07/23/a-fast-go-library-for-exponential-moving-averages/).
+
+### Exponentially Weighted Moving Average
+
+An exponentially weighted moving average is a way to continuously compute a type of
+average for a series of numbers, as the numbers arrive. After a value in the series is
+added to the average, its weight in the average decreases exponentially over time. This
+biases the average towards more recent data. EWMAs are useful for several reasons, chiefly
+their inexpensive computational and memory cost, as well as the fact that they represent
+the recent central tendency of the series of values.
+
+The EWMA algorithm requires a decay factor, alpha. The larger the alpha, the more the average
+is biased towards recent history. The alpha must be between 0 and 1, and is typically
+a fairly small number, such as 0.04. We will discuss the choice of alpha later.
+
+The algorithm works thus, in pseudocode:
+
+1. Multiply the next number in the series by alpha.
+2. Multiply the current value of the average by 1 minus alpha.
+3. Add the result of steps 1 and 2, and store it as the new current value of the average.
+4. Repeat for each number in the series.
+
+There are special-case behaviors for how to initialize the current value, and these vary
+between implementations. One approach is to start with the first value in the series;
+another is to average the first 10 or so values in the series using an arithmetic average,
+and then begin the incremental updating of the average. Each method has pros and cons.
+
+It may help to look at it pictorially. Suppose the series has five numbers, and we choose
+alpha to be 0.50 for simplicity. Here's the series, with numbers in the neighborhood of 300.
+
+
+
+Now let's take the moving average of those numbers. First we set the average to the value
+of the first number.
+
+
+
+Next we multiply the next number by alpha, multiply the current value by 1-alpha, and add
+them to generate a new value.
+
+
+
+This continues until we are done.
+
+
+
+Notice how each of the values in the series decays by half each time a new value
+is added, and the top of the bars in the lower portion of the image represents the
+size of the moving average. It is a smoothed, or low-pass, average of the original
+series.
+
+For further reading, see [Exponentially weighted moving average](http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average) on wikipedia.
+
+### Choosing Alpha
+
+Consider a fixed-size sliding-window moving average (not an exponentially weighted moving average)
+that averages over the previous N samples. What is the average age of each sample? It is N/2.
+
+Now suppose that you wish to construct a EWMA whose samples have the same average age. The formula
+to compute the alpha required for this is: alpha = 2/(N+1). Proof is in the book
+"Production and Operations Analysis" by Steven Nahmias.
+
+So, for example, if you have a time-series with samples once per second, and you want to get the
+moving average over the previous minute, you should use an alpha of .032786885. This, by the way,
+is the constant alpha used for this repository's SimpleEWMA.
+
+### Implementations
+
+This repository contains two implementations of the EWMA algorithm, with different properties.
+
+The implementations all conform to the MovingAverage interface, and the constructor returns
+that type.
+
+Current implementations assume an implicit time interval of 1.0 between every sample added.
+That is, the passage of time is treated as though it's the same as the arrival of samples.
+If you need time-based decay when samples are not arriving precisely at set intervals, then
+this package will not support your needs at present.
+
+#### SimpleEWMA
+
+A SimpleEWMA is designed for low CPU and memory consumption. It **will** have different behavior than the VariableEWMA
+for multiple reasons. It has no warm-up period and it uses a constant
+decay. These properties let it use less memory. It will also behave
+differently when it's equal to zero, which is assumed to mean
+uninitialized, so if a value is likely to actually become zero over time,
+then any non-zero value will cause a sharp jump instead of a small change.
+
+#### VariableEWMA
+
+Unlike SimpleEWMA, this supports a custom age which must be stored, and thus uses more memory.
+It also has a "warmup" time when you start adding values to it. It will report a value of 0.0
+until you have added the required number of samples to it. It uses some memory to store the
+number of samples added to it. As a result it uses a little over twice the memory of SimpleEWMA.
+
+## Usage
+
+### API Documentation
+
+View the GoDoc generated documentation [here](http://godoc.org/github.com/VividCortex/ewma).
+
+```go
+package main
+
+import "github.com/VividCortex/ewma"
+
+func main() {
+ samples := [100]float64{
+ 4599, 5711, 4746, 4621, 5037, 4218, 4925, 4281, 5207, 5203, 5594, 5149,
+ }
+
+ e := ewma.NewMovingAverage() //=> Returns a SimpleEWMA if called without params
+ a := ewma.NewMovingAverage(5) //=> returns a VariableEWMA with a decay of 2 / (5 + 1)
+
+ for _, f := range samples {
+ e.Add(f)
+ a.Add(f)
+ }
+
+ e.Value() //=> 13.577404704631077
+ a.Value() //=> 1.5806140565521463e-12
+}
+```
+
+## Contributing
+
+We only accept pull requests for minor fixes or improvements. This includes:
+
+* Small bug fixes
+* Typos
+* Documentation or comments
+
+Please open issues to discuss new features. Pull requests for new features will be rejected,
+so we recommend forking the repository and making changes in your fork for your use case.
+
+## License
+
+This repository is Copyright (c) 2013 VividCortex, Inc. All rights reserved.
+It is licensed under the MIT license. Please see the LICENSE file for applicable license terms.
diff --git a/vendor/github.com/VividCortex/ewma/codecov.yml b/vendor/github.com/VividCortex/ewma/codecov.yml
new file mode 100644
index 00000000..0d36d903
--- /dev/null
+++ b/vendor/github.com/VividCortex/ewma/codecov.yml
@@ -0,0 +1,6 @@
+coverage:
+ status:
+ project:
+ default:
+ threshold: 15%
+ patch: off
diff --git a/vendor/github.com/VividCortex/ewma/ewma.go b/vendor/github.com/VividCortex/ewma/ewma.go
new file mode 100644
index 00000000..44d5d53e
--- /dev/null
+++ b/vendor/github.com/VividCortex/ewma/ewma.go
@@ -0,0 +1,126 @@
+// Package ewma implements exponentially weighted moving averages.
+package ewma
+
+// Copyright (c) 2013 VividCortex, Inc. All rights reserved.
+// Please see the LICENSE file for applicable license terms.
+
+const (
+ // By default, we average over a one-minute period, which means the average
+ // age of the metrics in the period is 30 seconds.
+ AVG_METRIC_AGE float64 = 30.0
+
+ // The formula for computing the decay factor from the average age comes
+ // from "Production and Operations Analysis" by Steven Nahmias.
+ DECAY float64 = 2 / (float64(AVG_METRIC_AGE) + 1)
+
+ // For best results, the moving average should not be initialized to the
+ // samples it sees immediately. The book "Production and Operations
+ // Analysis" by Steven Nahmias suggests initializing the moving average to
+ // the mean of the first 10 samples. Until the VariableEwma has seen this
+ // many samples, it is not "ready" to be queried for the value of the
+ // moving average. This adds some memory cost.
+ WARMUP_SAMPLES uint8 = 10
+)
+
+// MovingAverage is the interface that computes a moving average over a time-
+// series stream of numbers. The average may be over a window or exponentially
+// decaying.
+type MovingAverage interface {
+ Add(float64)
+ Value() float64
+ Set(float64)
+}
+
+// NewMovingAverage constructs a MovingAverage that computes an average with the
+// desired characteristics in the moving window or exponential decay. If no
+// age is given, it constructs a default exponentially weighted implementation
+// that consumes minimal memory. The age is related to the decay factor alpha
+// by the formula given for the DECAY constant. It signifies the average age
+// of the samples as time goes to infinity.
+func NewMovingAverage(age ...float64) MovingAverage {
+ if len(age) == 0 || age[0] == AVG_METRIC_AGE {
+ return new(SimpleEWMA)
+ }
+ return &VariableEWMA{
+ decay: 2 / (age[0] + 1),
+ }
+}
+
+// A SimpleEWMA represents the exponentially weighted moving average of a
+// series of numbers. It WILL have different behavior than the VariableEWMA
+// for multiple reasons. It has no warm-up period and it uses a constant
+// decay. These properties let it use less memory. It will also behave
+// differently when it's equal to zero, which is assumed to mean
+// uninitialized, so if a value is likely to actually become zero over time,
+// then any non-zero value will cause a sharp jump instead of a small change.
+// However, note that this takes a long time, and the value may just
+// decays to a stable value that's close to zero, but which won't be mistaken
+// for uninitialized. See http://play.golang.org/p/litxBDr_RC for example.
+type SimpleEWMA struct {
+ // The current value of the average. After adding with Add(), this is
+ // updated to reflect the average of all values seen thus far.
+ value float64
+}
+
+// Add adds a value to the series and updates the moving average.
+func (e *SimpleEWMA) Add(value float64) {
+ if e.value == 0 { // this is a proxy for "uninitialized"
+ e.value = value
+ } else {
+ e.value = (value * DECAY) + (e.value * (1 - DECAY))
+ }
+}
+
+// Value returns the current value of the moving average.
+func (e *SimpleEWMA) Value() float64 {
+ return e.value
+}
+
+// Set sets the EWMA's value.
+func (e *SimpleEWMA) Set(value float64) {
+ e.value = value
+}
+
+// VariableEWMA represents the exponentially weighted moving average of a series of
+// numbers. Unlike SimpleEWMA, it supports a custom age, and thus uses more memory.
+type VariableEWMA struct {
+ // The multiplier factor by which the previous samples decay.
+ decay float64
+ // The current value of the average.
+ value float64
+ // The number of samples added to this instance.
+ count uint8
+}
+
+// Add adds a value to the series and updates the moving average.
+func (e *VariableEWMA) Add(value float64) {
+ switch {
+ case e.count < WARMUP_SAMPLES:
+ e.count++
+ e.value += value
+ case e.count == WARMUP_SAMPLES:
+ e.count++
+ e.value = e.value / float64(WARMUP_SAMPLES)
+ e.value = (value * e.decay) + (e.value * (1 - e.decay))
+ default:
+ e.value = (value * e.decay) + (e.value * (1 - e.decay))
+ }
+}
+
+// Value returns the current value of the average, or 0.0 if the series hasn't
+// warmed up yet.
+func (e *VariableEWMA) Value() float64 {
+ if e.count <= WARMUP_SAMPLES {
+ return 0.0
+ }
+
+ return e.value
+}
+
+// Set sets the EWMA's value.
+func (e *VariableEWMA) Set(value float64) {
+ e.value = value
+ if e.count <= WARMUP_SAMPLES {
+ e.count = WARMUP_SAMPLES + 1
+ }
+}
diff --git a/vendor/github.com/acarl005/stripansi/LICENSE b/vendor/github.com/acarl005/stripansi/LICENSE
new file mode 100644
index 00000000..00abe0db
--- /dev/null
+++ b/vendor/github.com/acarl005/stripansi/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Andrew Carlson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/acarl005/stripansi/README.md b/vendor/github.com/acarl005/stripansi/README.md
new file mode 100644
index 00000000..8bdb1f50
--- /dev/null
+++ b/vendor/github.com/acarl005/stripansi/README.md
@@ -0,0 +1,30 @@
+Strip ANSI
+==========
+
+This Go package removes ANSI escape codes from strings.
+
+Ideally, we would prevent these from appearing in any text we want to process.
+However, sometimes this can't be helped, and we need to be able to deal with that noise.
+This will use a regexp to remove those unwanted escape codes.
+
+
+## Install
+
+```sh
+$ go get -u github.com/acarl005/stripansi
+```
+
+## Usage
+
+```go
+import (
+ "fmt"
+ "github.com/acarl005/stripansi"
+)
+
+func main() {
+ msg := "\x1b[38;5;140m foo\x1b[0m bar"
+ cleanMsg := stripansi.Strip(msg)
+ fmt.Println(cleanMsg) // " foo bar"
+}
+```
diff --git a/vendor/github.com/acarl005/stripansi/stripansi.go b/vendor/github.com/acarl005/stripansi/stripansi.go
new file mode 100644
index 00000000..235732a7
--- /dev/null
+++ b/vendor/github.com/acarl005/stripansi/stripansi.go
@@ -0,0 +1,13 @@
+package stripansi
+
+import (
+ "regexp"
+)
+
+const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
+
+var re = regexp.MustCompile(ansi)
+
+func Strip(str string) string {
+ return re.ReplaceAllString(str, "")
+}
diff --git a/vendor/github.com/aead/serpent/.gitignore b/vendor/github.com/aead/serpent/.gitignore
new file mode 100644
index 00000000..9d3d8437
--- /dev/null
+++ b/vendor/github.com/aead/serpent/.gitignore
@@ -0,0 +1,25 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+.vscode
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/aead/serpent/LICENSE b/vendor/github.com/aead/serpent/LICENSE
new file mode 100644
index 00000000..b6a9210b
--- /dev/null
+++ b/vendor/github.com/aead/serpent/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Andreas Auernhammer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/aead/serpent/README.md b/vendor/github.com/aead/serpent/README.md
new file mode 100644
index 00000000..6dbceee8
--- /dev/null
+++ b/vendor/github.com/aead/serpent/README.md
@@ -0,0 +1,9 @@
+[](https://godoc.org/github.com/aead/serpent)
+
+## The Serpent block cipher
+
+Serpent is a symmetric key block cipher that was a finalist in the Advanced Encryption Standard (AES) contest,
+where it was ranked second to Rijndael. Serpent was designed by Ross Anderson, Eli Biham, and Lars Knudsen.
+
+### Installation
+Install in your GOPATH: `go get -u github.com/aead/serpent`
diff --git a/vendor/github.com/aead/serpent/sbox_ref.go b/vendor/github.com/aead/serpent/sbox_ref.go
new file mode 100644
index 00000000..515afc69
--- /dev/null
+++ b/vendor/github.com/aead/serpent/sbox_ref.go
@@ -0,0 +1,316 @@
+// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
+// Use of this source code is governed by a license that can be
+// found in the LICENSE file.
+
+package serpent
+
+// The linear transformation of serpent
+// This version, tries not to minimize the
+// number of registers, but maximize parallism.
+func linear(v0, v1, v2, v3 *uint32) {
+ t0 := ((*v0 << 13) | (*v0 >> (32 - 13)))
+ t2 := ((*v2 << 3) | (*v2 >> (32 - 3)))
+ t1 := *v1 ^ t0 ^ t2
+ t3 := *v3 ^ t2 ^ (t0 << 3)
+ *v1 = (t1 << 1) | (t1 >> (32 - 1))
+ *v3 = (t3 << 7) | (t3 >> (32 - 7))
+ t0 ^= *v1 ^ *v3
+ t2 ^= *v3 ^ (*v1 << 7)
+ *v0 = (t0 << 5) | (t0 >> (32 - 5))
+ *v2 = (t2 << 22) | (t2 >> (32 - 22))
+}
+
+// The inverse linear transformation of serpent
+// This version, tries not to minimize the
+// number of registers, but maximize parallism.
+func linearInv(v0, v1, v2, v3 *uint32) {
+ t2 := (*v2 >> 22) | (*v2 << (32 - 22))
+ t0 := (*v0 >> 5) | (*v0 << (32 - 5))
+ t2 ^= *v3 ^ (*v1 << 7)
+ t0 ^= *v1 ^ *v3
+ t3 := (*v3 >> 7) | (*v3 << (32 - 7))
+ t1 := (*v1 >> 1) | (*v1 << (32 - 1))
+ *v3 = t3 ^ t2 ^ (t0 << 3)
+ *v1 = t1 ^ t0 ^ t2
+ *v2 = (t2 >> 3) | (t2 << (32 - 3))
+ *v0 = (t0 >> 13) | (t0 << (32 - 13))
+}
+
+// The following functions sb0,sb1, ..., sb7 represent the 8 Serpent S-Boxes.
+// sb0Inv til sb7Inv are the inverse functions (e.g. sb0Inv is the Inverse to sb0
+// and vice versa).
+// The S-Boxes differ from the original Serpent definitions. This is for
+// optimisation. The functions use the Serpent S-Box improvements for (non x86)
+// from Dr. B. R. Gladman and Sam Simpson.
+
+// S-Box 0
+func sb0(r0, r1, r2, r3 *uint32) {
+ t0 := *r0 ^ *r3
+ t1 := *r2 ^ t0
+ t2 := *r1 ^ t1
+ *r3 = (*r0 & *r3) ^ t2
+ t3 := *r0 ^ (*r1 & t0)
+ *r2 = t2 ^ (*r2 | t3)
+ t4 := *r3 & (t1 ^ t3)
+ *r1 = (^t1) ^ t4
+ *r0 = t4 ^ (^t3)
+}
+
+// Inverse S-Box 0
+func sb0Inv(r0, r1, r2, r3 *uint32) {
+ t0 := ^(*r0)
+ t1 := *r0 ^ *r1
+ t2 := *r3 ^ (t0 | t1)
+ t3 := *r2 ^ t2
+ *r2 = t1 ^ t3
+ t4 := t0 ^ (*r3 & t1)
+ *r1 = t2 ^ (*r2 & t4)
+ *r3 = (*r0 & t2) ^ (t3 | *r1)
+ *r0 = *r3 ^ (t3 ^ t4)
+}
+
+// S-Box 1
+func sb1(r0, r1, r2, r3 *uint32) {
+ t0 := *r1 ^ (^(*r0))
+ t1 := *r2 ^ (*r0 | t0)
+ *r2 = *r3 ^ t1
+ t2 := *r1 ^ (*r3 | t0)
+ t3 := t0 ^ *r2
+ *r3 = t3 ^ (t1 & t2)
+ t4 := t1 ^ t2
+ *r1 = *r3 ^ t4
+ *r0 = t1 ^ (t3 & t4)
+}
+
+// Inverse S-Box 1
+func sb1Inv(r0, r1, r2, r3 *uint32) {
+ t0 := *r1 ^ *r3
+ t1 := *r0 ^ (*r1 & t0)
+ t2 := t0 ^ t1
+ *r3 = *r2 ^ t2
+ t3 := *r1 ^ (t0 & t1)
+ t4 := *r3 | t3
+ *r1 = t1 ^ t4
+ t5 := ^(*r1)
+ t6 := *r3 ^ t3
+ *r0 = t5 ^ t6
+ *r2 = t2 ^ (t5 | t6)
+}
+
+// S-Box 2
+func sb2(r0, r1, r2, r3 *uint32) {
+ v0 := *r0 // save r0
+ v3 := *r3 // save r3
+ t0 := ^v0
+ t1 := *r1 ^ v3
+ t2 := *r2 & t0
+ *r0 = t1 ^ t2
+ t3 := *r2 ^ t0
+ t4 := *r2 ^ *r0
+ t5 := *r1 & t4
+ *r3 = t3 ^ t5
+ *r2 = v0 ^ ((v3 | t5) & (*r0 | t3))
+ *r1 = (t1 ^ *r3) ^ (*r2 ^ (v3 | t0))
+}
+
+// Inverse S-Box 2
+func sb2Inv(r0, r1, r2, r3 *uint32) {
+ v0 := *r0 // save r0
+ v3 := *r3 // save r3
+ t0 := *r1 ^ v3
+ t1 := ^t0
+ t2 := v0 ^ *r2
+ t3 := *r2 ^ t0
+ t4 := *r1 & t3
+ *r0 = t2 ^ t4
+ t5 := v0 | t1
+ t6 := v3 ^ t5
+ t7 := t2 | t6
+ *r3 = t0 ^ t7
+ t8 := ^t3
+ t9 := *r0 | *r3
+ *r1 = t8 ^ t9
+ *r2 = (v3 & t8) ^ (t2 ^ t9)
+}
+
+// S-Box 3
+func sb3(r0, r1, r2, r3 *uint32) {
+ v1 := *r1 // save r1
+ v3 := *r3 // save r3
+ t0 := *r0 ^ *r1
+ t1 := *r0 & *r2
+ t2 := *r0 | *r3
+ t3 := *r2 ^ *r3
+ t4 := t0 & t2
+ t5 := t1 | t4
+ *r2 = t3 ^ t5
+ t6 := *r1 ^ t2
+ t7 := t5 ^ t6
+ t8 := t3 & t7
+ *r0 = t0 ^ t8
+ t9 := *r2 & *r0
+ *r1 = t7 ^ t9
+ *r3 = (v1 | v3) ^ (t3 ^ t9)
+}
+
+// Inverse S-Box 3
+func sb3Inv(r0, r1, r2, r3 *uint32) {
+ t0 := *r0 | *r1
+ t1 := *r1 ^ *r2
+ t2 := *r1 & t1
+ t3 := *r0 ^ t2
+ t4 := *r2 ^ t3
+ t5 := *r3 | t3
+ *r0 = t1 ^ t5
+ t6 := t1 | t5
+ t7 := *r3 ^ t6
+ *r2 = t4 ^ t7
+ t8 := t0 ^ t7
+ t9 := *r0 & t8
+ *r3 = t3 ^ t9
+ *r1 = *r3 ^ (*r0 ^ t8)
+}
+
+// S-Box 4
+func sb4(r0, r1, r2, r3 *uint32) {
+ v0 := *r0 // save r0
+ t0 := v0 ^ *r3
+ t1 := *r3 & t0
+ t2 := *r2 ^ t1
+ t3 := *r1 | t2
+ *r3 = t0 ^ t3
+ t4 := ^(*r1)
+ t5 := t0 | t4
+ *r0 = t2 ^ t5
+ t6 := v0 & *r0
+ t7 := t0 ^ t4
+ t8 := t3 & t7
+ *r2 = t6 ^ t8
+ *r1 = (v0 ^ t2) ^ (t7 & *r2)
+}
+
+// Inverse S-Box 4
+func sb4Inv(r0, r1, r2, r3 *uint32) {
+ v3 := *r3 // save r3
+ t0 := *r2 | v3
+ t1 := *r0 & t0
+ t2 := *r1 ^ t1
+ t3 := *r0 & t2
+ t4 := *r2 ^ t3
+ *r1 = v3 ^ t4
+ t5 := ^(*r0)
+ t6 := t4 & *r1
+ *r3 = t2 ^ t6
+ t7 := *r1 | t5
+ t8 := v3 ^ t7
+ *r0 = *r3 ^ t8
+ *r2 = (t2 & t8) ^ (*r1 ^ t5)
+}
+
+// S-Box 5
+func sb5(r0, r1, r2, r3 *uint32) {
+ v1 := *r1 // save r1
+ t0 := ^(*r0)
+ t1 := *r0 ^ v1
+ t2 := *r0 ^ *r3
+ t3 := *r2 ^ t0
+ t4 := t1 | t2
+ *r0 = t3 ^ t4
+ t5 := *r3 & *r0
+ t6 := t1 ^ *r0
+ *r1 = t5 ^ t6
+ t7 := t0 | *r0
+ t8 := t1 | t5
+ t9 := t2 ^ t7
+ *r2 = t8 ^ t9
+ *r3 = (v1 ^ t5) ^ (*r1 & t9)
+}
+
+// Inverse S-Box 5
+func sb5Inv(r0, r1, r2, r3 *uint32) {
+ v0 := *r0 // save r0
+ v1 := *r1 // save r1
+ v3 := *r3 // save r3
+ t0 := ^(*r2)
+ t1 := v1 & t0
+ t2 := v3 ^ t1
+ t3 := v0 & t2
+ t4 := v1 ^ t0
+ *r3 = t3 ^ t4
+ t5 := v1 | *r3
+ t6 := v0 & t5
+ *r1 = t2 ^ t6
+ t7 := v0 | v3
+ t8 := t0 ^ t5
+ *r0 = t7 ^ t8
+ *r2 = (v1 & t7) ^ (t3 | (v0 ^ *r2))
+}
+
+// S-Box 6
+func sb6(r0, r1, r2, r3 *uint32) {
+ t0 := ^(*r0)
+ t1 := *r0 ^ *r3
+ t2 := *r1 ^ t1
+ t3 := t0 | t1
+ t4 := *r2 ^ t3
+ *r1 = *r1 ^ t4
+ t5 := t1 | *r1
+ t6 := *r3 ^ t5
+ t7 := t4 & t6
+ *r2 = t2 ^ t7
+ t8 := t4 ^ t6
+ *r0 = *r2 ^ t8
+ *r3 = (^t4) ^ (t2 & t8)
+}
+
+// Inverse S-Box 6
+func sb6Inv(r0, r1, r2, r3 *uint32) {
+ v1 := *r1 // save r1
+ v3 := *r3 // save r3
+ t0 := ^(*r0)
+ t1 := *r0 ^ v1
+ t2 := *r2 ^ t1
+ t3 := *r2 | t0
+ t4 := v3 ^ t3
+ *r1 = t2 ^ t4
+ t5 := t2 & t4
+ t6 := t1 ^ t5
+ t7 := v1 | t6
+ *r3 = t4 ^ t7
+ t8 := v1 | *r3
+ *r0 = t6 ^ t8
+ *r2 = (v3 & t0) ^ (t2 ^ t8)
+}
+
+// S-Box 7
+func sb7(r0, r1, r2, r3 *uint32) {
+ t0 := *r1 ^ *r2
+ t1 := *r2 & t0
+ t2 := *r3 ^ t1
+ t3 := *r0 ^ t2
+ t4 := *r3 | t0
+ t5 := t3 & t4
+ *r1 = *r1 ^ t5
+ t6 := t2 | *r1
+ t7 := *r0 & t3
+ *r3 = t0 ^ t7
+ t8 := t3 ^ t6
+ t9 := *r3 & t8
+ *r2 = t2 ^ t9
+ *r0 = (^t8) ^ (*r3 & *r2)
+}
+
+// Inverse S-Box 7
+func sb7Inv(r0, r1, r2, r3 *uint32) {
+ v0 := *r0 // save r0
+ v3 := *r3 // save r3
+ t0 := *r2 | (v0 & *r1)
+ t1 := v3 & (v0 | *r1)
+ *r3 = t0 ^ t1
+ t2 := ^v3
+ t3 := *r1 ^ t1
+ t4 := t3 | (*r3 ^ t2)
+ *r1 = v0 ^ t4
+ *r0 = (*r2 ^ t3) ^ (v3 | *r1)
+ *r2 = (t0 ^ *r1) ^ (*r0 ^ (v0 & *r3))
+}
diff --git a/vendor/github.com/aead/serpent/serpent.go b/vendor/github.com/aead/serpent/serpent.go
new file mode 100644
index 00000000..b3fb811d
--- /dev/null
+++ b/vendor/github.com/aead/serpent/serpent.go
@@ -0,0 +1,119 @@
+// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
+// Use of this source code is governed by a license that can be
+// found in the LICENSE file.
+
+// Package serpent implements the Serpent block cipher
+// submitted to the AES challenge. Serpent was designed by
+// Ross Anderson, Eli Biham und Lars Knudsen.
+// The block cipher takes a 128, 192 or 256 bit key and
+// has a block size of 128 bit.
+package serpent // import "github.com/aead/serpent"
+
+import (
+ "crypto/cipher"
+ "errors"
+)
+
+// BlockSize is the serpent block size in bytes.
+const BlockSize = 16
+
+const phi = 0x9e3779b9 // The Serpent phi constant (sqrt(5) - 1) * 2**31
+
+var errKeySize = errors.New("invalid key size")
+
+// NewCipher returns a new cipher.Block implementing the serpent block cipher.
+// The key argument must be 128, 192 or 256 bit (16, 24, 32 byte).
+func NewCipher(key []byte) (cipher.Block, error) {
+ if k := len(key); k != 16 && k != 24 && k != 32 {
+ return nil, errKeySize
+ }
+ s := &subkeys{}
+ s.keySchedule(key)
+ return s, nil
+}
+
+// The 132 32 bit subkeys of serpent
+type subkeys [132]uint32
+
+func (s *subkeys) BlockSize() int { return BlockSize }
+
+func (s *subkeys) Encrypt(dst, src []byte) {
+ if len(src) < BlockSize {
+ panic("src buffer to small")
+ }
+ if len(dst) < BlockSize {
+ panic("dst buffer to small")
+ }
+ encryptBlock(dst, src, s)
+}
+
+func (s *subkeys) Decrypt(dst, src []byte) {
+ if len(src) < BlockSize {
+ panic("src buffer to small")
+ }
+ if len(dst) < BlockSize {
+ panic("dst buffer to small")
+ }
+ decryptBlock(dst, src, s)
+}
+
+// The key schedule of serpent.
+func (s *subkeys) keySchedule(key []byte) {
+ var k [16]uint32
+ j := 0
+ for i := 0; i+4 <= len(key); i += 4 {
+ k[j] = uint32(key[i]) | uint32(key[i+1])<<8 | uint32(key[i+2])<<16 | uint32(key[i+3])<<24
+ j++
+ }
+ if j < 8 {
+ k[j] = 1
+ }
+
+ for i := 8; i < 16; i++ {
+ x := k[i-8] ^ k[i-5] ^ k[i-3] ^ k[i-1] ^ phi ^ uint32(i-8)
+ k[i] = (x << 11) | (x >> 21)
+ s[i-8] = k[i]
+ }
+ for i := 8; i < 132; i++ {
+ x := s[i-8] ^ s[i-5] ^ s[i-3] ^ s[i-1] ^ phi ^ uint32(i)
+ s[i] = (x << 11) | (x >> 21)
+ }
+
+ sb3(&s[0], &s[1], &s[2], &s[3])
+ sb2(&s[4], &s[5], &s[6], &s[7])
+ sb1(&s[8], &s[9], &s[10], &s[11])
+ sb0(&s[12], &s[13], &s[14], &s[15])
+ sb7(&s[16], &s[17], &s[18], &s[19])
+ sb6(&s[20], &s[21], &s[22], &s[23])
+ sb5(&s[24], &s[25], &s[26], &s[27])
+ sb4(&s[28], &s[29], &s[30], &s[31])
+
+ sb3(&s[32], &s[33], &s[34], &s[35])
+ sb2(&s[36], &s[37], &s[38], &s[39])
+ sb1(&s[40], &s[41], &s[42], &s[43])
+ sb0(&s[44], &s[45], &s[46], &s[47])
+ sb7(&s[48], &s[49], &s[50], &s[51])
+ sb6(&s[52], &s[53], &s[54], &s[55])
+ sb5(&s[56], &s[57], &s[58], &s[59])
+ sb4(&s[60], &s[61], &s[62], &s[63])
+
+ sb3(&s[64], &s[65], &s[66], &s[67])
+ sb2(&s[68], &s[69], &s[70], &s[71])
+ sb1(&s[72], &s[73], &s[74], &s[75])
+ sb0(&s[76], &s[77], &s[78], &s[79])
+ sb7(&s[80], &s[81], &s[82], &s[83])
+ sb6(&s[84], &s[85], &s[86], &s[87])
+ sb5(&s[88], &s[89], &s[90], &s[91])
+ sb4(&s[92], &s[93], &s[94], &s[95])
+
+ sb3(&s[96], &s[97], &s[98], &s[99])
+ sb2(&s[100], &s[101], &s[102], &s[103])
+ sb1(&s[104], &s[105], &s[106], &s[107])
+ sb0(&s[108], &s[109], &s[110], &s[111])
+ sb7(&s[112], &s[113], &s[114], &s[115])
+ sb6(&s[116], &s[117], &s[118], &s[119])
+ sb5(&s[120], &s[121], &s[122], &s[123])
+ sb4(&s[124], &s[125], &s[126], &s[127])
+
+ sb3(&s[128], &s[129], &s[130], &s[131])
+}
diff --git a/vendor/github.com/aead/serpent/serpent_ref.go b/vendor/github.com/aead/serpent/serpent_ref.go
new file mode 100644
index 00000000..2d3ff02a
--- /dev/null
+++ b/vendor/github.com/aead/serpent/serpent_ref.go
@@ -0,0 +1,276 @@
+// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
+// Use of this source code is governed by a license that can be
+// found in the LICENSE file.
+
+package serpent
+
+// Encrypts one block with the given 132 sub-keys sk.
+func encryptBlock(dst, src []byte, sk *subkeys) {
+ // Transform the input block to 4 x 32 bit registers
+ r0 := uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24
+ r1 := uint32(src[4]) | uint32(src[5])<<8 | uint32(src[6])<<16 | uint32(src[7])<<24
+ r2 := uint32(src[8]) | uint32(src[9])<<8 | uint32(src[10])<<16 | uint32(src[11])<<24
+ r3 := uint32(src[12]) | uint32(src[13])<<8 | uint32(src[14])<<16 | uint32(src[15])<<24
+
+ // Encrypt the block with the 132 sub-keys and 8 S-Boxes
+ r0, r1, r2, r3 = r0^sk[0], r1^sk[1], r2^sk[2], r3^sk[3]
+ sb0(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[4], r1^sk[5], r2^sk[6], r3^sk[7]
+ sb1(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[8], r1^sk[9], r2^sk[10], r3^sk[11]
+ sb2(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[12], r1^sk[13], r2^sk[14], r3^sk[15]
+ sb3(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[16], r1^sk[17], r2^sk[18], r3^sk[19]
+ sb4(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[20], r1^sk[21], r2^sk[22], r3^sk[23]
+ sb5(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[24], r1^sk[25], r2^sk[26], r3^sk[27]
+ sb6(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[28], r1^sk[29], r2^sk[30], r3^sk[31]
+ sb7(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+
+ r0, r1, r2, r3 = r0^sk[32], r1^sk[33], r2^sk[34], r3^sk[35]
+ sb0(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[36], r1^sk[37], r2^sk[38], r3^sk[39]
+ sb1(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[40], r1^sk[41], r2^sk[42], r3^sk[43]
+ sb2(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[44], r1^sk[45], r2^sk[46], r3^sk[47]
+ sb3(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[48], r1^sk[49], r2^sk[50], r3^sk[51]
+ sb4(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[52], r1^sk[53], r2^sk[54], r3^sk[55]
+ sb5(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[56], r1^sk[57], r2^sk[58], r3^sk[59]
+ sb6(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[60], r1^sk[61], r2^sk[62], r3^sk[63]
+ sb7(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+
+ r0, r1, r2, r3 = r0^sk[64], r1^sk[65], r2^sk[66], r3^sk[67]
+ sb0(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[68], r1^sk[69], r2^sk[70], r3^sk[71]
+ sb1(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[72], r1^sk[73], r2^sk[74], r3^sk[75]
+ sb2(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[76], r1^sk[77], r2^sk[78], r3^sk[79]
+ sb3(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[80], r1^sk[81], r2^sk[82], r3^sk[83]
+ sb4(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[84], r1^sk[85], r2^sk[86], r3^sk[87]
+ sb5(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[88], r1^sk[89], r2^sk[90], r3^sk[91]
+ sb6(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[92], r1^sk[93], r2^sk[94], r3^sk[95]
+ sb7(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+
+ r0, r1, r2, r3 = r0^sk[96], r1^sk[97], r2^sk[98], r3^sk[99]
+ sb0(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[100], r1^sk[101], r2^sk[102], r3^sk[103]
+ sb1(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[104], r1^sk[105], r2^sk[106], r3^sk[107]
+ sb2(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[108], r1^sk[109], r2^sk[110], r3^sk[111]
+ sb3(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[112], r1^sk[113], r2^sk[114], r3^sk[115]
+ sb4(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[116], r1^sk[117], r2^sk[118], r3^sk[119]
+ sb5(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[120], r1^sk[121], r2^sk[122], r3^sk[123]
+ sb6(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[124], r1^sk[125], r2^sk[126], r3^sk[127]
+ sb7(&r0, &r1, &r2, &r3)
+
+ // whitening
+ r0 ^= sk[128]
+ r1 ^= sk[129]
+ r2 ^= sk[130]
+ r3 ^= sk[131]
+
+ // write the encrypted block to the output
+
+ dst[0] = byte(r0)
+ dst[1] = byte(r0 >> 8)
+ dst[2] = byte(r0 >> 16)
+ dst[3] = byte(r0 >> 24)
+ dst[4] = byte(r1)
+ dst[5] = byte(r1 >> 8)
+ dst[6] = byte(r1 >> 16)
+ dst[7] = byte(r1 >> 24)
+ dst[8] = byte(r2)
+ dst[9] = byte(r2 >> 8)
+ dst[10] = byte(r2 >> 16)
+ dst[11] = byte(r2 >> 24)
+ dst[12] = byte(r3)
+ dst[13] = byte(r3 >> 8)
+ dst[14] = byte(r3 >> 16)
+ dst[15] = byte(r3 >> 24)
+}
+
+// Decrypts one block with the given 132 sub-keys sk.
+func decryptBlock(dst, src []byte, sk *subkeys) {
+ // Transform the input block to 4 x 32 bit registers
+ r0 := uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24
+ r1 := uint32(src[4]) | uint32(src[5])<<8 | uint32(src[6])<<16 | uint32(src[7])<<24
+ r2 := uint32(src[8]) | uint32(src[9])<<8 | uint32(src[10])<<16 | uint32(src[11])<<24
+ r3 := uint32(src[12]) | uint32(src[13])<<8 | uint32(src[14])<<16 | uint32(src[15])<<24
+
+ // undo whitening
+ r0 ^= sk[128]
+ r1 ^= sk[129]
+ r2 ^= sk[130]
+ r3 ^= sk[131]
+
+ // Decrypt the block with the 132 sub-keys and 8 S-Boxes
+ sb7Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[124], r1^sk[125], r2^sk[126], r3^sk[127]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb6Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[120], r1^sk[121], r2^sk[122], r3^sk[123]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb5Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[116], r1^sk[117], r2^sk[118], r3^sk[119]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb4Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[112], r1^sk[113], r2^sk[114], r3^sk[115]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb3Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[108], r1^sk[109], r2^sk[110], r3^sk[111]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb2Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[104], r1^sk[105], r2^sk[106], r3^sk[107]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb1Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[100], r1^sk[101], r2^sk[102], r3^sk[103]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb0Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[96], r1^sk[97], r2^sk[98], r3^sk[99]
+ linearInv(&r0, &r1, &r2, &r3)
+
+ sb7Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[92], r1^sk[93], r2^sk[94], r3^sk[95]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb6Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[88], r1^sk[89], r2^sk[90], r3^sk[91]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb5Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[84], r1^sk[85], r2^sk[86], r3^sk[87]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb4Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[80], r1^sk[81], r2^sk[82], r3^sk[83]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb3Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[76], r1^sk[77], r2^sk[78], r3^sk[79]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb2Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[72], r1^sk[73], r2^sk[74], r3^sk[75]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb1Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[68], r1^sk[69], r2^sk[70], r3^sk[71]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb0Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[64], r1^sk[65], r2^sk[66], r3^sk[67]
+ linearInv(&r0, &r1, &r2, &r3)
+
+ sb7Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[60], r1^sk[61], r2^sk[62], r3^sk[63]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb6Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[56], r1^sk[57], r2^sk[58], r3^sk[59]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb5Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[52], r1^sk[53], r2^sk[54], r3^sk[55]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb4Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[48], r1^sk[49], r2^sk[50], r3^sk[51]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb3Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[44], r1^sk[45], r2^sk[46], r3^sk[47]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb2Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[40], r1^sk[41], r2^sk[42], r3^sk[43]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb1Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[36], r1^sk[37], r2^sk[38], r3^sk[39]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb0Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[32], r1^sk[33], r2^sk[34], r3^sk[35]
+ linearInv(&r0, &r1, &r2, &r3)
+
+ sb7Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[28], r1^sk[29], r2^sk[30], r3^sk[31]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb6Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[24], r1^sk[25], r2^sk[26], r3^sk[27]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb5Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[20], r1^sk[21], r2^sk[22], r3^sk[23]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb4Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[16], r1^sk[17], r2^sk[18], r3^sk[19]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb3Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[12], r1^sk[13], r2^sk[14], r3^sk[15]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb2Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[8], r1^sk[9], r2^sk[10], r3^sk[11]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb1Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[4], r1^sk[5], r2^sk[6], r3^sk[7]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb0Inv(&r0, &r1, &r2, &r3)
+
+ r0 ^= sk[0]
+ r1 ^= sk[1]
+ r2 ^= sk[2]
+ r3 ^= sk[3]
+
+ // write the decrypted block to the output
+ dst[0] = byte(r0)
+ dst[1] = byte(r0 >> 8)
+ dst[2] = byte(r0 >> 16)
+ dst[3] = byte(r0 >> 24)
+ dst[4] = byte(r1)
+ dst[5] = byte(r1 >> 8)
+ dst[6] = byte(r1 >> 16)
+ dst[7] = byte(r1 >> 24)
+ dst[8] = byte(r2)
+ dst[9] = byte(r2 >> 8)
+ dst[10] = byte(r2 >> 16)
+ dst[11] = byte(r2 >> 24)
+ dst[12] = byte(r3)
+ dst[13] = byte(r3 >> 8)
+ dst[14] = byte(r3 >> 16)
+ dst[15] = byte(r3 >> 24)
+}
diff --git a/vendor/github.com/blang/semver/v4/LICENSE b/vendor/github.com/blang/semver/v4/LICENSE
new file mode 100644
index 00000000..5ba5c86f
--- /dev/null
+++ b/vendor/github.com/blang/semver/v4/LICENSE
@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (c) 2014 Benedikt Lang
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/vendor/github.com/blang/semver/v4/json.go b/vendor/github.com/blang/semver/v4/json.go
new file mode 100644
index 00000000..a74bf7c4
--- /dev/null
+++ b/vendor/github.com/blang/semver/v4/json.go
@@ -0,0 +1,23 @@
+package semver
+
+import (
+ "encoding/json"
+)
+
+// MarshalJSON implements the encoding/json.Marshaler interface.
+func (v Version) MarshalJSON() ([]byte, error) {
+ return json.Marshal(v.String())
+}
+
+// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
+func (v *Version) UnmarshalJSON(data []byte) (err error) {
+ var versionString string
+
+ if err = json.Unmarshal(data, &versionString); err != nil {
+ return
+ }
+
+ *v, err = Parse(versionString)
+
+ return
+}
diff --git a/vendor/github.com/blang/semver/v4/range.go b/vendor/github.com/blang/semver/v4/range.go
new file mode 100644
index 00000000..95f7139b
--- /dev/null
+++ b/vendor/github.com/blang/semver/v4/range.go
@@ -0,0 +1,416 @@
+package semver
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "unicode"
+)
+
+type wildcardType int
+
+const (
+ noneWildcard wildcardType = iota
+ majorWildcard wildcardType = 1
+ minorWildcard wildcardType = 2
+ patchWildcard wildcardType = 3
+)
+
+func wildcardTypefromInt(i int) wildcardType {
+ switch i {
+ case 1:
+ return majorWildcard
+ case 2:
+ return minorWildcard
+ case 3:
+ return patchWildcard
+ default:
+ return noneWildcard
+ }
+}
+
+type comparator func(Version, Version) bool
+
+var (
+ compEQ comparator = func(v1 Version, v2 Version) bool {
+ return v1.Compare(v2) == 0
+ }
+ compNE = func(v1 Version, v2 Version) bool {
+ return v1.Compare(v2) != 0
+ }
+ compGT = func(v1 Version, v2 Version) bool {
+ return v1.Compare(v2) == 1
+ }
+ compGE = func(v1 Version, v2 Version) bool {
+ return v1.Compare(v2) >= 0
+ }
+ compLT = func(v1 Version, v2 Version) bool {
+ return v1.Compare(v2) == -1
+ }
+ compLE = func(v1 Version, v2 Version) bool {
+ return v1.Compare(v2) <= 0
+ }
+)
+
+type versionRange struct {
+ v Version
+ c comparator
+}
+
+// rangeFunc creates a Range from the given versionRange.
+func (vr *versionRange) rangeFunc() Range {
+ return Range(func(v Version) bool {
+ return vr.c(v, vr.v)
+ })
+}
+
+// Range represents a range of versions.
+// A Range can be used to check if a Version satisfies it:
+//
+// range, err := semver.ParseRange(">1.0.0 <2.0.0")
+// range(semver.MustParse("1.1.1") // returns true
+type Range func(Version) bool
+
+// OR combines the existing Range with another Range using logical OR.
+func (rf Range) OR(f Range) Range {
+ return Range(func(v Version) bool {
+ return rf(v) || f(v)
+ })
+}
+
+// AND combines the existing Range with another Range using logical AND.
+func (rf Range) AND(f Range) Range {
+ return Range(func(v Version) bool {
+ return rf(v) && f(v)
+ })
+}
+
+// ParseRange parses a range and returns a Range.
+// If the range could not be parsed an error is returned.
+//
+// Valid ranges are:
+// - "<1.0.0"
+// - "<=1.0.0"
+// - ">1.0.0"
+// - ">=1.0.0"
+// - "1.0.0", "=1.0.0", "==1.0.0"
+// - "!1.0.0", "!=1.0.0"
+//
+// A Range can consist of multiple ranges separated by space:
+// Ranges can be linked by logical AND:
+// - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0"
+// - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2
+//
+// Ranges can also be linked by logical OR:
+// - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"
+//
+// AND has a higher precedence than OR. It's not possible to use brackets.
+//
+// Ranges can be combined by both AND and OR
+//
+// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
+func ParseRange(s string) (Range, error) {
+ parts := splitAndTrim(s)
+ orParts, err := splitORParts(parts)
+ if err != nil {
+ return nil, err
+ }
+ expandedParts, err := expandWildcardVersion(orParts)
+ if err != nil {
+ return nil, err
+ }
+ var orFn Range
+ for _, p := range expandedParts {
+ var andFn Range
+ for _, ap := range p {
+ opStr, vStr, err := splitComparatorVersion(ap)
+ if err != nil {
+ return nil, err
+ }
+ vr, err := buildVersionRange(opStr, vStr)
+ if err != nil {
+ return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err)
+ }
+ rf := vr.rangeFunc()
+
+ // Set function
+ if andFn == nil {
+ andFn = rf
+ } else { // Combine with existing function
+ andFn = andFn.AND(rf)
+ }
+ }
+ if orFn == nil {
+ orFn = andFn
+ } else {
+ orFn = orFn.OR(andFn)
+ }
+
+ }
+ return orFn, nil
+}
+
+// splitORParts splits the already cleaned parts by '||'.
+// Checks for invalid positions of the operator and returns an
+// error if found.
+func splitORParts(parts []string) ([][]string, error) {
+ var ORparts [][]string
+ last := 0
+ for i, p := range parts {
+ if p == "||" {
+ if i == 0 {
+ return nil, fmt.Errorf("First element in range is '||'")
+ }
+ ORparts = append(ORparts, parts[last:i])
+ last = i + 1
+ }
+ }
+ if last == len(parts) {
+ return nil, fmt.Errorf("Last element in range is '||'")
+ }
+ ORparts = append(ORparts, parts[last:])
+ return ORparts, nil
+}
+
+// buildVersionRange takes a slice of 2: operator and version
+// and builds a versionRange, otherwise an error.
+func buildVersionRange(opStr, vStr string) (*versionRange, error) {
+ c := parseComparator(opStr)
+ if c == nil {
+ return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, ""))
+ }
+ v, err := Parse(vStr)
+ if err != nil {
+ return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err)
+ }
+
+ return &versionRange{
+ v: v,
+ c: c,
+ }, nil
+
+}
+
+// inArray checks if a byte is contained in an array of bytes
+func inArray(s byte, list []byte) bool {
+ for _, el := range list {
+ if el == s {
+ return true
+ }
+ }
+ return false
+}
+
+// splitAndTrim splits a range string by spaces and cleans whitespaces
+func splitAndTrim(s string) (result []string) {
+ last := 0
+ var lastChar byte
+ excludeFromSplit := []byte{'>', '<', '='}
+ for i := 0; i < len(s); i++ {
+ if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) {
+ if last < i-1 {
+ result = append(result, s[last:i])
+ }
+ last = i + 1
+ } else if s[i] != ' ' {
+ lastChar = s[i]
+ }
+ }
+ if last < len(s)-1 {
+ result = append(result, s[last:])
+ }
+
+ for i, v := range result {
+ result[i] = strings.Replace(v, " ", "", -1)
+ }
+
+ // parts := strings.Split(s, " ")
+ // for _, x := range parts {
+ // if s := strings.TrimSpace(x); len(s) != 0 {
+ // result = append(result, s)
+ // }
+ // }
+ return
+}
+
+// splitComparatorVersion splits the comparator from the version.
+// Input must be free of leading or trailing spaces.
+func splitComparatorVersion(s string) (string, string, error) {
+ i := strings.IndexFunc(s, unicode.IsDigit)
+ if i == -1 {
+ return "", "", fmt.Errorf("Could not get version from string: %q", s)
+ }
+ return strings.TrimSpace(s[0:i]), s[i:], nil
+}
+
+// getWildcardType will return the type of wildcard that the
+// passed version contains
+func getWildcardType(vStr string) wildcardType {
+ parts := strings.Split(vStr, ".")
+ nparts := len(parts)
+ wildcard := parts[nparts-1]
+
+ possibleWildcardType := wildcardTypefromInt(nparts)
+ if wildcard == "x" {
+ return possibleWildcardType
+ }
+
+ return noneWildcard
+}
+
+// createVersionFromWildcard will convert a wildcard version
+// into a regular version, replacing 'x's with '0's, handling
+// special cases like '1.x.x' and '1.x'
+func createVersionFromWildcard(vStr string) string {
+ // handle 1.x.x
+ vStr2 := strings.Replace(vStr, ".x.x", ".x", 1)
+ vStr2 = strings.Replace(vStr2, ".x", ".0", 1)
+ parts := strings.Split(vStr2, ".")
+
+ // handle 1.x
+ if len(parts) == 2 {
+ return vStr2 + ".0"
+ }
+
+ return vStr2
+}
+
+// incrementMajorVersion will increment the major version
+// of the passed version
+func incrementMajorVersion(vStr string) (string, error) {
+ parts := strings.Split(vStr, ".")
+ i, err := strconv.Atoi(parts[0])
+ if err != nil {
+ return "", err
+ }
+ parts[0] = strconv.Itoa(i + 1)
+
+ return strings.Join(parts, "."), nil
+}
+
+// incrementMajorVersion will increment the minor version
+// of the passed version
+func incrementMinorVersion(vStr string) (string, error) {
+ parts := strings.Split(vStr, ".")
+ i, err := strconv.Atoi(parts[1])
+ if err != nil {
+ return "", err
+ }
+ parts[1] = strconv.Itoa(i + 1)
+
+ return strings.Join(parts, "."), nil
+}
+
+// expandWildcardVersion will expand wildcards inside versions
+// following these rules:
+//
+// * when dealing with patch wildcards:
+// >= 1.2.x will become >= 1.2.0
+// <= 1.2.x will become < 1.3.0
+// > 1.2.x will become >= 1.3.0
+// < 1.2.x will become < 1.2.0
+// != 1.2.x will become < 1.2.0 >= 1.3.0
+//
+// * when dealing with minor wildcards:
+// >= 1.x will become >= 1.0.0
+// <= 1.x will become < 2.0.0
+// > 1.x will become >= 2.0.0
+// < 1.0 will become < 1.0.0
+// != 1.x will become < 1.0.0 >= 2.0.0
+//
+// * when dealing with wildcards without
+// version operator:
+// 1.2.x will become >= 1.2.0 < 1.3.0
+// 1.x will become >= 1.0.0 < 2.0.0
+func expandWildcardVersion(parts [][]string) ([][]string, error) {
+ var expandedParts [][]string
+ for _, p := range parts {
+ var newParts []string
+ for _, ap := range p {
+ if strings.Contains(ap, "x") {
+ opStr, vStr, err := splitComparatorVersion(ap)
+ if err != nil {
+ return nil, err
+ }
+
+ versionWildcardType := getWildcardType(vStr)
+ flatVersion := createVersionFromWildcard(vStr)
+
+ var resultOperator string
+ var shouldIncrementVersion bool
+ switch opStr {
+ case ">":
+ resultOperator = ">="
+ shouldIncrementVersion = true
+ case ">=":
+ resultOperator = ">="
+ case "<":
+ resultOperator = "<"
+ case "<=":
+ resultOperator = "<"
+ shouldIncrementVersion = true
+ case "", "=", "==":
+ newParts = append(newParts, ">="+flatVersion)
+ resultOperator = "<"
+ shouldIncrementVersion = true
+ case "!=", "!":
+ newParts = append(newParts, "<"+flatVersion)
+ resultOperator = ">="
+ shouldIncrementVersion = true
+ }
+
+ var resultVersion string
+ if shouldIncrementVersion {
+ switch versionWildcardType {
+ case patchWildcard:
+ resultVersion, _ = incrementMinorVersion(flatVersion)
+ case minorWildcard:
+ resultVersion, _ = incrementMajorVersion(flatVersion)
+ }
+ } else {
+ resultVersion = flatVersion
+ }
+
+ ap = resultOperator + resultVersion
+ }
+ newParts = append(newParts, ap)
+ }
+ expandedParts = append(expandedParts, newParts)
+ }
+
+ return expandedParts, nil
+}
+
+func parseComparator(s string) comparator {
+ switch s {
+ case "==":
+ fallthrough
+ case "":
+ fallthrough
+ case "=":
+ return compEQ
+ case ">":
+ return compGT
+ case ">=":
+ return compGE
+ case "<":
+ return compLT
+ case "<=":
+ return compLE
+ case "!":
+ fallthrough
+ case "!=":
+ return compNE
+ }
+
+ return nil
+}
+
+// MustParseRange is like ParseRange but panics if the range cannot be parsed.
+func MustParseRange(s string) Range {
+ r, err := ParseRange(s)
+ if err != nil {
+ panic(`semver: ParseRange(` + s + `): ` + err.Error())
+ }
+ return r
+}
diff --git a/vendor/github.com/blang/semver/v4/semver.go b/vendor/github.com/blang/semver/v4/semver.go
new file mode 100644
index 00000000..307de610
--- /dev/null
+++ b/vendor/github.com/blang/semver/v4/semver.go
@@ -0,0 +1,476 @@
+package semver
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+const (
+ numbers string = "0123456789"
+ alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
+ alphanum = alphas + numbers
+)
+
+// SpecVersion is the latest fully supported spec version of semver
+var SpecVersion = Version{
+ Major: 2,
+ Minor: 0,
+ Patch: 0,
+}
+
+// Version represents a semver compatible version
+type Version struct {
+ Major uint64
+ Minor uint64
+ Patch uint64
+ Pre []PRVersion
+ Build []string //No Precedence
+}
+
+// Version to string
+func (v Version) String() string {
+ b := make([]byte, 0, 5)
+ b = strconv.AppendUint(b, v.Major, 10)
+ b = append(b, '.')
+ b = strconv.AppendUint(b, v.Minor, 10)
+ b = append(b, '.')
+ b = strconv.AppendUint(b, v.Patch, 10)
+
+ if len(v.Pre) > 0 {
+ b = append(b, '-')
+ b = append(b, v.Pre[0].String()...)
+
+ for _, pre := range v.Pre[1:] {
+ b = append(b, '.')
+ b = append(b, pre.String()...)
+ }
+ }
+
+ if len(v.Build) > 0 {
+ b = append(b, '+')
+ b = append(b, v.Build[0]...)
+
+ for _, build := range v.Build[1:] {
+ b = append(b, '.')
+ b = append(b, build...)
+ }
+ }
+
+ return string(b)
+}
+
+// FinalizeVersion discards prerelease and build number and only returns
+// major, minor and patch number.
+func (v Version) FinalizeVersion() string {
+ b := make([]byte, 0, 5)
+ b = strconv.AppendUint(b, v.Major, 10)
+ b = append(b, '.')
+ b = strconv.AppendUint(b, v.Minor, 10)
+ b = append(b, '.')
+ b = strconv.AppendUint(b, v.Patch, 10)
+ return string(b)
+}
+
+// Equals checks if v is equal to o.
+func (v Version) Equals(o Version) bool {
+ return (v.Compare(o) == 0)
+}
+
+// EQ checks if v is equal to o.
+func (v Version) EQ(o Version) bool {
+ return (v.Compare(o) == 0)
+}
+
+// NE checks if v is not equal to o.
+func (v Version) NE(o Version) bool {
+ return (v.Compare(o) != 0)
+}
+
+// GT checks if v is greater than o.
+func (v Version) GT(o Version) bool {
+ return (v.Compare(o) == 1)
+}
+
+// GTE checks if v is greater than or equal to o.
+func (v Version) GTE(o Version) bool {
+ return (v.Compare(o) >= 0)
+}
+
+// GE checks if v is greater than or equal to o.
+func (v Version) GE(o Version) bool {
+ return (v.Compare(o) >= 0)
+}
+
+// LT checks if v is less than o.
+func (v Version) LT(o Version) bool {
+ return (v.Compare(o) == -1)
+}
+
+// LTE checks if v is less than or equal to o.
+func (v Version) LTE(o Version) bool {
+ return (v.Compare(o) <= 0)
+}
+
+// LE checks if v is less than or equal to o.
+func (v Version) LE(o Version) bool {
+ return (v.Compare(o) <= 0)
+}
+
+// Compare compares Versions v to o:
+// -1 == v is less than o
+// 0 == v is equal to o
+// 1 == v is greater than o
+func (v Version) Compare(o Version) int {
+ if v.Major != o.Major {
+ if v.Major > o.Major {
+ return 1
+ }
+ return -1
+ }
+ if v.Minor != o.Minor {
+ if v.Minor > o.Minor {
+ return 1
+ }
+ return -1
+ }
+ if v.Patch != o.Patch {
+ if v.Patch > o.Patch {
+ return 1
+ }
+ return -1
+ }
+
+ // Quick comparison if a version has no prerelease versions
+ if len(v.Pre) == 0 && len(o.Pre) == 0 {
+ return 0
+ } else if len(v.Pre) == 0 && len(o.Pre) > 0 {
+ return 1
+ } else if len(v.Pre) > 0 && len(o.Pre) == 0 {
+ return -1
+ }
+
+ i := 0
+ for ; i < len(v.Pre) && i < len(o.Pre); i++ {
+ if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 {
+ continue
+ } else if comp == 1 {
+ return 1
+ } else {
+ return -1
+ }
+ }
+
+ // If all pr versions are the equal but one has further prversion, this one greater
+ if i == len(v.Pre) && i == len(o.Pre) {
+ return 0
+ } else if i == len(v.Pre) && i < len(o.Pre) {
+ return -1
+ } else {
+ return 1
+ }
+
+}
+
+// IncrementPatch increments the patch version
+func (v *Version) IncrementPatch() error {
+ v.Patch++
+ return nil
+}
+
+// IncrementMinor increments the minor version
+func (v *Version) IncrementMinor() error {
+ v.Minor++
+ v.Patch = 0
+ return nil
+}
+
+// IncrementMajor increments the major version
+func (v *Version) IncrementMajor() error {
+ v.Major++
+ v.Minor = 0
+ v.Patch = 0
+ return nil
+}
+
+// Validate validates v and returns error in case
+func (v Version) Validate() error {
+ // Major, Minor, Patch already validated using uint64
+
+ for _, pre := range v.Pre {
+ if !pre.IsNum { //Numeric prerelease versions already uint64
+ if len(pre.VersionStr) == 0 {
+ return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr)
+ }
+ if !containsOnly(pre.VersionStr, alphanum) {
+ return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr)
+ }
+ }
+ }
+
+ for _, build := range v.Build {
+ if len(build) == 0 {
+ return fmt.Errorf("Build meta data can not be empty %q", build)
+ }
+ if !containsOnly(build, alphanum) {
+ return fmt.Errorf("Invalid character(s) found in build meta data %q", build)
+ }
+ }
+
+ return nil
+}
+
+// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
+func New(s string) (*Version, error) {
+ v, err := Parse(s)
+ vp := &v
+ return vp, err
+}
+
+// Make is an alias for Parse, parses version string and returns a validated Version or error
+func Make(s string) (Version, error) {
+ return Parse(s)
+}
+
+// ParseTolerant allows for certain version specifications that do not strictly adhere to semver
+// specs to be parsed by this library. It does so by normalizing versions before passing them to
+// Parse(). It currently trims spaces, removes a "v" prefix, adds a 0 patch number to versions
+// with only major and minor components specified, and removes leading 0s.
+func ParseTolerant(s string) (Version, error) {
+ s = strings.TrimSpace(s)
+ s = strings.TrimPrefix(s, "v")
+
+ // Split into major.minor.(patch+pr+meta)
+ parts := strings.SplitN(s, ".", 3)
+ // Remove leading zeros.
+ for i, p := range parts {
+ if len(p) > 1 {
+ p = strings.TrimLeft(p, "0")
+ if len(p) == 0 || !strings.ContainsAny(p[0:1], "0123456789") {
+ p = "0" + p
+ }
+ parts[i] = p
+ }
+ }
+ // Fill up shortened versions.
+ if len(parts) < 3 {
+ if strings.ContainsAny(parts[len(parts)-1], "+-") {
+ return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
+ }
+ for len(parts) < 3 {
+ parts = append(parts, "0")
+ }
+ }
+ s = strings.Join(parts, ".")
+
+ return Parse(s)
+}
+
+// Parse parses version string and returns a validated Version or error
+func Parse(s string) (Version, error) {
+ if len(s) == 0 {
+ return Version{}, errors.New("Version string empty")
+ }
+
+ // Split into major.minor.(patch+pr+meta)
+ parts := strings.SplitN(s, ".", 3)
+ if len(parts) != 3 {
+ return Version{}, errors.New("No Major.Minor.Patch elements found")
+ }
+
+ // Major
+ if !containsOnly(parts[0], numbers) {
+ return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0])
+ }
+ if hasLeadingZeroes(parts[0]) {
+ return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0])
+ }
+ major, err := strconv.ParseUint(parts[0], 10, 64)
+ if err != nil {
+ return Version{}, err
+ }
+
+ // Minor
+ if !containsOnly(parts[1], numbers) {
+ return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1])
+ }
+ if hasLeadingZeroes(parts[1]) {
+ return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1])
+ }
+ minor, err := strconv.ParseUint(parts[1], 10, 64)
+ if err != nil {
+ return Version{}, err
+ }
+
+ v := Version{}
+ v.Major = major
+ v.Minor = minor
+
+ var build, prerelease []string
+ patchStr := parts[2]
+
+ if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 {
+ build = strings.Split(patchStr[buildIndex+1:], ".")
+ patchStr = patchStr[:buildIndex]
+ }
+
+ if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 {
+ prerelease = strings.Split(patchStr[preIndex+1:], ".")
+ patchStr = patchStr[:preIndex]
+ }
+
+ if !containsOnly(patchStr, numbers) {
+ return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr)
+ }
+ if hasLeadingZeroes(patchStr) {
+ return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr)
+ }
+ patch, err := strconv.ParseUint(patchStr, 10, 64)
+ if err != nil {
+ return Version{}, err
+ }
+
+ v.Patch = patch
+
+ // Prerelease
+ for _, prstr := range prerelease {
+ parsedPR, err := NewPRVersion(prstr)
+ if err != nil {
+ return Version{}, err
+ }
+ v.Pre = append(v.Pre, parsedPR)
+ }
+
+ // Build meta data
+ for _, str := range build {
+ if len(str) == 0 {
+ return Version{}, errors.New("Build meta data is empty")
+ }
+ if !containsOnly(str, alphanum) {
+ return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str)
+ }
+ v.Build = append(v.Build, str)
+ }
+
+ return v, nil
+}
+
+// MustParse is like Parse but panics if the version cannot be parsed.
+func MustParse(s string) Version {
+ v, err := Parse(s)
+ if err != nil {
+ panic(`semver: Parse(` + s + `): ` + err.Error())
+ }
+ return v
+}
+
+// PRVersion represents a PreRelease Version
+type PRVersion struct {
+ VersionStr string
+ VersionNum uint64
+ IsNum bool
+}
+
+// NewPRVersion creates a new valid prerelease version
+func NewPRVersion(s string) (PRVersion, error) {
+ if len(s) == 0 {
+ return PRVersion{}, errors.New("Prerelease is empty")
+ }
+ v := PRVersion{}
+ if containsOnly(s, numbers) {
+ if hasLeadingZeroes(s) {
+ return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s)
+ }
+ num, err := strconv.ParseUint(s, 10, 64)
+
+ // Might never be hit, but just in case
+ if err != nil {
+ return PRVersion{}, err
+ }
+ v.VersionNum = num
+ v.IsNum = true
+ } else if containsOnly(s, alphanum) {
+ v.VersionStr = s
+ v.IsNum = false
+ } else {
+ return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s)
+ }
+ return v, nil
+}
+
+// IsNumeric checks if prerelease-version is numeric
+func (v PRVersion) IsNumeric() bool {
+ return v.IsNum
+}
+
+// Compare compares two PreRelease Versions v and o:
+// -1 == v is less than o
+// 0 == v is equal to o
+// 1 == v is greater than o
+func (v PRVersion) Compare(o PRVersion) int {
+ if v.IsNum && !o.IsNum {
+ return -1
+ } else if !v.IsNum && o.IsNum {
+ return 1
+ } else if v.IsNum && o.IsNum {
+ if v.VersionNum == o.VersionNum {
+ return 0
+ } else if v.VersionNum > o.VersionNum {
+ return 1
+ } else {
+ return -1
+ }
+ } else { // both are Alphas
+ if v.VersionStr == o.VersionStr {
+ return 0
+ } else if v.VersionStr > o.VersionStr {
+ return 1
+ } else {
+ return -1
+ }
+ }
+}
+
+// PreRelease version to string
+func (v PRVersion) String() string {
+ if v.IsNum {
+ return strconv.FormatUint(v.VersionNum, 10)
+ }
+ return v.VersionStr
+}
+
+func containsOnly(s string, set string) bool {
+ return strings.IndexFunc(s, func(r rune) bool {
+ return !strings.ContainsRune(set, r)
+ }) == -1
+}
+
+func hasLeadingZeroes(s string) bool {
+ return len(s) > 1 && s[0] == '0'
+}
+
+// NewBuildVersion creates a new valid build version
+func NewBuildVersion(s string) (string, error) {
+ if len(s) == 0 {
+ return "", errors.New("Buildversion is empty")
+ }
+ if !containsOnly(s, alphanum) {
+ return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s)
+ }
+ return s, nil
+}
+
+// FinalizeVersion returns the major, minor and patch number only and discards
+// prerelease and build number.
+func FinalizeVersion(s string) (string, error) {
+ v, err := Parse(s)
+ if err != nil {
+ return "", err
+ }
+ v.Pre = nil
+ v.Build = nil
+
+ finalVer := v.String()
+ return finalVer, nil
+}
diff --git a/vendor/github.com/blang/semver/v4/sort.go b/vendor/github.com/blang/semver/v4/sort.go
new file mode 100644
index 00000000..e18f8808
--- /dev/null
+++ b/vendor/github.com/blang/semver/v4/sort.go
@@ -0,0 +1,28 @@
+package semver
+
+import (
+ "sort"
+)
+
+// Versions represents multiple versions.
+type Versions []Version
+
+// Len returns length of version collection
+func (s Versions) Len() int {
+ return len(s)
+}
+
+// Swap swaps two versions inside the collection by its indices
+func (s Versions) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+// Less checks if version at index i is less than version at index j
+func (s Versions) Less(i, j int) bool {
+ return s[i].LT(s[j])
+}
+
+// Sort sorts a slice of versions
+func Sort(versions []Version) {
+ sort.Sort(Versions(versions))
+}
diff --git a/vendor/github.com/blang/semver/v4/sql.go b/vendor/github.com/blang/semver/v4/sql.go
new file mode 100644
index 00000000..db958134
--- /dev/null
+++ b/vendor/github.com/blang/semver/v4/sql.go
@@ -0,0 +1,30 @@
+package semver
+
+import (
+ "database/sql/driver"
+ "fmt"
+)
+
+// Scan implements the database/sql.Scanner interface.
+func (v *Version) Scan(src interface{}) (err error) {
+ var str string
+ switch src := src.(type) {
+ case string:
+ str = src
+ case []byte:
+ str = string(src)
+ default:
+ return fmt.Errorf("version.Scan: cannot convert %T to string", src)
+ }
+
+ if t, err := Parse(str); err == nil {
+ *v = t
+ }
+
+ return
+}
+
+// Value implements the database/sql/driver.Valuer interface.
+func (v Version) Value() (driver.Value, error) {
+ return v.String(), nil
+}
diff --git a/vendor/github.com/checkpoint-restore/checkpointctl/LICENSE b/vendor/github.com/checkpoint-restore/checkpointctl/LICENSE
new file mode 100644
index 00000000..8dada3ed
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/checkpointctl/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ 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.
diff --git a/vendor/github.com/checkpoint-restore/checkpointctl/lib/annotations.go b/vendor/github.com/checkpoint-restore/checkpointctl/lib/annotations.go
new file mode 100644
index 00000000..8d0ad980
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/checkpointctl/lib/annotations.go
@@ -0,0 +1,65 @@
+package metadata
+
+const (
+ // CheckpointAnnotationEngine specifies the name of the container engine (e.g., podman, cri-o).
+ CheckpointAnnotationEngine = "org.criu.checkpoint.engine.name"
+
+ // CheckpointAnnotationEngineVersion specifies the version of the container engine.
+ CheckpointAnnotationEngineVersion = "org.criu.checkpoint.engine.version"
+
+ // CheckpointAnnotationName specifies the name of the container associated with the checkpoint.
+ CheckpointAnnotationName = "org.criu.checkpoint.container.name"
+
+ // CheckpointAnnotationPod specifies the name of the pod associated with the checkpoint.
+ CheckpointAnnotationPod = "org.criu.checkpoint.pod.name"
+
+ // CheckpointAnnotationPodID specifies the ID of the pod associated with the checkpoint.
+ CheckpointAnnotationPodID = "org.criu.checkpoint.pod.id"
+
+ // CheckpointAnnotationNamespace specifies the namespace of the pod associated with the checkpoint.
+ CheckpointAnnotationNamespace = "org.criu.checkpoint.pod.namespace"
+
+ // CheckpointAnnotationRootfsImageName specifies the name of the root filesystem image associated with the checkpoint.
+ CheckpointAnnotationRootfsImageName = "org.criu.checkpoint.rootfsImageName"
+
+ // CheckpointAnnotationRootfsImageUserRequested specifies the name of the root filesystem image requested by the user.
+ CheckpointAnnotationRootfsImageUserRequested = "org.criu.checkpoint.rootfsImageUserRequested"
+
+ // CheckpointAnnotationRootfsImageSha specifies the SHA hash of the root filesystem image associated with the checkpoint.
+ CheckpointAnnotationRootfsImageSha = "org.criu.checkpoint.rootfsImageSha"
+
+ // CheckpointAnnotationRootfsImageID specifies the ID of the root filesystem image associated with the checkpoint.
+ CheckpointAnnotationRootfsImageID = "org.criu.checkpoint.rootfsImageID"
+
+ // CheckpointAnnotationRawImageName specifies the original unprocessed name of the image used to create the container.
+ CheckpointAnnotationRawImageName = "org.criu.checkpoint.rawImageName"
+
+ // CheckpointAnnotationRuntimeName specifies the runtime used on the host where the checkpoint was created.
+ CheckpointAnnotationRuntimeName = "org.criu.checkpoint.runtime.name"
+
+ // CheckpointAnnotationRuntimeVersion specifies the version of the runtime used on the host where the checkpoint was created.
+ CheckpointAnnotationRuntimeVersion = "org.criu.checkpoint.runtime.version"
+
+ // CheckpointAnnotationCriuVersion specifies the version of CRIU used on the host where the checkpoint was created.
+ CheckpointAnnotationCriuVersion = "org.criu.checkpoint.criu.version"
+
+ // CheckpointAnnotationConmonVersion specifies the version of conmon used on the host where the checkpoint was created.
+ CheckpointAnnotationConmonVersion = "org.criu.checkpoint.conmon.version"
+
+ // CheckpointAnnotationHostArch specifies the CPU architecture of the host where the checkpoint was created.
+ CheckpointAnnotationHostArch = "org.criu.checkpoint.host.arch"
+
+ // CheckpointAnnotationHostKernel specifies the kernel version used by the host where the checkpoint was created.
+ CheckpointAnnotationHostKernel = "org.criu.checkpoint.host.kernel"
+
+ // CheckpointAnnotationCgroupVersion specifies the cgroup version used by the host where the checkpoint was created.
+ CheckpointAnnotationCgroupVersion = "org.criu.checkpoint.cgroup.version"
+
+ // CheckpointAnnotationDistributionVersion specifies the name of the host distribution on which the checkpoint was created.
+ // This annotation is particularly useful because some distributions may include non-upstream patches
+ // that can cause CRIU (Checkpoint/Restore in Userspace) to fail.
+ CheckpointAnnotationDistributionName = "org.criu.checkpoint.distribution.name"
+
+ // CheckpointAnnotationDistributionVersion specifies the version of the host distribution on which the checkpoint was created.
+ CheckpointAnnotationDistributionVersion = "org.criu.checkpoint.distribution.version"
+)
diff --git a/vendor/github.com/checkpoint-restore/checkpointctl/lib/metadata.go b/vendor/github.com/checkpoint-restore/checkpointctl/lib/metadata.go
new file mode 100644
index 00000000..1d68f887
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/checkpointctl/lib/metadata.go
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package metadata
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "time"
+
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+const (
+ // container archive
+ ConfigDumpFile = "config.dump"
+ SpecDumpFile = "spec.dump"
+ StatusDumpFile = "status.dump"
+ NetworkStatusFile = "network.status"
+ CheckpointDirectory = "checkpoint"
+ CheckpointVolumesDirectory = "volumes"
+ DevShmCheckpointTar = "devshm-checkpoint.tar"
+ RootFsDiffTar = "rootfs-diff.tar"
+ DeletedFilesFile = "deleted.files"
+ DumpLogFile = "dump.log"
+ RestoreLogFile = "restore.log"
+ // pod archive
+ PodOptionsFile = "pod.options"
+ PodDumpFile = "pod.dump"
+ // containerd only
+ StatusFile = "status"
+ // CRIU Images
+ PagesPrefix = "pages-"
+ AmdgpuPagesPrefix = "amdgpu-pages-"
+)
+
+// This is a reduced copy of what Podman uses to store checkpoint metadata
+type ContainerConfig struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ RootfsImage string `json:"rootfsImage,omitempty"`
+ RootfsImageRef string `json:"rootfsImageRef,omitempty"`
+ RootfsImageName string `json:"rootfsImageName,omitempty"`
+ OCIRuntime string `json:"runtime,omitempty"`
+ CreatedTime time.Time `json:"createdTime"`
+ CheckpointedAt time.Time `json:"checkpointedTime"`
+ RestoredAt time.Time `json:"restoredTime"`
+ Restored bool `json:"restored"`
+}
+
+type Spec struct {
+ Annotations map[string]string `json:"annotations,omitempty"`
+}
+
+type ContainerdStatus struct {
+ CreatedAt int64
+ StartedAt int64
+ FinishedAt int64
+ ExitCode int32
+ Pid uint32
+ Reason string
+ Message string
+}
+
+// This structure is used by the KubernetesContainerCheckpointMetadata structure
+type KubernetesCheckpoint struct {
+ Archive string `json:"archive,omitempty"`
+ Size int64 `json:"size,omitempty"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+}
+
+// This structure is the basis for Kubernetes to track how many checkpoints
+// for a certain container have been created.
+type KubernetesContainerCheckpointMetadata struct {
+ PodFullName string `json:"podFullName,omitempty"`
+ ContainerName string `json:"containerName,omitempty"`
+ TotalSize int64 `json:"totalSize,omitempty"`
+ Checkpoints []KubernetesCheckpoint `json:"checkpoints"`
+}
+
+func ReadContainerCheckpointSpecDump(checkpointDirectory string) (*spec.Spec, string, error) {
+ var specDump spec.Spec
+ specDumpFile, err := ReadJSONFile(&specDump, checkpointDirectory, SpecDumpFile)
+
+ return &specDump, specDumpFile, err
+}
+
+func ReadContainerCheckpointConfigDump(checkpointDirectory string) (*ContainerConfig, string, error) {
+ var containerConfig ContainerConfig
+ configDumpFile, err := ReadJSONFile(&containerConfig, checkpointDirectory, ConfigDumpFile)
+
+ return &containerConfig, configDumpFile, err
+}
+
+func ReadContainerCheckpointDeletedFiles(checkpointDirectory string) ([]string, string, error) {
+ var deletedFiles []string
+ deletedFilesFile, err := ReadJSONFile(&deletedFiles, checkpointDirectory, DeletedFilesFile)
+
+ return deletedFiles, deletedFilesFile, err
+}
+
+func ReadContainerCheckpointStatusFile(checkpointDirectory string) (*ContainerdStatus, string, error) {
+ var containerdStatus ContainerdStatus
+ statusFile, err := ReadJSONFile(&containerdStatus, checkpointDirectory, StatusFile)
+
+ return &containerdStatus, statusFile, err
+}
+
+// WriteJSONFile marshalls and writes the given data to a JSON file
+func WriteJSONFile(v interface{}, dir, file string) (string, error) {
+ fileJSON, err := json.MarshalIndent(v, "", " ")
+ if err != nil {
+ return "", fmt.Errorf("error marshalling JSON: %w", err)
+ }
+ file = filepath.Join(dir, file)
+ if err := os.WriteFile(file, fileJSON, 0o600); err != nil {
+ return "", err
+ }
+
+ return file, nil
+}
+
+func ReadJSONFile(v interface{}, dir, file string) (string, error) {
+ file = filepath.Join(dir, file)
+ content, err := os.ReadFile(file)
+ if err != nil {
+ return "", err
+ }
+ if err = json.Unmarshal(content, v); err != nil {
+ return "", fmt.Errorf("failed to unmarshal %s: %w", file, err)
+ }
+
+ return file, nil
+}
+
+func ByteToString(b int64) string {
+ const unit = 1024
+ if b < unit {
+ return fmt.Sprintf("%d B", b)
+ }
+ div, exp := int64(unit), 0
+ for n := b / unit; n >= unit; n /= unit {
+ div *= unit
+ exp++
+ }
+
+ return fmt.Sprintf("%.1f %ciB",
+ float64(b)/float64(div), "KMGTPE"[exp])
+}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/.gitignore b/vendor/github.com/checkpoint-restore/go-criu/v7/.gitignore
new file mode 100644
index 00000000..eb1b08bc
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/.gitignore
@@ -0,0 +1,18 @@
+test/test
+test/test.coverage
+test/piggie/piggie
+test/phaul/phaul
+test/phaul/phaul.coverage
+test/loop/loop
+test/mmapper/mmapper
+test/crit/crit-test
+test/crit/test-imgs
+test/crit/crit-test.coverage
+test/.coverage/
+image
+scripts/magic-gen/*.h
+scripts/magic-gen/expected.go
+scripts/magic-gen/output.go
+crit/bin
+crit/test-imgs/
+__pycache__
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/.golangci.yml b/vendor/github.com/checkpoint-restore/go-criu/v7/.golangci.yml
new file mode 100644
index 00000000..a0d20be2
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/.golangci.yml
@@ -0,0 +1,22 @@
+linters:
+ presets:
+ - bugs
+ - performance
+ - unused
+ - format
+ disable:
+ - musttag
+ enable:
+ - whitespace
+ - misspell
+ - dupl
+ - gosimple
+ - stylecheck
+
+linters-settings:
+ exhaustive:
+ default-signifies-exhaustive: true
+ gosec:
+ excludes:
+ # https://github.com/securego/gosec/issues/1185
+ - G115
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/LICENSE b/vendor/github.com/checkpoint-restore/go-criu/v7/LICENSE
new file mode 100644
index 00000000..8dada3ed
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ 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.
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/MAINTAINERS b/vendor/github.com/checkpoint-restore/go-criu/v7/MAINTAINERS
new file mode 100644
index 00000000..4611c33b
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/MAINTAINERS
@@ -0,0 +1,4 @@
+Adrian Reber
+Kir Kolyshkin
+Prajwal S N
+Radostin Stoyanov
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/Makefile b/vendor/github.com/checkpoint-restore/go-criu/v7/Makefile
new file mode 100644
index 00000000..12e06423
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/Makefile
@@ -0,0 +1,42 @@
+SHELL = /bin/bash
+GO ?= go
+CC ?= gcc
+
+all: build
+
+lint:
+ golangci-lint run ./...
+
+build: rpc/rpc.pb.go stats/stats.pb.go
+ $(GO) build -v ./...
+ # Build crit binary
+ $(MAKE) -C crit bin/crit
+
+test: build
+ $(MAKE) -C test
+
+coverage:
+ $(MAKE) -C test coverage
+
+rpc/rpc.proto:
+ curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/rpc.proto -o $@
+
+rpc/rpc.pb.go: rpc/rpc.proto
+ protoc --go_out=. --go_opt=M$^=rpc/ $^
+
+stats/stats.proto:
+ curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/stats.proto -o $@
+
+stats/stats.pb.go: stats/stats.proto
+ protoc --go_out=. --go_opt=M$^=stats/ $^
+
+vendor:
+ $(GO) mod tidy
+ $(GO) mod vendor
+ $(GO) mod verify
+
+clean:
+ $(MAKE) -C crit/ clean
+ $(MAKE) -C test/ clean
+
+.PHONY: build test lint vendor coverage clean
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/README.md b/vendor/github.com/checkpoint-restore/go-criu/v7/README.md
new file mode 100644
index 00000000..14a08eb7
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/README.md
@@ -0,0 +1,112 @@
+
+# go-criu -- Go bindings for CRIU
+
+[](https://github.com/checkpoint-restore/go-criu/actions?query=workflow%3Aci)
+[](https://github.com/checkpoint-restore/go-criu/actions?query=workflow%3Averify)
+[](https://pkg.go.dev/github.com/checkpoint-restore/go-criu)
+
+This repository provides Go bindings for [CRIU](https://criu.org/).
+The code is based on the Go-based PHaul implementation from the CRIU repository.
+For easier inclusion into other Go projects, the CRIU Go bindings have been
+moved to this repository.
+
+## CRIU
+
+The Go bindings provide an easy way to use the CRIU RPC calls from Go without
+the need to set up all the infrastructure to make the actual RPC connection to CRIU.
+
+The following example would print the version of CRIU:
+
+```go
+import (
+ "log"
+
+ "github.com/checkpoint-restore/go-criu/v7"
+)
+
+func main() {
+ c := criu.MakeCriu()
+ version, err := c.GetCriuVersion()
+ if err != nil {
+ log.Fatalln(err)
+ }
+ log.Println(version)
+}
+```
+
+or to just check if at least a certain CRIU version is installed:
+
+```go
+ c := criu.MakeCriu()
+ result, err := c.IsCriuAtLeast(31100)
+```
+
+## CRIT
+
+The `crit` package provides bindings to decode, encode, and manipulate
+CRIU image files natively within Go. It also provides a CLI tool similar
+to the original CRIT Python tool. To get started with this, see the docs
+at [CRIT (Go library)](https://criu.org/CRIT_%28Go_library%29).
+
+## Releases
+
+The first go-criu release was 3.11 based on CRIU 3.11. The initial plan
+was to follow CRIU so that go-criu would carry the same version number as
+CRIU.
+
+As go-criu is imported in other projects and as Go modules are expected
+to follow Semantic Versioning go-criu will also follow Semantic Versioning
+starting with the 4.0.0 release.
+
+The following table shows the relation between go-criu and criu versions:
+
+| Major version | Latest release | CRIU version |
+| -------------- | -------------- | ------------ |
+| v7 | 7.2.0 | 3.19 |
+| v7 | 7.0.0 | 3.18 |
+| v6 | 6.3.0 | 3.17 |
+| v5 | 5.3.0 | 3.16 |
+| v5 | 5.0.0 | 3.15 |
+| v4 | 4.1.0 | 3.14 |
+
+## How to contribute
+
+While bug fixes can first be identified via an "issue", that is not required.
+It's ok to just open up a PR with the fix, but make sure you include the same
+information you would have included in an issue - like how to reproduce it.
+
+PRs for new features should include some background on what use cases the
+new code is trying to address. When possible and when it makes sense, try to
+break-up larger PRs into smaller ones - it's easier to review smaller
+code changes. But only if those smaller ones make sense as stand-alone PRs.
+
+Regardless of the type of PR, all PRs should include:
+
+* well documented code changes
+* additional testcases. Ideally, they should fail w/o your code change applied
+* documentation changes
+
+Squash your commits into logical pieces of work that might want to be reviewed
+separate from the rest of the PRs. Ideally, each commit should implement a
+single idea, and the PR branch should pass the tests at every commit. GitHub
+makes it easy to review the cumulative effect of many commits; so, when in
+doubt, use smaller commits.
+
+PRs that fix issues should include a reference like `Closes #XXXX` in the
+commit message so that github will automatically close the referenced issue
+when the PR is merged.
+
+Contributors must assert that they are in compliance with the [Developer
+Certificate of Origin 1.1](http://developercertificate.org/). This is achieved
+by adding a "Signed-off-by" line containing the contributor's name and e-mail
+to every commit message. Your signature certifies that you wrote the patch or
+otherwise have the right to pass it on as an open-source patch.
+
+## License and copyright
+
+Unless mentioned otherwise in a specific file's header, all code in
+this project is released under the Apache 2.0 license.
+
+The author of a change remains the copyright holder of their code
+(no copyright assignment). The list of authors and contributors can be
+retrieved from the git commit history and in some cases, the file headers.
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/codecov.yml b/vendor/github.com/checkpoint-restore/go-criu/v7/codecov.yml
new file mode 100644
index 00000000..b52d0c49
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/codecov.yml
@@ -0,0 +1,2 @@
+ignore:
+ - "test"
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/features.go b/vendor/github.com/checkpoint-restore/go-criu/v7/features.go
new file mode 100644
index 00000000..c62e69e0
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/features.go
@@ -0,0 +1,45 @@
+package criu
+
+import (
+ "errors"
+
+ "github.com/checkpoint-restore/go-criu/v7/rpc"
+)
+
+// Feature checking in go-criu is based on the libcriu feature checking function.
+
+// Feature checking allows the user to check if CRIU supports
+// certain features. There are CRIU features which do not depend
+// on the version of CRIU but on kernel features or architecture.
+//
+// One example is memory tracking. Memory tracking can be disabled
+// in the kernel or there are architectures which do not support
+// it (aarch64 for example). By using the feature check a libcriu
+// user can easily query CRIU if a certain feature is available.
+//
+// The features which should be checked can be marked in the
+// structure 'struct criu_feature_check'. Each structure member
+// that is set to true will result in CRIU checking for the
+// availability of that feature in the current combination of
+// CRIU/kernel/architecture.
+//
+// Available features will be set to true when the function
+// returns successfully. Missing features will be set to false.
+
+func (c *Criu) FeatureCheck(features *rpc.CriuFeatures) (*rpc.CriuFeatures, error) {
+ resp, err := c.doSwrkWithResp(
+ rpc.CriuReqType_FEATURE_CHECK,
+ nil,
+ nil,
+ features,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ if resp.GetType() != rpc.CriuReqType_FEATURE_CHECK {
+ return nil, errors.New("unexpected CRIU RPC response")
+ }
+
+ return features, nil
+}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/main.go b/vendor/github.com/checkpoint-restore/go-criu/v7/main.go
new file mode 100644
index 00000000..8f29d2ee
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/main.go
@@ -0,0 +1,274 @@
+package criu
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "os/exec"
+ "strconv"
+ "syscall"
+
+ "github.com/checkpoint-restore/go-criu/v7/rpc"
+ "google.golang.org/protobuf/proto"
+)
+
+// Criu struct
+type Criu struct {
+ swrkCmd *exec.Cmd
+ swrkSk *os.File
+ swrkPath string
+}
+
+// MakeCriu returns the Criu object required for most operations
+func MakeCriu() *Criu {
+ return &Criu{
+ swrkPath: "criu",
+ }
+}
+
+// SetCriuPath allows setting the path to the CRIU binary
+// if it is in a non standard location
+func (c *Criu) SetCriuPath(path string) {
+ c.swrkPath = path
+}
+
+// Prepare sets up everything for the RPC communication to CRIU
+func (c *Criu) Prepare() error {
+ fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_SEQPACKET, 0)
+ if err != nil {
+ return err
+ }
+
+ cln := os.NewFile(uintptr(fds[0]), "criu-xprt-cln")
+ syscall.CloseOnExec(fds[0])
+ srv := os.NewFile(uintptr(fds[1]), "criu-xprt-srv")
+ defer srv.Close()
+
+ args := []string{"swrk", strconv.Itoa(fds[1])}
+ // #nosec G204
+ cmd := exec.Command(c.swrkPath, args...)
+
+ err = cmd.Start()
+ if err != nil {
+ cln.Close()
+ return err
+ }
+
+ c.swrkCmd = cmd
+ c.swrkSk = cln
+
+ return nil
+}
+
+// Cleanup cleans up
+func (c *Criu) Cleanup() error {
+ var errs []error
+ if c.swrkCmd != nil {
+ if err := c.swrkSk.Close(); err != nil {
+ errs = append(errs, err)
+ }
+ c.swrkSk = nil
+ if err := c.swrkCmd.Wait(); err != nil {
+ errs = append(errs, fmt.Errorf("criu swrk failed: %w", err))
+ }
+ c.swrkCmd = nil
+ }
+ return errors.Join(errs...)
+}
+
+func (c *Criu) sendAndRecv(reqB []byte) ([]byte, int, error) {
+ cln := c.swrkSk
+ _, err := cln.Write(reqB)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ respB := make([]byte, 2*4096)
+ n, err := cln.Read(respB)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ return respB, n, nil
+}
+
+func (c *Criu) doSwrk(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) error {
+ resp, err := c.doSwrkWithResp(reqType, opts, nfy, nil)
+ if err != nil {
+ return err
+ }
+ respType := resp.GetType()
+ if respType != reqType {
+ return errors.New("unexpected CRIU RPC response")
+ }
+
+ return nil
+}
+
+func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify, features *rpc.CriuFeatures) (resp *rpc.CriuResp, retErr error) {
+ req := rpc.CriuReq{
+ Type: &reqType,
+ Opts: opts,
+ }
+
+ if nfy != nil {
+ opts.NotifyScripts = proto.Bool(true)
+ }
+
+ if features != nil {
+ req.Features = features
+ }
+
+ if c.swrkCmd == nil {
+ err := c.Prepare()
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ // append any cleanup errors to the returned error
+ err := c.Cleanup()
+ if err != nil {
+ retErr = errors.Join(retErr, err)
+ }
+ }()
+ }
+
+ for {
+ reqB, err := proto.Marshal(&req)
+ if err != nil {
+ return nil, err
+ }
+
+ respB, respS, err := c.sendAndRecv(reqB)
+ if err != nil {
+ return nil, err
+ }
+
+ resp = &rpc.CriuResp{}
+ err = proto.Unmarshal(respB[:respS], resp)
+ if err != nil {
+ return nil, err
+ }
+
+ if !resp.GetSuccess() {
+ return resp, fmt.Errorf("operation failed (msg:%s err:%d)",
+ resp.GetCrErrmsg(), resp.GetCrErrno())
+ }
+
+ respType := resp.GetType()
+ if respType != rpc.CriuReqType_NOTIFY {
+ break
+ }
+ if nfy == nil {
+ return resp, errors.New("unexpected notify")
+ }
+
+ notify := resp.GetNotify()
+ switch notify.GetScript() {
+ case "pre-dump":
+ err = nfy.PreDump()
+ case "post-dump":
+ err = nfy.PostDump()
+ case "pre-restore":
+ err = nfy.PreRestore()
+ case "post-restore":
+ err = nfy.PostRestore(notify.GetPid())
+ case "network-lock":
+ err = nfy.NetworkLock()
+ case "network-unlock":
+ err = nfy.NetworkUnlock()
+ case "setup-namespaces":
+ err = nfy.SetupNamespaces(notify.GetPid())
+ case "post-setup-namespaces":
+ err = nfy.PostSetupNamespaces()
+ case "post-resume":
+ err = nfy.PostResume()
+ default:
+ err = nil
+ }
+
+ if err != nil {
+ return resp, err
+ }
+
+ req = rpc.CriuReq{
+ Type: &respType,
+ NotifySuccess: proto.Bool(true),
+ }
+ }
+
+ return resp, nil
+}
+
+// Dump dumps a process
+func (c *Criu) Dump(opts *rpc.CriuOpts, nfy Notify) error {
+ return c.doSwrk(rpc.CriuReqType_DUMP, opts, nfy)
+}
+
+// Restore restores a process
+func (c *Criu) Restore(opts *rpc.CriuOpts, nfy Notify) error {
+ return c.doSwrk(rpc.CriuReqType_RESTORE, opts, nfy)
+}
+
+// PreDump does a pre-dump
+func (c *Criu) PreDump(opts *rpc.CriuOpts, nfy Notify) error {
+ return c.doSwrk(rpc.CriuReqType_PRE_DUMP, opts, nfy)
+}
+
+// StartPageServer starts the page server
+func (c *Criu) StartPageServer(opts *rpc.CriuOpts) error {
+ return c.doSwrk(rpc.CriuReqType_PAGE_SERVER, opts, nil)
+}
+
+// StartPageServerChld starts the page server and returns PID and port
+func (c *Criu) StartPageServerChld(opts *rpc.CriuOpts) (int, int, error) {
+ resp, err := c.doSwrkWithResp(rpc.CriuReqType_PAGE_SERVER_CHLD, opts, nil, nil)
+ if err != nil {
+ return 0, 0, err
+ }
+
+ return int(resp.GetPs().GetPid()), int(resp.GetPs().GetPort()), nil
+}
+
+// GetCriuVersion executes the VERSION RPC call and returns the version
+// as an integer. Major * 10000 + Minor * 100 + SubLevel
+func (c *Criu) GetCriuVersion() (int, error) {
+ resp, err := c.doSwrkWithResp(rpc.CriuReqType_VERSION, nil, nil, nil)
+ if err != nil {
+ return 0, err
+ }
+
+ if resp.GetType() != rpc.CriuReqType_VERSION {
+ return 0, errors.New("unexpected CRIU RPC response")
+ }
+
+ version := resp.GetVersion().GetMajorNumber() * 10000
+ version += resp.GetVersion().GetMinorNumber() * 100
+ if resp.GetVersion().GetSublevel() != 0 {
+ version += resp.GetVersion().GetSublevel()
+ }
+
+ if resp.GetVersion().GetGitid() != "" {
+ // taken from runc: if it is a git release -> increase minor by 1
+ version -= (version % 100)
+ version += 100
+ }
+
+ return int(version), nil
+}
+
+// IsCriuAtLeast checks if the version is at least the same
+// as the parameter version
+func (c *Criu) IsCriuAtLeast(version int) (bool, error) {
+ criuVersion, err := c.GetCriuVersion()
+ if err != nil {
+ return false, err
+ }
+
+ if criuVersion >= version {
+ return true, nil
+ }
+
+ return false, nil
+}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/notify.go b/vendor/github.com/checkpoint-restore/go-criu/v7/notify.go
new file mode 100644
index 00000000..a177f2bb
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/notify.go
@@ -0,0 +1,62 @@
+package criu
+
+// Notify interface
+type Notify interface {
+ PreDump() error
+ PostDump() error
+ PreRestore() error
+ PostRestore(pid int32) error
+ NetworkLock() error
+ NetworkUnlock() error
+ SetupNamespaces(pid int32) error
+ PostSetupNamespaces() error
+ PostResume() error
+}
+
+// NoNotify struct
+type NoNotify struct{}
+
+// PreDump NoNotify
+func (c NoNotify) PreDump() error {
+ return nil
+}
+
+// PostDump NoNotify
+func (c NoNotify) PostDump() error {
+ return nil
+}
+
+// PreRestore NoNotify
+func (c NoNotify) PreRestore() error {
+ return nil
+}
+
+// PostRestore NoNotify
+func (c NoNotify) PostRestore(pid int32) error {
+ return nil
+}
+
+// NetworkLock NoNotify
+func (c NoNotify) NetworkLock() error {
+ return nil
+}
+
+// NetworkUnlock NoNotify
+func (c NoNotify) NetworkUnlock() error {
+ return nil
+}
+
+// SetupNamespaces NoNotify
+func (c NoNotify) SetupNamespaces(pid int32) error {
+ return nil
+}
+
+// PostSetupNamespaces NoNotify
+func (c NoNotify) PostSetupNamespaces() error {
+ return nil
+}
+
+// PostResume NoNotify
+func (c NoNotify) PostResume() error {
+ return nil
+}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/rpc/rpc.pb.go b/vendor/github.com/checkpoint-restore/go-criu/v7/rpc/rpc.pb.go
new file mode 100644
index 00000000..730496b0
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/rpc/rpc.pb.go
@@ -0,0 +1,2390 @@
+// SPDX-License-Identifier: MIT
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.30.0
+// protoc v4.23.4
+// source: rpc/rpc.proto
+
+package rpc
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type CriuCgMode int32
+
+const (
+ CriuCgMode_IGNORE CriuCgMode = 0
+ CriuCgMode_CG_NONE CriuCgMode = 1
+ CriuCgMode_PROPS CriuCgMode = 2
+ CriuCgMode_SOFT CriuCgMode = 3
+ CriuCgMode_FULL CriuCgMode = 4
+ CriuCgMode_STRICT CriuCgMode = 5
+ CriuCgMode_DEFAULT CriuCgMode = 6
+)
+
+// Enum value maps for CriuCgMode.
+var (
+ CriuCgMode_name = map[int32]string{
+ 0: "IGNORE",
+ 1: "CG_NONE",
+ 2: "PROPS",
+ 3: "SOFT",
+ 4: "FULL",
+ 5: "STRICT",
+ 6: "DEFAULT",
+ }
+ CriuCgMode_value = map[string]int32{
+ "IGNORE": 0,
+ "CG_NONE": 1,
+ "PROPS": 2,
+ "SOFT": 3,
+ "FULL": 4,
+ "STRICT": 5,
+ "DEFAULT": 6,
+ }
+)
+
+func (x CriuCgMode) Enum() *CriuCgMode {
+ p := new(CriuCgMode)
+ *p = x
+ return p
+}
+
+func (x CriuCgMode) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (CriuCgMode) Descriptor() protoreflect.EnumDescriptor {
+ return file_rpc_rpc_proto_enumTypes[0].Descriptor()
+}
+
+func (CriuCgMode) Type() protoreflect.EnumType {
+ return &file_rpc_rpc_proto_enumTypes[0]
+}
+
+func (x CriuCgMode) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *CriuCgMode) UnmarshalJSON(b []byte) error {
+ num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+ if err != nil {
+ return err
+ }
+ *x = CriuCgMode(num)
+ return nil
+}
+
+// Deprecated: Use CriuCgMode.Descriptor instead.
+func (CriuCgMode) EnumDescriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{0}
+}
+
+type CriuNetworkLockMethod int32
+
+const (
+ CriuNetworkLockMethod_IPTABLES CriuNetworkLockMethod = 1
+ CriuNetworkLockMethod_NFTABLES CriuNetworkLockMethod = 2
+ CriuNetworkLockMethod_SKIP CriuNetworkLockMethod = 3
+)
+
+// Enum value maps for CriuNetworkLockMethod.
+var (
+ CriuNetworkLockMethod_name = map[int32]string{
+ 1: "IPTABLES",
+ 2: "NFTABLES",
+ 3: "SKIP",
+ }
+ CriuNetworkLockMethod_value = map[string]int32{
+ "IPTABLES": 1,
+ "NFTABLES": 2,
+ "SKIP": 3,
+ }
+)
+
+func (x CriuNetworkLockMethod) Enum() *CriuNetworkLockMethod {
+ p := new(CriuNetworkLockMethod)
+ *p = x
+ return p
+}
+
+func (x CriuNetworkLockMethod) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (CriuNetworkLockMethod) Descriptor() protoreflect.EnumDescriptor {
+ return file_rpc_rpc_proto_enumTypes[1].Descriptor()
+}
+
+func (CriuNetworkLockMethod) Type() protoreflect.EnumType {
+ return &file_rpc_rpc_proto_enumTypes[1]
+}
+
+func (x CriuNetworkLockMethod) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *CriuNetworkLockMethod) UnmarshalJSON(b []byte) error {
+ num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+ if err != nil {
+ return err
+ }
+ *x = CriuNetworkLockMethod(num)
+ return nil
+}
+
+// Deprecated: Use CriuNetworkLockMethod.Descriptor instead.
+func (CriuNetworkLockMethod) EnumDescriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{1}
+}
+
+type CriuPreDumpMode int32
+
+const (
+ CriuPreDumpMode_SPLICE CriuPreDumpMode = 1
+ CriuPreDumpMode_VM_READ CriuPreDumpMode = 2
+)
+
+// Enum value maps for CriuPreDumpMode.
+var (
+ CriuPreDumpMode_name = map[int32]string{
+ 1: "SPLICE",
+ 2: "VM_READ",
+ }
+ CriuPreDumpMode_value = map[string]int32{
+ "SPLICE": 1,
+ "VM_READ": 2,
+ }
+)
+
+func (x CriuPreDumpMode) Enum() *CriuPreDumpMode {
+ p := new(CriuPreDumpMode)
+ *p = x
+ return p
+}
+
+func (x CriuPreDumpMode) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (CriuPreDumpMode) Descriptor() protoreflect.EnumDescriptor {
+ return file_rpc_rpc_proto_enumTypes[2].Descriptor()
+}
+
+func (CriuPreDumpMode) Type() protoreflect.EnumType {
+ return &file_rpc_rpc_proto_enumTypes[2]
+}
+
+func (x CriuPreDumpMode) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *CriuPreDumpMode) UnmarshalJSON(b []byte) error {
+ num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+ if err != nil {
+ return err
+ }
+ *x = CriuPreDumpMode(num)
+ return nil
+}
+
+// Deprecated: Use CriuPreDumpMode.Descriptor instead.
+func (CriuPreDumpMode) EnumDescriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{2}
+}
+
+type CriuReqType int32
+
+const (
+ CriuReqType_EMPTY CriuReqType = 0
+ CriuReqType_DUMP CriuReqType = 1
+ CriuReqType_RESTORE CriuReqType = 2
+ CriuReqType_CHECK CriuReqType = 3
+ CriuReqType_PRE_DUMP CriuReqType = 4
+ CriuReqType_PAGE_SERVER CriuReqType = 5
+ CriuReqType_NOTIFY CriuReqType = 6
+ CriuReqType_CPUINFO_DUMP CriuReqType = 7
+ CriuReqType_CPUINFO_CHECK CriuReqType = 8
+ CriuReqType_FEATURE_CHECK CriuReqType = 9
+ CriuReqType_VERSION CriuReqType = 10
+ CriuReqType_WAIT_PID CriuReqType = 11
+ CriuReqType_PAGE_SERVER_CHLD CriuReqType = 12
+ CriuReqType_SINGLE_PRE_DUMP CriuReqType = 13
+)
+
+// Enum value maps for CriuReqType.
+var (
+ CriuReqType_name = map[int32]string{
+ 0: "EMPTY",
+ 1: "DUMP",
+ 2: "RESTORE",
+ 3: "CHECK",
+ 4: "PRE_DUMP",
+ 5: "PAGE_SERVER",
+ 6: "NOTIFY",
+ 7: "CPUINFO_DUMP",
+ 8: "CPUINFO_CHECK",
+ 9: "FEATURE_CHECK",
+ 10: "VERSION",
+ 11: "WAIT_PID",
+ 12: "PAGE_SERVER_CHLD",
+ 13: "SINGLE_PRE_DUMP",
+ }
+ CriuReqType_value = map[string]int32{
+ "EMPTY": 0,
+ "DUMP": 1,
+ "RESTORE": 2,
+ "CHECK": 3,
+ "PRE_DUMP": 4,
+ "PAGE_SERVER": 5,
+ "NOTIFY": 6,
+ "CPUINFO_DUMP": 7,
+ "CPUINFO_CHECK": 8,
+ "FEATURE_CHECK": 9,
+ "VERSION": 10,
+ "WAIT_PID": 11,
+ "PAGE_SERVER_CHLD": 12,
+ "SINGLE_PRE_DUMP": 13,
+ }
+)
+
+func (x CriuReqType) Enum() *CriuReqType {
+ p := new(CriuReqType)
+ *p = x
+ return p
+}
+
+func (x CriuReqType) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (CriuReqType) Descriptor() protoreflect.EnumDescriptor {
+ return file_rpc_rpc_proto_enumTypes[3].Descriptor()
+}
+
+func (CriuReqType) Type() protoreflect.EnumType {
+ return &file_rpc_rpc_proto_enumTypes[3]
+}
+
+func (x CriuReqType) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *CriuReqType) UnmarshalJSON(b []byte) error {
+ num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+ if err != nil {
+ return err
+ }
+ *x = CriuReqType(num)
+ return nil
+}
+
+// Deprecated: Use CriuReqType.Descriptor instead.
+func (CriuReqType) EnumDescriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{3}
+}
+
+type CriuPageServerInfo struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Address *string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"`
+ Port *int32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"`
+ Pid *int32 `protobuf:"varint,3,opt,name=pid" json:"pid,omitempty"`
+ Fd *int32 `protobuf:"varint,4,opt,name=fd" json:"fd,omitempty"`
+}
+
+func (x *CriuPageServerInfo) Reset() {
+ *x = CriuPageServerInfo{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CriuPageServerInfo) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriuPageServerInfo) ProtoMessage() {}
+
+func (x *CriuPageServerInfo) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriuPageServerInfo.ProtoReflect.Descriptor instead.
+func (*CriuPageServerInfo) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *CriuPageServerInfo) GetAddress() string {
+ if x != nil && x.Address != nil {
+ return *x.Address
+ }
+ return ""
+}
+
+func (x *CriuPageServerInfo) GetPort() int32 {
+ if x != nil && x.Port != nil {
+ return *x.Port
+ }
+ return 0
+}
+
+func (x *CriuPageServerInfo) GetPid() int32 {
+ if x != nil && x.Pid != nil {
+ return *x.Pid
+ }
+ return 0
+}
+
+func (x *CriuPageServerInfo) GetFd() int32 {
+ if x != nil && x.Fd != nil {
+ return *x.Fd
+ }
+ return 0
+}
+
+type CriuVethPair struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ IfIn *string `protobuf:"bytes,1,req,name=if_in,json=ifIn" json:"if_in,omitempty"`
+ IfOut *string `protobuf:"bytes,2,req,name=if_out,json=ifOut" json:"if_out,omitempty"`
+}
+
+func (x *CriuVethPair) Reset() {
+ *x = CriuVethPair{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CriuVethPair) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriuVethPair) ProtoMessage() {}
+
+func (x *CriuVethPair) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriuVethPair.ProtoReflect.Descriptor instead.
+func (*CriuVethPair) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *CriuVethPair) GetIfIn() string {
+ if x != nil && x.IfIn != nil {
+ return *x.IfIn
+ }
+ return ""
+}
+
+func (x *CriuVethPair) GetIfOut() string {
+ if x != nil && x.IfOut != nil {
+ return *x.IfOut
+ }
+ return ""
+}
+
+type ExtMountMap struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Key *string `protobuf:"bytes,1,req,name=key" json:"key,omitempty"`
+ Val *string `protobuf:"bytes,2,req,name=val" json:"val,omitempty"`
+}
+
+func (x *ExtMountMap) Reset() {
+ *x = ExtMountMap{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *ExtMountMap) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExtMountMap) ProtoMessage() {}
+
+func (x *ExtMountMap) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ExtMountMap.ProtoReflect.Descriptor instead.
+func (*ExtMountMap) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *ExtMountMap) GetKey() string {
+ if x != nil && x.Key != nil {
+ return *x.Key
+ }
+ return ""
+}
+
+func (x *ExtMountMap) GetVal() string {
+ if x != nil && x.Val != nil {
+ return *x.Val
+ }
+ return ""
+}
+
+type JoinNamespace struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Ns *string `protobuf:"bytes,1,req,name=ns" json:"ns,omitempty"`
+ NsFile *string `protobuf:"bytes,2,req,name=ns_file,json=nsFile" json:"ns_file,omitempty"`
+ ExtraOpt *string `protobuf:"bytes,3,opt,name=extra_opt,json=extraOpt" json:"extra_opt,omitempty"`
+}
+
+func (x *JoinNamespace) Reset() {
+ *x = JoinNamespace{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *JoinNamespace) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*JoinNamespace) ProtoMessage() {}
+
+func (x *JoinNamespace) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use JoinNamespace.ProtoReflect.Descriptor instead.
+func (*JoinNamespace) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *JoinNamespace) GetNs() string {
+ if x != nil && x.Ns != nil {
+ return *x.Ns
+ }
+ return ""
+}
+
+func (x *JoinNamespace) GetNsFile() string {
+ if x != nil && x.NsFile != nil {
+ return *x.NsFile
+ }
+ return ""
+}
+
+func (x *JoinNamespace) GetExtraOpt() string {
+ if x != nil && x.ExtraOpt != nil {
+ return *x.ExtraOpt
+ }
+ return ""
+}
+
+type InheritFd struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Key *string `protobuf:"bytes,1,req,name=key" json:"key,omitempty"`
+ Fd *int32 `protobuf:"varint,2,req,name=fd" json:"fd,omitempty"`
+}
+
+func (x *InheritFd) Reset() {
+ *x = InheritFd{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *InheritFd) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*InheritFd) ProtoMessage() {}
+
+func (x *InheritFd) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[4]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use InheritFd.ProtoReflect.Descriptor instead.
+func (*InheritFd) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *InheritFd) GetKey() string {
+ if x != nil && x.Key != nil {
+ return *x.Key
+ }
+ return ""
+}
+
+func (x *InheritFd) GetFd() int32 {
+ if x != nil && x.Fd != nil {
+ return *x.Fd
+ }
+ return 0
+}
+
+type CgroupRoot struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Ctrl *string `protobuf:"bytes,1,opt,name=ctrl" json:"ctrl,omitempty"`
+ Path *string `protobuf:"bytes,2,req,name=path" json:"path,omitempty"`
+}
+
+func (x *CgroupRoot) Reset() {
+ *x = CgroupRoot{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CgroupRoot) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CgroupRoot) ProtoMessage() {}
+
+func (x *CgroupRoot) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[5]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CgroupRoot.ProtoReflect.Descriptor instead.
+func (*CgroupRoot) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *CgroupRoot) GetCtrl() string {
+ if x != nil && x.Ctrl != nil {
+ return *x.Ctrl
+ }
+ return ""
+}
+
+func (x *CgroupRoot) GetPath() string {
+ if x != nil && x.Path != nil {
+ return *x.Path
+ }
+ return ""
+}
+
+type UnixSk struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Inode *uint32 `protobuf:"varint,1,req,name=inode" json:"inode,omitempty"`
+}
+
+func (x *UnixSk) Reset() {
+ *x = UnixSk{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *UnixSk) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UnixSk) ProtoMessage() {}
+
+func (x *UnixSk) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[6]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use UnixSk.ProtoReflect.Descriptor instead.
+func (*UnixSk) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *UnixSk) GetInode() uint32 {
+ if x != nil && x.Inode != nil {
+ return *x.Inode
+ }
+ return 0
+}
+
+type CriuOpts struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ ImagesDirFd *int32 `protobuf:"varint,1,req,name=images_dir_fd,json=imagesDirFd,def=-1" json:"images_dir_fd,omitempty"`
+ ImagesDir *string `protobuf:"bytes,68,opt,name=images_dir,json=imagesDir" json:"images_dir,omitempty"` // used only if images_dir_fd == -1
+ Pid *int32 `protobuf:"varint,2,opt,name=pid" json:"pid,omitempty"` // if not set on dump, will dump requesting process
+ LeaveRunning *bool `protobuf:"varint,3,opt,name=leave_running,json=leaveRunning" json:"leave_running,omitempty"`
+ ExtUnixSk *bool `protobuf:"varint,4,opt,name=ext_unix_sk,json=extUnixSk" json:"ext_unix_sk,omitempty"`
+ TcpEstablished *bool `protobuf:"varint,5,opt,name=tcp_established,json=tcpEstablished" json:"tcp_established,omitempty"`
+ EvasiveDevices *bool `protobuf:"varint,6,opt,name=evasive_devices,json=evasiveDevices" json:"evasive_devices,omitempty"`
+ ShellJob *bool `protobuf:"varint,7,opt,name=shell_job,json=shellJob" json:"shell_job,omitempty"`
+ FileLocks *bool `protobuf:"varint,8,opt,name=file_locks,json=fileLocks" json:"file_locks,omitempty"`
+ LogLevel *int32 `protobuf:"varint,9,opt,name=log_level,json=logLevel,def=2" json:"log_level,omitempty"`
+ LogFile *string `protobuf:"bytes,10,opt,name=log_file,json=logFile" json:"log_file,omitempty"` // No subdirs are allowed. Consider using work-dir
+ Ps *CriuPageServerInfo `protobuf:"bytes,11,opt,name=ps" json:"ps,omitempty"`
+ NotifyScripts *bool `protobuf:"varint,12,opt,name=notify_scripts,json=notifyScripts" json:"notify_scripts,omitempty"`
+ Root *string `protobuf:"bytes,13,opt,name=root" json:"root,omitempty"`
+ ParentImg *string `protobuf:"bytes,14,opt,name=parent_img,json=parentImg" json:"parent_img,omitempty"`
+ TrackMem *bool `protobuf:"varint,15,opt,name=track_mem,json=trackMem" json:"track_mem,omitempty"`
+ AutoDedup *bool `protobuf:"varint,16,opt,name=auto_dedup,json=autoDedup" json:"auto_dedup,omitempty"`
+ WorkDirFd *int32 `protobuf:"varint,17,opt,name=work_dir_fd,json=workDirFd" json:"work_dir_fd,omitempty"`
+ LinkRemap *bool `protobuf:"varint,18,opt,name=link_remap,json=linkRemap" json:"link_remap,omitempty"`
+ Veths []*CriuVethPair `protobuf:"bytes,19,rep,name=veths" json:"veths,omitempty"` // DEPRECATED, use external instead
+ CpuCap *uint32 `protobuf:"varint,20,opt,name=cpu_cap,json=cpuCap,def=4294967295" json:"cpu_cap,omitempty"`
+ ForceIrmap *bool `protobuf:"varint,21,opt,name=force_irmap,json=forceIrmap" json:"force_irmap,omitempty"`
+ ExecCmd []string `protobuf:"bytes,22,rep,name=exec_cmd,json=execCmd" json:"exec_cmd,omitempty"`
+ ExtMnt []*ExtMountMap `protobuf:"bytes,23,rep,name=ext_mnt,json=extMnt" json:"ext_mnt,omitempty"` // DEPRECATED, use external instead
+ ManageCgroups *bool `protobuf:"varint,24,opt,name=manage_cgroups,json=manageCgroups" json:"manage_cgroups,omitempty"` // backward compatibility
+ CgRoot []*CgroupRoot `protobuf:"bytes,25,rep,name=cg_root,json=cgRoot" json:"cg_root,omitempty"`
+ RstSibling *bool `protobuf:"varint,26,opt,name=rst_sibling,json=rstSibling" json:"rst_sibling,omitempty"` // swrk only
+ InheritFd []*InheritFd `protobuf:"bytes,27,rep,name=inherit_fd,json=inheritFd" json:"inherit_fd,omitempty"` // swrk only
+ AutoExtMnt *bool `protobuf:"varint,28,opt,name=auto_ext_mnt,json=autoExtMnt" json:"auto_ext_mnt,omitempty"`
+ ExtSharing *bool `protobuf:"varint,29,opt,name=ext_sharing,json=extSharing" json:"ext_sharing,omitempty"`
+ ExtMasters *bool `protobuf:"varint,30,opt,name=ext_masters,json=extMasters" json:"ext_masters,omitempty"`
+ SkipMnt []string `protobuf:"bytes,31,rep,name=skip_mnt,json=skipMnt" json:"skip_mnt,omitempty"`
+ EnableFs []string `protobuf:"bytes,32,rep,name=enable_fs,json=enableFs" json:"enable_fs,omitempty"`
+ UnixSkIno []*UnixSk `protobuf:"bytes,33,rep,name=unix_sk_ino,json=unixSkIno" json:"unix_sk_ino,omitempty"` // DEPRECATED, use external instead
+ ManageCgroupsMode *CriuCgMode `protobuf:"varint,34,opt,name=manage_cgroups_mode,json=manageCgroupsMode,enum=CriuCgMode" json:"manage_cgroups_mode,omitempty"`
+ GhostLimit *uint32 `protobuf:"varint,35,opt,name=ghost_limit,json=ghostLimit,def=1048576" json:"ghost_limit,omitempty"`
+ IrmapScanPaths []string `protobuf:"bytes,36,rep,name=irmap_scan_paths,json=irmapScanPaths" json:"irmap_scan_paths,omitempty"`
+ External []string `protobuf:"bytes,37,rep,name=external" json:"external,omitempty"`
+ EmptyNs *uint32 `protobuf:"varint,38,opt,name=empty_ns,json=emptyNs" json:"empty_ns,omitempty"`
+ JoinNs []*JoinNamespace `protobuf:"bytes,39,rep,name=join_ns,json=joinNs" json:"join_ns,omitempty"`
+ CgroupProps *string `protobuf:"bytes,41,opt,name=cgroup_props,json=cgroupProps" json:"cgroup_props,omitempty"`
+ CgroupPropsFile *string `protobuf:"bytes,42,opt,name=cgroup_props_file,json=cgroupPropsFile" json:"cgroup_props_file,omitempty"`
+ CgroupDumpController []string `protobuf:"bytes,43,rep,name=cgroup_dump_controller,json=cgroupDumpController" json:"cgroup_dump_controller,omitempty"`
+ FreezeCgroup *string `protobuf:"bytes,44,opt,name=freeze_cgroup,json=freezeCgroup" json:"freeze_cgroup,omitempty"`
+ Timeout *uint32 `protobuf:"varint,45,opt,name=timeout" json:"timeout,omitempty"`
+ TcpSkipInFlight *bool `protobuf:"varint,46,opt,name=tcp_skip_in_flight,json=tcpSkipInFlight" json:"tcp_skip_in_flight,omitempty"`
+ WeakSysctls *bool `protobuf:"varint,47,opt,name=weak_sysctls,json=weakSysctls" json:"weak_sysctls,omitempty"`
+ LazyPages *bool `protobuf:"varint,48,opt,name=lazy_pages,json=lazyPages" json:"lazy_pages,omitempty"`
+ StatusFd *int32 `protobuf:"varint,49,opt,name=status_fd,json=statusFd" json:"status_fd,omitempty"`
+ OrphanPtsMaster *bool `protobuf:"varint,50,opt,name=orphan_pts_master,json=orphanPtsMaster" json:"orphan_pts_master,omitempty"`
+ ConfigFile *string `protobuf:"bytes,51,opt,name=config_file,json=configFile" json:"config_file,omitempty"`
+ TcpClose *bool `protobuf:"varint,52,opt,name=tcp_close,json=tcpClose" json:"tcp_close,omitempty"`
+ LsmProfile *string `protobuf:"bytes,53,opt,name=lsm_profile,json=lsmProfile" json:"lsm_profile,omitempty"`
+ TlsCacert *string `protobuf:"bytes,54,opt,name=tls_cacert,json=tlsCacert" json:"tls_cacert,omitempty"`
+ TlsCacrl *string `protobuf:"bytes,55,opt,name=tls_cacrl,json=tlsCacrl" json:"tls_cacrl,omitempty"`
+ TlsCert *string `protobuf:"bytes,56,opt,name=tls_cert,json=tlsCert" json:"tls_cert,omitempty"`
+ TlsKey *string `protobuf:"bytes,57,opt,name=tls_key,json=tlsKey" json:"tls_key,omitempty"`
+ Tls *bool `protobuf:"varint,58,opt,name=tls" json:"tls,omitempty"`
+ TlsNoCnVerify *bool `protobuf:"varint,59,opt,name=tls_no_cn_verify,json=tlsNoCnVerify" json:"tls_no_cn_verify,omitempty"`
+ CgroupYard *string `protobuf:"bytes,60,opt,name=cgroup_yard,json=cgroupYard" json:"cgroup_yard,omitempty"`
+ PreDumpMode *CriuPreDumpMode `protobuf:"varint,61,opt,name=pre_dump_mode,json=preDumpMode,enum=CriuPreDumpMode,def=1" json:"pre_dump_mode,omitempty"`
+ PidfdStoreSk *int32 `protobuf:"varint,62,opt,name=pidfd_store_sk,json=pidfdStoreSk" json:"pidfd_store_sk,omitempty"`
+ LsmMountContext *string `protobuf:"bytes,63,opt,name=lsm_mount_context,json=lsmMountContext" json:"lsm_mount_context,omitempty"`
+ NetworkLock *CriuNetworkLockMethod `protobuf:"varint,64,opt,name=network_lock,json=networkLock,enum=CriuNetworkLockMethod,def=1" json:"network_lock,omitempty"`
+ MntnsCompatMode *bool `protobuf:"varint,65,opt,name=mntns_compat_mode,json=mntnsCompatMode" json:"mntns_compat_mode,omitempty"`
+ SkipFileRwxCheck *bool `protobuf:"varint,66,opt,name=skip_file_rwx_check,json=skipFileRwxCheck" json:"skip_file_rwx_check,omitempty"`
+ Unprivileged *bool `protobuf:"varint,67,opt,name=unprivileged" json:"unprivileged,omitempty"`
+ LeaveStopped *bool `protobuf:"varint,69,opt,name=leave_stopped,json=leaveStopped" json:"leave_stopped,omitempty"`
+ DisplayStats *bool `protobuf:"varint,70,opt,name=display_stats,json=displayStats" json:"display_stats,omitempty"`
+ LogToStderr *bool `protobuf:"varint,71,opt,name=log_to_stderr,json=logToStderr" json:"log_to_stderr,omitempty"` // optional bool check_mounts = 128;
+}
+
+// Default values for CriuOpts fields.
+const (
+ Default_CriuOpts_ImagesDirFd = int32(-1)
+ Default_CriuOpts_LogLevel = int32(2)
+ Default_CriuOpts_CpuCap = uint32(4294967295)
+ Default_CriuOpts_GhostLimit = uint32(1048576)
+ Default_CriuOpts_PreDumpMode = CriuPreDumpMode_SPLICE
+ Default_CriuOpts_NetworkLock = CriuNetworkLockMethod_IPTABLES
+)
+
+func (x *CriuOpts) Reset() {
+ *x = CriuOpts{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[7]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CriuOpts) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriuOpts) ProtoMessage() {}
+
+func (x *CriuOpts) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[7]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriuOpts.ProtoReflect.Descriptor instead.
+func (*CriuOpts) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *CriuOpts) GetImagesDirFd() int32 {
+ if x != nil && x.ImagesDirFd != nil {
+ return *x.ImagesDirFd
+ }
+ return Default_CriuOpts_ImagesDirFd
+}
+
+func (x *CriuOpts) GetImagesDir() string {
+ if x != nil && x.ImagesDir != nil {
+ return *x.ImagesDir
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetPid() int32 {
+ if x != nil && x.Pid != nil {
+ return *x.Pid
+ }
+ return 0
+}
+
+func (x *CriuOpts) GetLeaveRunning() bool {
+ if x != nil && x.LeaveRunning != nil {
+ return *x.LeaveRunning
+ }
+ return false
+}
+
+func (x *CriuOpts) GetExtUnixSk() bool {
+ if x != nil && x.ExtUnixSk != nil {
+ return *x.ExtUnixSk
+ }
+ return false
+}
+
+func (x *CriuOpts) GetTcpEstablished() bool {
+ if x != nil && x.TcpEstablished != nil {
+ return *x.TcpEstablished
+ }
+ return false
+}
+
+func (x *CriuOpts) GetEvasiveDevices() bool {
+ if x != nil && x.EvasiveDevices != nil {
+ return *x.EvasiveDevices
+ }
+ return false
+}
+
+func (x *CriuOpts) GetShellJob() bool {
+ if x != nil && x.ShellJob != nil {
+ return *x.ShellJob
+ }
+ return false
+}
+
+func (x *CriuOpts) GetFileLocks() bool {
+ if x != nil && x.FileLocks != nil {
+ return *x.FileLocks
+ }
+ return false
+}
+
+func (x *CriuOpts) GetLogLevel() int32 {
+ if x != nil && x.LogLevel != nil {
+ return *x.LogLevel
+ }
+ return Default_CriuOpts_LogLevel
+}
+
+func (x *CriuOpts) GetLogFile() string {
+ if x != nil && x.LogFile != nil {
+ return *x.LogFile
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetPs() *CriuPageServerInfo {
+ if x != nil {
+ return x.Ps
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetNotifyScripts() bool {
+ if x != nil && x.NotifyScripts != nil {
+ return *x.NotifyScripts
+ }
+ return false
+}
+
+func (x *CriuOpts) GetRoot() string {
+ if x != nil && x.Root != nil {
+ return *x.Root
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetParentImg() string {
+ if x != nil && x.ParentImg != nil {
+ return *x.ParentImg
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetTrackMem() bool {
+ if x != nil && x.TrackMem != nil {
+ return *x.TrackMem
+ }
+ return false
+}
+
+func (x *CriuOpts) GetAutoDedup() bool {
+ if x != nil && x.AutoDedup != nil {
+ return *x.AutoDedup
+ }
+ return false
+}
+
+func (x *CriuOpts) GetWorkDirFd() int32 {
+ if x != nil && x.WorkDirFd != nil {
+ return *x.WorkDirFd
+ }
+ return 0
+}
+
+func (x *CriuOpts) GetLinkRemap() bool {
+ if x != nil && x.LinkRemap != nil {
+ return *x.LinkRemap
+ }
+ return false
+}
+
+func (x *CriuOpts) GetVeths() []*CriuVethPair {
+ if x != nil {
+ return x.Veths
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetCpuCap() uint32 {
+ if x != nil && x.CpuCap != nil {
+ return *x.CpuCap
+ }
+ return Default_CriuOpts_CpuCap
+}
+
+func (x *CriuOpts) GetForceIrmap() bool {
+ if x != nil && x.ForceIrmap != nil {
+ return *x.ForceIrmap
+ }
+ return false
+}
+
+func (x *CriuOpts) GetExecCmd() []string {
+ if x != nil {
+ return x.ExecCmd
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetExtMnt() []*ExtMountMap {
+ if x != nil {
+ return x.ExtMnt
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetManageCgroups() bool {
+ if x != nil && x.ManageCgroups != nil {
+ return *x.ManageCgroups
+ }
+ return false
+}
+
+func (x *CriuOpts) GetCgRoot() []*CgroupRoot {
+ if x != nil {
+ return x.CgRoot
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetRstSibling() bool {
+ if x != nil && x.RstSibling != nil {
+ return *x.RstSibling
+ }
+ return false
+}
+
+func (x *CriuOpts) GetInheritFd() []*InheritFd {
+ if x != nil {
+ return x.InheritFd
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetAutoExtMnt() bool {
+ if x != nil && x.AutoExtMnt != nil {
+ return *x.AutoExtMnt
+ }
+ return false
+}
+
+func (x *CriuOpts) GetExtSharing() bool {
+ if x != nil && x.ExtSharing != nil {
+ return *x.ExtSharing
+ }
+ return false
+}
+
+func (x *CriuOpts) GetExtMasters() bool {
+ if x != nil && x.ExtMasters != nil {
+ return *x.ExtMasters
+ }
+ return false
+}
+
+func (x *CriuOpts) GetSkipMnt() []string {
+ if x != nil {
+ return x.SkipMnt
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetEnableFs() []string {
+ if x != nil {
+ return x.EnableFs
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetUnixSkIno() []*UnixSk {
+ if x != nil {
+ return x.UnixSkIno
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetManageCgroupsMode() CriuCgMode {
+ if x != nil && x.ManageCgroupsMode != nil {
+ return *x.ManageCgroupsMode
+ }
+ return CriuCgMode_IGNORE
+}
+
+func (x *CriuOpts) GetGhostLimit() uint32 {
+ if x != nil && x.GhostLimit != nil {
+ return *x.GhostLimit
+ }
+ return Default_CriuOpts_GhostLimit
+}
+
+func (x *CriuOpts) GetIrmapScanPaths() []string {
+ if x != nil {
+ return x.IrmapScanPaths
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetExternal() []string {
+ if x != nil {
+ return x.External
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetEmptyNs() uint32 {
+ if x != nil && x.EmptyNs != nil {
+ return *x.EmptyNs
+ }
+ return 0
+}
+
+func (x *CriuOpts) GetJoinNs() []*JoinNamespace {
+ if x != nil {
+ return x.JoinNs
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetCgroupProps() string {
+ if x != nil && x.CgroupProps != nil {
+ return *x.CgroupProps
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetCgroupPropsFile() string {
+ if x != nil && x.CgroupPropsFile != nil {
+ return *x.CgroupPropsFile
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetCgroupDumpController() []string {
+ if x != nil {
+ return x.CgroupDumpController
+ }
+ return nil
+}
+
+func (x *CriuOpts) GetFreezeCgroup() string {
+ if x != nil && x.FreezeCgroup != nil {
+ return *x.FreezeCgroup
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetTimeout() uint32 {
+ if x != nil && x.Timeout != nil {
+ return *x.Timeout
+ }
+ return 0
+}
+
+func (x *CriuOpts) GetTcpSkipInFlight() bool {
+ if x != nil && x.TcpSkipInFlight != nil {
+ return *x.TcpSkipInFlight
+ }
+ return false
+}
+
+func (x *CriuOpts) GetWeakSysctls() bool {
+ if x != nil && x.WeakSysctls != nil {
+ return *x.WeakSysctls
+ }
+ return false
+}
+
+func (x *CriuOpts) GetLazyPages() bool {
+ if x != nil && x.LazyPages != nil {
+ return *x.LazyPages
+ }
+ return false
+}
+
+func (x *CriuOpts) GetStatusFd() int32 {
+ if x != nil && x.StatusFd != nil {
+ return *x.StatusFd
+ }
+ return 0
+}
+
+func (x *CriuOpts) GetOrphanPtsMaster() bool {
+ if x != nil && x.OrphanPtsMaster != nil {
+ return *x.OrphanPtsMaster
+ }
+ return false
+}
+
+func (x *CriuOpts) GetConfigFile() string {
+ if x != nil && x.ConfigFile != nil {
+ return *x.ConfigFile
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetTcpClose() bool {
+ if x != nil && x.TcpClose != nil {
+ return *x.TcpClose
+ }
+ return false
+}
+
+func (x *CriuOpts) GetLsmProfile() string {
+ if x != nil && x.LsmProfile != nil {
+ return *x.LsmProfile
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetTlsCacert() string {
+ if x != nil && x.TlsCacert != nil {
+ return *x.TlsCacert
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetTlsCacrl() string {
+ if x != nil && x.TlsCacrl != nil {
+ return *x.TlsCacrl
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetTlsCert() string {
+ if x != nil && x.TlsCert != nil {
+ return *x.TlsCert
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetTlsKey() string {
+ if x != nil && x.TlsKey != nil {
+ return *x.TlsKey
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetTls() bool {
+ if x != nil && x.Tls != nil {
+ return *x.Tls
+ }
+ return false
+}
+
+func (x *CriuOpts) GetTlsNoCnVerify() bool {
+ if x != nil && x.TlsNoCnVerify != nil {
+ return *x.TlsNoCnVerify
+ }
+ return false
+}
+
+func (x *CriuOpts) GetCgroupYard() string {
+ if x != nil && x.CgroupYard != nil {
+ return *x.CgroupYard
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetPreDumpMode() CriuPreDumpMode {
+ if x != nil && x.PreDumpMode != nil {
+ return *x.PreDumpMode
+ }
+ return Default_CriuOpts_PreDumpMode
+}
+
+func (x *CriuOpts) GetPidfdStoreSk() int32 {
+ if x != nil && x.PidfdStoreSk != nil {
+ return *x.PidfdStoreSk
+ }
+ return 0
+}
+
+func (x *CriuOpts) GetLsmMountContext() string {
+ if x != nil && x.LsmMountContext != nil {
+ return *x.LsmMountContext
+ }
+ return ""
+}
+
+func (x *CriuOpts) GetNetworkLock() CriuNetworkLockMethod {
+ if x != nil && x.NetworkLock != nil {
+ return *x.NetworkLock
+ }
+ return Default_CriuOpts_NetworkLock
+}
+
+func (x *CriuOpts) GetMntnsCompatMode() bool {
+ if x != nil && x.MntnsCompatMode != nil {
+ return *x.MntnsCompatMode
+ }
+ return false
+}
+
+func (x *CriuOpts) GetSkipFileRwxCheck() bool {
+ if x != nil && x.SkipFileRwxCheck != nil {
+ return *x.SkipFileRwxCheck
+ }
+ return false
+}
+
+func (x *CriuOpts) GetUnprivileged() bool {
+ if x != nil && x.Unprivileged != nil {
+ return *x.Unprivileged
+ }
+ return false
+}
+
+func (x *CriuOpts) GetLeaveStopped() bool {
+ if x != nil && x.LeaveStopped != nil {
+ return *x.LeaveStopped
+ }
+ return false
+}
+
+func (x *CriuOpts) GetDisplayStats() bool {
+ if x != nil && x.DisplayStats != nil {
+ return *x.DisplayStats
+ }
+ return false
+}
+
+func (x *CriuOpts) GetLogToStderr() bool {
+ if x != nil && x.LogToStderr != nil {
+ return *x.LogToStderr
+ }
+ return false
+}
+
+type CriuDumpResp struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Restored *bool `protobuf:"varint,1,opt,name=restored" json:"restored,omitempty"`
+}
+
+func (x *CriuDumpResp) Reset() {
+ *x = CriuDumpResp{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CriuDumpResp) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriuDumpResp) ProtoMessage() {}
+
+func (x *CriuDumpResp) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[8]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriuDumpResp.ProtoReflect.Descriptor instead.
+func (*CriuDumpResp) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{8}
+}
+
+func (x *CriuDumpResp) GetRestored() bool {
+ if x != nil && x.Restored != nil {
+ return *x.Restored
+ }
+ return false
+}
+
+type CriuRestoreResp struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Pid *int32 `protobuf:"varint,1,req,name=pid" json:"pid,omitempty"`
+}
+
+func (x *CriuRestoreResp) Reset() {
+ *x = CriuRestoreResp{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CriuRestoreResp) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriuRestoreResp) ProtoMessage() {}
+
+func (x *CriuRestoreResp) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[9]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriuRestoreResp.ProtoReflect.Descriptor instead.
+func (*CriuRestoreResp) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *CriuRestoreResp) GetPid() int32 {
+ if x != nil && x.Pid != nil {
+ return *x.Pid
+ }
+ return 0
+}
+
+type CriuNotify struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Script *string `protobuf:"bytes,1,opt,name=script" json:"script,omitempty"`
+ Pid *int32 `protobuf:"varint,2,opt,name=pid" json:"pid,omitempty"`
+}
+
+func (x *CriuNotify) Reset() {
+ *x = CriuNotify{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CriuNotify) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriuNotify) ProtoMessage() {}
+
+func (x *CriuNotify) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[10]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriuNotify.ProtoReflect.Descriptor instead.
+func (*CriuNotify) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *CriuNotify) GetScript() string {
+ if x != nil && x.Script != nil {
+ return *x.Script
+ }
+ return ""
+}
+
+func (x *CriuNotify) GetPid() int32 {
+ if x != nil && x.Pid != nil {
+ return *x.Pid
+ }
+ return 0
+}
+
+// List of features which can queried via
+// CRIU_REQ_TYPE__FEATURE_CHECK
+type CriuFeatures struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ MemTrack *bool `protobuf:"varint,1,opt,name=mem_track,json=memTrack" json:"mem_track,omitempty"`
+ LazyPages *bool `protobuf:"varint,2,opt,name=lazy_pages,json=lazyPages" json:"lazy_pages,omitempty"`
+ PidfdStore *bool `protobuf:"varint,3,opt,name=pidfd_store,json=pidfdStore" json:"pidfd_store,omitempty"`
+}
+
+func (x *CriuFeatures) Reset() {
+ *x = CriuFeatures{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[11]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CriuFeatures) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriuFeatures) ProtoMessage() {}
+
+func (x *CriuFeatures) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[11]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriuFeatures.ProtoReflect.Descriptor instead.
+func (*CriuFeatures) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{11}
+}
+
+func (x *CriuFeatures) GetMemTrack() bool {
+ if x != nil && x.MemTrack != nil {
+ return *x.MemTrack
+ }
+ return false
+}
+
+func (x *CriuFeatures) GetLazyPages() bool {
+ if x != nil && x.LazyPages != nil {
+ return *x.LazyPages
+ }
+ return false
+}
+
+func (x *CriuFeatures) GetPidfdStore() bool {
+ if x != nil && x.PidfdStore != nil {
+ return *x.PidfdStore
+ }
+ return false
+}
+
+type CriuReq struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Type *CriuReqType `protobuf:"varint,1,req,name=type,enum=CriuReqType" json:"type,omitempty"`
+ Opts *CriuOpts `protobuf:"bytes,2,opt,name=opts" json:"opts,omitempty"`
+ NotifySuccess *bool `protobuf:"varint,3,opt,name=notify_success,json=notifySuccess" json:"notify_success,omitempty"`
+ // When set service won't close the connection but
+ // will wait for more req-s to appear. Works not
+ // for all request types.
+ KeepOpen *bool `protobuf:"varint,4,opt,name=keep_open,json=keepOpen" json:"keep_open,omitempty"`
+ // 'features' can be used to query which features
+ // are supported by the installed criu/kernel
+ // via RPC.
+ Features *CriuFeatures `protobuf:"bytes,5,opt,name=features" json:"features,omitempty"`
+ // 'pid' is used for WAIT_PID
+ Pid *uint32 `protobuf:"varint,6,opt,name=pid" json:"pid,omitempty"`
+}
+
+func (x *CriuReq) Reset() {
+ *x = CriuReq{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[12]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CriuReq) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriuReq) ProtoMessage() {}
+
+func (x *CriuReq) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[12]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriuReq.ProtoReflect.Descriptor instead.
+func (*CriuReq) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *CriuReq) GetType() CriuReqType {
+ if x != nil && x.Type != nil {
+ return *x.Type
+ }
+ return CriuReqType_EMPTY
+}
+
+func (x *CriuReq) GetOpts() *CriuOpts {
+ if x != nil {
+ return x.Opts
+ }
+ return nil
+}
+
+func (x *CriuReq) GetNotifySuccess() bool {
+ if x != nil && x.NotifySuccess != nil {
+ return *x.NotifySuccess
+ }
+ return false
+}
+
+func (x *CriuReq) GetKeepOpen() bool {
+ if x != nil && x.KeepOpen != nil {
+ return *x.KeepOpen
+ }
+ return false
+}
+
+func (x *CriuReq) GetFeatures() *CriuFeatures {
+ if x != nil {
+ return x.Features
+ }
+ return nil
+}
+
+func (x *CriuReq) GetPid() uint32 {
+ if x != nil && x.Pid != nil {
+ return *x.Pid
+ }
+ return 0
+}
+
+type CriuResp struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Type *CriuReqType `protobuf:"varint,1,req,name=type,enum=CriuReqType" json:"type,omitempty"`
+ Success *bool `protobuf:"varint,2,req,name=success" json:"success,omitempty"`
+ Dump *CriuDumpResp `protobuf:"bytes,3,opt,name=dump" json:"dump,omitempty"`
+ Restore *CriuRestoreResp `protobuf:"bytes,4,opt,name=restore" json:"restore,omitempty"`
+ Notify *CriuNotify `protobuf:"bytes,5,opt,name=notify" json:"notify,omitempty"`
+ Ps *CriuPageServerInfo `protobuf:"bytes,6,opt,name=ps" json:"ps,omitempty"`
+ CrErrno *int32 `protobuf:"varint,7,opt,name=cr_errno,json=crErrno" json:"cr_errno,omitempty"`
+ Features *CriuFeatures `protobuf:"bytes,8,opt,name=features" json:"features,omitempty"`
+ CrErrmsg *string `protobuf:"bytes,9,opt,name=cr_errmsg,json=crErrmsg" json:"cr_errmsg,omitempty"`
+ Version *CriuVersion `protobuf:"bytes,10,opt,name=version" json:"version,omitempty"`
+ Status *int32 `protobuf:"varint,11,opt,name=status" json:"status,omitempty"`
+}
+
+func (x *CriuResp) Reset() {
+ *x = CriuResp{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[13]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CriuResp) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriuResp) ProtoMessage() {}
+
+func (x *CriuResp) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[13]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriuResp.ProtoReflect.Descriptor instead.
+func (*CriuResp) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{13}
+}
+
+func (x *CriuResp) GetType() CriuReqType {
+ if x != nil && x.Type != nil {
+ return *x.Type
+ }
+ return CriuReqType_EMPTY
+}
+
+func (x *CriuResp) GetSuccess() bool {
+ if x != nil && x.Success != nil {
+ return *x.Success
+ }
+ return false
+}
+
+func (x *CriuResp) GetDump() *CriuDumpResp {
+ if x != nil {
+ return x.Dump
+ }
+ return nil
+}
+
+func (x *CriuResp) GetRestore() *CriuRestoreResp {
+ if x != nil {
+ return x.Restore
+ }
+ return nil
+}
+
+func (x *CriuResp) GetNotify() *CriuNotify {
+ if x != nil {
+ return x.Notify
+ }
+ return nil
+}
+
+func (x *CriuResp) GetPs() *CriuPageServerInfo {
+ if x != nil {
+ return x.Ps
+ }
+ return nil
+}
+
+func (x *CriuResp) GetCrErrno() int32 {
+ if x != nil && x.CrErrno != nil {
+ return *x.CrErrno
+ }
+ return 0
+}
+
+func (x *CriuResp) GetFeatures() *CriuFeatures {
+ if x != nil {
+ return x.Features
+ }
+ return nil
+}
+
+func (x *CriuResp) GetCrErrmsg() string {
+ if x != nil && x.CrErrmsg != nil {
+ return *x.CrErrmsg
+ }
+ return ""
+}
+
+func (x *CriuResp) GetVersion() *CriuVersion {
+ if x != nil {
+ return x.Version
+ }
+ return nil
+}
+
+func (x *CriuResp) GetStatus() int32 {
+ if x != nil && x.Status != nil {
+ return *x.Status
+ }
+ return 0
+}
+
+// Answer for criu_req_type.VERSION requests
+type CriuVersion struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ MajorNumber *int32 `protobuf:"varint,1,req,name=major_number,json=majorNumber" json:"major_number,omitempty"`
+ MinorNumber *int32 `protobuf:"varint,2,req,name=minor_number,json=minorNumber" json:"minor_number,omitempty"`
+ Gitid *string `protobuf:"bytes,3,opt,name=gitid" json:"gitid,omitempty"`
+ Sublevel *int32 `protobuf:"varint,4,opt,name=sublevel" json:"sublevel,omitempty"`
+ Extra *int32 `protobuf:"varint,5,opt,name=extra" json:"extra,omitempty"`
+ Name *string `protobuf:"bytes,6,opt,name=name" json:"name,omitempty"`
+}
+
+func (x *CriuVersion) Reset() {
+ *x = CriuVersion{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rpc_rpc_proto_msgTypes[14]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CriuVersion) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriuVersion) ProtoMessage() {}
+
+func (x *CriuVersion) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_rpc_proto_msgTypes[14]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriuVersion.ProtoReflect.Descriptor instead.
+func (*CriuVersion) Descriptor() ([]byte, []int) {
+ return file_rpc_rpc_proto_rawDescGZIP(), []int{14}
+}
+
+func (x *CriuVersion) GetMajorNumber() int32 {
+ if x != nil && x.MajorNumber != nil {
+ return *x.MajorNumber
+ }
+ return 0
+}
+
+func (x *CriuVersion) GetMinorNumber() int32 {
+ if x != nil && x.MinorNumber != nil {
+ return *x.MinorNumber
+ }
+ return 0
+}
+
+func (x *CriuVersion) GetGitid() string {
+ if x != nil && x.Gitid != nil {
+ return *x.Gitid
+ }
+ return ""
+}
+
+func (x *CriuVersion) GetSublevel() int32 {
+ if x != nil && x.Sublevel != nil {
+ return *x.Sublevel
+ }
+ return 0
+}
+
+func (x *CriuVersion) GetExtra() int32 {
+ if x != nil && x.Extra != nil {
+ return *x.Extra
+ }
+ return 0
+}
+
+func (x *CriuVersion) GetName() string {
+ if x != nil && x.Name != nil {
+ return *x.Name
+ }
+ return ""
+}
+
+var File_rpc_rpc_proto protoreflect.FileDescriptor
+
+var file_rpc_rpc_proto_rawDesc = []byte{
+ 0x0a, 0x0d, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
+ 0x67, 0x0a, 0x15, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72,
+ 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65,
+ 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
+ 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x66, 0x64, 0x18, 0x04,
+ 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x66, 0x64, 0x22, 0x3c, 0x0a, 0x0e, 0x63, 0x72, 0x69, 0x75,
+ 0x5f, 0x76, 0x65, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x69, 0x72, 0x12, 0x13, 0x0a, 0x05, 0x69, 0x66,
+ 0x5f, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x69, 0x66, 0x49, 0x6e, 0x12,
+ 0x15, 0x0a, 0x06, 0x69, 0x66, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x02, 0x28, 0x09, 0x52,
+ 0x05, 0x69, 0x66, 0x4f, 0x75, 0x74, 0x22, 0x33, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x5f, 0x6d, 0x6f,
+ 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x61, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
+ 0x20, 0x02, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x76, 0x61, 0x6c,
+ 0x18, 0x02, 0x20, 0x02, 0x28, 0x09, 0x52, 0x03, 0x76, 0x61, 0x6c, 0x22, 0x56, 0x0a, 0x0e, 0x6a,
+ 0x6f, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x0e, 0x0a,
+ 0x02, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x02, 0x6e, 0x73, 0x12, 0x17, 0x0a,
+ 0x07, 0x6e, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x02, 0x28, 0x09, 0x52, 0x06,
+ 0x6e, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f,
+ 0x6f, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x74, 0x72, 0x61,
+ 0x4f, 0x70, 0x74, 0x22, 0x2e, 0x0a, 0x0a, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x5f, 0x66,
+ 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x03,
+ 0x6b, 0x65, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x66, 0x64, 0x18, 0x02, 0x20, 0x02, 0x28, 0x05, 0x52,
+ 0x02, 0x66, 0x64, 0x22, 0x35, 0x0a, 0x0b, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x72, 0x6f,
+ 0x6f, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x74, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x04, 0x63, 0x74, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02,
+ 0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x1f, 0x0a, 0x07, 0x75, 0x6e,
+ 0x69, 0x78, 0x5f, 0x73, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01,
+ 0x20, 0x02, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0xe4, 0x13, 0x0a, 0x09,
+ 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6f, 0x70, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0d, 0x69, 0x6d, 0x61,
+ 0x67, 0x65, 0x73, 0x5f, 0x64, 0x69, 0x72, 0x5f, 0x66, 0x64, 0x18, 0x01, 0x20, 0x02, 0x28, 0x05,
+ 0x3a, 0x02, 0x2d, 0x31, 0x52, 0x0b, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x44, 0x69, 0x72, 0x46,
+ 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x64, 0x69, 0x72, 0x18,
+ 0x44, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x44, 0x69, 0x72,
+ 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70,
+ 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x5f, 0x72, 0x75, 0x6e, 0x6e,
+ 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6c, 0x65, 0x61, 0x76, 0x65,
+ 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x5f, 0x75,
+ 0x6e, 0x69, 0x78, 0x5f, 0x73, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x78,
+ 0x74, 0x55, 0x6e, 0x69, 0x78, 0x53, 0x6b, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x63, 0x70, 0x5f, 0x65,
+ 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,
+ 0x52, 0x0e, 0x74, 0x63, 0x70, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64,
+ 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x76, 0x61, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x64, 0x65, 0x76, 0x69,
+ 0x63, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x76, 0x61, 0x73, 0x69,
+ 0x76, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x65,
+ 0x6c, 0x6c, 0x5f, 0x6a, 0x6f, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x68,
+ 0x65, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6c,
+ 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65,
+ 0x4c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x1e, 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76,
+ 0x65, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x3a, 0x01, 0x32, 0x52, 0x08, 0x6c, 0x6f, 0x67,
+ 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x6c,
+ 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65,
+ 0x12, 0x26, 0x0a, 0x02, 0x70, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63,
+ 0x72, 0x69, 0x75, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f,
+ 0x69, 0x6e, 0x66, 0x6f, 0x52, 0x02, 0x70, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69,
+ 0x66, 0x79, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08,
+ 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x12,
+ 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72,
+ 0x6f, 0x6f, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6d,
+ 0x67, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49,
+ 0x6d, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x6d, 0x65, 0x6d, 0x18,
+ 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x4d, 0x65, 0x6d, 0x12,
+ 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x64, 0x75, 0x70, 0x18, 0x10, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x65, 0x64, 0x75, 0x70, 0x12, 0x1e,
+ 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x64, 0x69, 0x72, 0x5f, 0x66, 0x64, 0x18, 0x11, 0x20,
+ 0x01, 0x28, 0x05, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x44, 0x69, 0x72, 0x46, 0x64, 0x12, 0x1d,
+ 0x0a, 0x0a, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x72, 0x65, 0x6d, 0x61, 0x70, 0x18, 0x12, 0x20, 0x01,
+ 0x28, 0x08, 0x52, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x6d, 0x61, 0x70, 0x12, 0x25, 0x0a,
+ 0x05, 0x76, 0x65, 0x74, 0x68, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63,
+ 0x72, 0x69, 0x75, 0x5f, 0x76, 0x65, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76,
+ 0x65, 0x74, 0x68, 0x73, 0x12, 0x23, 0x0a, 0x07, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x18,
+ 0x14, 0x20, 0x01, 0x28, 0x0d, 0x3a, 0x0a, 0x34, 0x32, 0x39, 0x34, 0x39, 0x36, 0x37, 0x32, 0x39,
+ 0x35, 0x52, 0x06, 0x63, 0x70, 0x75, 0x43, 0x61, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x6f, 0x72,
+ 0x63, 0x65, 0x5f, 0x69, 0x72, 0x6d, 0x61, 0x70, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
+ 0x66, 0x6f, 0x72, 0x63, 0x65, 0x49, 0x72, 0x6d, 0x61, 0x70, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x78,
+ 0x65, 0x63, 0x5f, 0x63, 0x6d, 0x64, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x65, 0x78,
+ 0x65, 0x63, 0x43, 0x6d, 0x64, 0x12, 0x27, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x5f, 0x6d, 0x6e, 0x74,
+ 0x18, 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x78, 0x74, 0x5f, 0x6d, 0x6f, 0x75,
+ 0x6e, 0x74, 0x5f, 0x6d, 0x61, 0x70, 0x52, 0x06, 0x65, 0x78, 0x74, 0x4d, 0x6e, 0x74, 0x12, 0x25,
+ 0x0a, 0x0e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73,
+ 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x43, 0x67,
+ 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x25, 0x0a, 0x07, 0x63, 0x67, 0x5f, 0x72, 0x6f, 0x6f, 0x74,
+ 0x18, 0x19, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f,
+ 0x72, 0x6f, 0x6f, 0x74, 0x52, 0x06, 0x63, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x0b,
+ 0x72, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x62, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x1a, 0x20, 0x01, 0x28,
+ 0x08, 0x52, 0x0a, 0x72, 0x73, 0x74, 0x53, 0x69, 0x62, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x2a, 0x0a,
+ 0x0a, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x5f, 0x66, 0x64, 0x18, 0x1b, 0x20, 0x03, 0x28,
+ 0x0b, 0x32, 0x0b, 0x2e, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x5f, 0x66, 0x64, 0x52, 0x09,
+ 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x46, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x75, 0x74,
+ 0x6f, 0x5f, 0x65, 0x78, 0x74, 0x5f, 0x6d, 0x6e, 0x74, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x45, 0x78, 0x74, 0x4d, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65,
+ 0x78, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x08,
+ 0x52, 0x0a, 0x65, 0x78, 0x74, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b,
+ 0x65, 0x78, 0x74, 0x5f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x1e, 0x20, 0x01, 0x28,
+ 0x08, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x19, 0x0a,
+ 0x08, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x6d, 0x6e, 0x74, 0x18, 0x1f, 0x20, 0x03, 0x28, 0x09, 0x52,
+ 0x07, 0x73, 0x6b, 0x69, 0x70, 0x4d, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x61, 0x62,
+ 0x6c, 0x65, 0x5f, 0x66, 0x73, 0x18, 0x20, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x61,
+ 0x62, 0x6c, 0x65, 0x46, 0x73, 0x12, 0x28, 0x0a, 0x0b, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x73, 0x6b,
+ 0x5f, 0x69, 0x6e, 0x6f, 0x18, 0x21, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x75, 0x6e, 0x69,
+ 0x78, 0x5f, 0x73, 0x6b, 0x52, 0x09, 0x75, 0x6e, 0x69, 0x78, 0x53, 0x6b, 0x49, 0x6e, 0x6f, 0x12,
+ 0x3d, 0x0a, 0x13, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70,
+ 0x73, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x63,
+ 0x72, 0x69, 0x75, 0x5f, 0x63, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x52, 0x11, 0x6d, 0x61, 0x6e,
+ 0x61, 0x67, 0x65, 0x43, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x28,
+ 0x0a, 0x0b, 0x67, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x23, 0x20,
+ 0x01, 0x28, 0x0d, 0x3a, 0x07, 0x31, 0x30, 0x34, 0x38, 0x35, 0x37, 0x36, 0x52, 0x0a, 0x67, 0x68,
+ 0x6f, 0x73, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x72, 0x6d, 0x61,
+ 0x70, 0x5f, 0x73, 0x63, 0x61, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x24, 0x20, 0x03,
+ 0x28, 0x09, 0x52, 0x0e, 0x69, 0x72, 0x6d, 0x61, 0x70, 0x53, 0x63, 0x61, 0x6e, 0x50, 0x61, 0x74,
+ 0x68, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x25,
+ 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x19,
+ 0x0a, 0x08, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x6e, 0x73, 0x18, 0x26, 0x20, 0x01, 0x28, 0x0d,
+ 0x52, 0x07, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x4e, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x6a, 0x6f, 0x69,
+ 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x27, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6a, 0x6f, 0x69,
+ 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x06, 0x6a, 0x6f, 0x69,
+ 0x6e, 0x4e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x72,
+ 0x6f, 0x70, 0x73, 0x18, 0x29, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x67, 0x72, 0x6f, 0x75,
+ 0x70, 0x50, 0x72, 0x6f, 0x70, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70,
+ 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x2a, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x0f, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x70, 0x73, 0x46, 0x69,
+ 0x6c, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x64, 0x75, 0x6d,
+ 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x18, 0x2b, 0x20, 0x03,
+ 0x28, 0x09, 0x52, 0x14, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x75, 0x6d, 0x70, 0x43, 0x6f,
+ 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x72, 0x65, 0x65,
+ 0x7a, 0x65, 0x5f, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x0c, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x43, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x18, 0x0a,
+ 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
+ 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x2b, 0x0a, 0x12, 0x74, 0x63, 0x70, 0x5f, 0x73,
+ 0x6b, 0x69, 0x70, 0x5f, 0x69, 0x6e, 0x5f, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x18, 0x2e, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x0f, 0x74, 0x63, 0x70, 0x53, 0x6b, 0x69, 0x70, 0x49, 0x6e, 0x46, 0x6c,
+ 0x69, 0x67, 0x68, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x73, 0x79, 0x73,
+ 0x63, 0x74, 0x6c, 0x73, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x77, 0x65, 0x61, 0x6b,
+ 0x53, 0x79, 0x73, 0x63, 0x74, 0x6c, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x61, 0x7a, 0x79, 0x5f,
+ 0x70, 0x61, 0x67, 0x65, 0x73, 0x18, 0x30, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6c, 0x61, 0x7a,
+ 0x79, 0x50, 0x61, 0x67, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
+ 0x5f, 0x66, 0x64, 0x18, 0x31, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x73, 0x74, 0x61, 0x74, 0x75,
+ 0x73, 0x46, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x74,
+ 0x73, 0x5f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x18, 0x32, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f,
+ 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x50, 0x74, 0x73, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x12,
+ 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x33,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x69, 0x6c, 0x65,
+ 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x63, 0x70, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x18, 0x34, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x63, 0x70, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x1f, 0x0a,
+ 0x0b, 0x6c, 0x73, 0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x35, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x0a, 0x6c, 0x73, 0x6d, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x1d,
+ 0x0a, 0x0a, 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x18, 0x36, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x09, 0x74, 0x6c, 0x73, 0x43, 0x61, 0x63, 0x65, 0x72, 0x74, 0x12, 0x1b, 0x0a,
+ 0x09, 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x61, 0x63, 0x72, 0x6c, 0x18, 0x37, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x08, 0x74, 0x6c, 0x73, 0x43, 0x61, 0x63, 0x72, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x6c,
+ 0x73, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x38, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x6c,
+ 0x73, 0x43, 0x65, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x6c, 0x73, 0x5f, 0x6b, 0x65, 0x79,
+ 0x18, 0x39, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x6c, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x10,
+ 0x0a, 0x03, 0x74, 0x6c, 0x73, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x74, 0x6c, 0x73,
+ 0x12, 0x27, 0x0a, 0x10, 0x74, 0x6c, 0x73, 0x5f, 0x6e, 0x6f, 0x5f, 0x63, 0x6e, 0x5f, 0x76, 0x65,
+ 0x72, 0x69, 0x66, 0x79, 0x18, 0x3b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x74, 0x6c, 0x73, 0x4e,
+ 0x6f, 0x43, 0x6e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x67, 0x72,
+ 0x6f, 0x75, 0x70, 0x5f, 0x79, 0x61, 0x72, 0x64, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
+ 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x59, 0x61, 0x72, 0x64, 0x12, 0x3f, 0x0a, 0x0d, 0x70, 0x72,
+ 0x65, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x3d, 0x20, 0x01, 0x28,
+ 0x0e, 0x32, 0x13, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x70, 0x72, 0x65, 0x5f, 0x64, 0x75, 0x6d,
+ 0x70, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x3a, 0x06, 0x53, 0x50, 0x4c, 0x49, 0x43, 0x45, 0x52, 0x0b,
+ 0x70, 0x72, 0x65, 0x44, 0x75, 0x6d, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70,
+ 0x69, 0x64, 0x66, 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x73, 0x6b, 0x18, 0x3e, 0x20,
+ 0x01, 0x28, 0x05, 0x52, 0x0c, 0x70, 0x69, 0x64, 0x66, 0x64, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53,
+ 0x6b, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x73, 0x6d, 0x5f, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63,
+ 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x3f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x73,
+ 0x6d, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x46, 0x0a,
+ 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x40, 0x20,
+ 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f,
+ 0x72, 0x6b, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x3a, 0x08,
+ 0x49, 0x50, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x53, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72,
+ 0x6b, 0x4c, 0x6f, 0x63, 0x6b, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x6e, 0x74, 0x6e, 0x73, 0x5f, 0x63,
+ 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x41, 0x20, 0x01, 0x28, 0x08,
+ 0x52, 0x0f, 0x6d, 0x6e, 0x74, 0x6e, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x4d, 0x6f, 0x64,
+ 0x65, 0x12, 0x2d, 0x0a, 0x13, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x72,
+ 0x77, 0x78, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x42, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10,
+ 0x73, 0x6b, 0x69, 0x70, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x77, 0x78, 0x43, 0x68, 0x65, 0x63, 0x6b,
+ 0x12, 0x22, 0x0a, 0x0c, 0x75, 0x6e, 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x64,
+ 0x18, 0x43, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x6e, 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c,
+ 0x65, 0x67, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x74,
+ 0x6f, 0x70, 0x70, 0x65, 0x64, 0x18, 0x45, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6c, 0x65, 0x61,
+ 0x76, 0x65, 0x53, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x69, 0x73,
+ 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x46, 0x20, 0x01, 0x28, 0x08,
+ 0x52, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x22,
+ 0x0a, 0x0d, 0x6c, 0x6f, 0x67, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x74, 0x64, 0x65, 0x72, 0x72, 0x18,
+ 0x47, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6c, 0x6f, 0x67, 0x54, 0x6f, 0x53, 0x74, 0x64, 0x65,
+ 0x72, 0x72, 0x22, 0x2c, 0x0a, 0x0e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f,
+ 0x72, 0x65, 0x73, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64,
+ 0x22, 0x25, 0x0a, 0x11, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65,
+ 0x5f, 0x72, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x02,
+ 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x37, 0x0a, 0x0b, 0x63, 0x72, 0x69, 0x75, 0x5f,
+ 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x10,
+ 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64,
+ 0x22, 0x6c, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65,
+ 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x12, 0x1d,
+ 0x0a, 0x0a, 0x6c, 0x61, 0x7a, 0x79, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x08, 0x52, 0x09, 0x6c, 0x61, 0x7a, 0x79, 0x50, 0x61, 0x67, 0x65, 0x73, 0x12, 0x1f, 0x0a,
+ 0x0b, 0x70, 0x69, 0x64, 0x66, 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x08, 0x52, 0x0a, 0x70, 0x69, 0x64, 0x66, 0x64, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0xd0,
+ 0x01, 0x0a, 0x08, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x12, 0x22, 0x0a, 0x04, 0x74,
+ 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75,
+ 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12,
+ 0x1e, 0x0a, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e,
+ 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6f, 0x70, 0x74, 0x73, 0x52, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x12,
+ 0x25, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73,
+ 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53,
+ 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6b, 0x65, 0x65, 0x70, 0x4f,
+ 0x70, 0x65, 0x6e, 0x12, 0x2a, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18,
+ 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x66, 0x65, 0x61,
+ 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12,
+ 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69,
+ 0x64, 0x22, 0x8f, 0x03, 0x0a, 0x09, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x12,
+ 0x22, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28, 0x0e, 0x32, 0x0e, 0x2e,
+ 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74,
+ 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02,
+ 0x20, 0x02, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a,
+ 0x04, 0x64, 0x75, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x72,
+ 0x69, 0x75, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x52, 0x04, 0x64, 0x75,
+ 0x6d, 0x70, 0x12, 0x2c, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f,
+ 0x72, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x52, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65,
+ 0x12, 0x24, 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x0c, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x06,
+ 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x26, 0x0a, 0x02, 0x70, 0x73, 0x18, 0x06, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x52, 0x02, 0x70, 0x73, 0x12, 0x19,
+ 0x0a, 0x08, 0x63, 0x72, 0x5f, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05,
+ 0x52, 0x07, 0x63, 0x72, 0x45, 0x72, 0x72, 0x6e, 0x6f, 0x12, 0x2a, 0x0a, 0x08, 0x66, 0x65, 0x61,
+ 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x72,
+ 0x69, 0x75, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x08, 0x66, 0x65, 0x61,
+ 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x72, 0x5f, 0x65, 0x72, 0x72, 0x6d,
+ 0x73, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x72, 0x45, 0x72, 0x72, 0x6d,
+ 0x73, 0x67, 0x12, 0x27, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69,
+ 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73,
+ 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61,
+ 0x74, 0x75, 0x73, 0x22, 0xb0, 0x01, 0x0a, 0x0c, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x76, 0x65, 0x72,
+ 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x6e, 0x75,
+ 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x02, 0x28, 0x05, 0x52, 0x0b, 0x6d, 0x61, 0x6a, 0x6f,
+ 0x72, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x6f, 0x72,
+ 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x02, 0x28, 0x05, 0x52, 0x0b, 0x6d,
+ 0x69, 0x6e, 0x6f, 0x72, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x69,
+ 0x74, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x69, 0x74, 0x69, 0x64,
+ 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01,
+ 0x28, 0x05, 0x52, 0x08, 0x73, 0x75, 0x62, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05,
+ 0x65, 0x78, 0x74, 0x72, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x65, 0x78, 0x74,
+ 0x72, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x5f, 0x0a, 0x0c, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x63,
+ 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x49, 0x47, 0x4e, 0x4f, 0x52, 0x45,
+ 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x47, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12,
+ 0x09, 0x0a, 0x05, 0x50, 0x52, 0x4f, 0x50, 0x53, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x4f,
+ 0x46, 0x54, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x55, 0x4c, 0x4c, 0x10, 0x04, 0x12, 0x0a,
+ 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x43, 0x54, 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45,
+ 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x06, 0x2a, 0x40, 0x0a, 0x18, 0x63, 0x72, 0x69, 0x75, 0x5f,
+ 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6d, 0x65, 0x74,
+ 0x68, 0x6f, 0x64, 0x12, 0x0c, 0x0a, 0x08, 0x49, 0x50, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x53, 0x10,
+ 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x46, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x53, 0x10, 0x02, 0x12,
+ 0x08, 0x0a, 0x04, 0x53, 0x4b, 0x49, 0x50, 0x10, 0x03, 0x2a, 0x2d, 0x0a, 0x12, 0x63, 0x72, 0x69,
+ 0x75, 0x5f, 0x70, 0x72, 0x65, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x12,
+ 0x0a, 0x0a, 0x06, 0x53, 0x50, 0x4c, 0x49, 0x43, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x56,
+ 0x4d, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x2a, 0xe5, 0x01, 0x0a, 0x0d, 0x63, 0x72, 0x69,
+ 0x75, 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x4d,
+ 0x50, 0x54, 0x59, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x01, 0x12,
+ 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x53, 0x54, 0x4f, 0x52, 0x45, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05,
+ 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, 0x45, 0x5f, 0x44,
+ 0x55, 0x4d, 0x50, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x53, 0x45,
+ 0x52, 0x56, 0x45, 0x52, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x59,
+ 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x50, 0x55, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x44, 0x55,
+ 0x4d, 0x50, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x50, 0x55, 0x49, 0x4e, 0x46, 0x4f, 0x5f,
+ 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x46, 0x45, 0x41, 0x54, 0x55,
+ 0x52, 0x45, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x09, 0x12, 0x0b, 0x0a, 0x07, 0x56, 0x45,
+ 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x0a, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x41, 0x49, 0x54, 0x5f,
+ 0x50, 0x49, 0x44, 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x53, 0x45,
+ 0x52, 0x56, 0x45, 0x52, 0x5f, 0x43, 0x48, 0x4c, 0x44, 0x10, 0x0c, 0x12, 0x13, 0x0a, 0x0f, 0x53,
+ 0x49, 0x4e, 0x47, 0x4c, 0x45, 0x5f, 0x50, 0x52, 0x45, 0x5f, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x0d,
+}
+
+var (
+ file_rpc_rpc_proto_rawDescOnce sync.Once
+ file_rpc_rpc_proto_rawDescData = file_rpc_rpc_proto_rawDesc
+)
+
+func file_rpc_rpc_proto_rawDescGZIP() []byte {
+ file_rpc_rpc_proto_rawDescOnce.Do(func() {
+ file_rpc_rpc_proto_rawDescData = protoimpl.X.CompressGZIP(file_rpc_rpc_proto_rawDescData)
+ })
+ return file_rpc_rpc_proto_rawDescData
+}
+
+var file_rpc_rpc_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
+var file_rpc_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
+var file_rpc_rpc_proto_goTypes = []interface{}{
+ (CriuCgMode)(0), // 0: criu_cg_mode
+ (CriuNetworkLockMethod)(0), // 1: criu_network_lock_method
+ (CriuPreDumpMode)(0), // 2: criu_pre_dump_mode
+ (CriuReqType)(0), // 3: criu_req_type
+ (*CriuPageServerInfo)(nil), // 4: criu_page_server_info
+ (*CriuVethPair)(nil), // 5: criu_veth_pair
+ (*ExtMountMap)(nil), // 6: ext_mount_map
+ (*JoinNamespace)(nil), // 7: join_namespace
+ (*InheritFd)(nil), // 8: inherit_fd
+ (*CgroupRoot)(nil), // 9: cgroup_root
+ (*UnixSk)(nil), // 10: unix_sk
+ (*CriuOpts)(nil), // 11: criu_opts
+ (*CriuDumpResp)(nil), // 12: criu_dump_resp
+ (*CriuRestoreResp)(nil), // 13: criu_restore_resp
+ (*CriuNotify)(nil), // 14: criu_notify
+ (*CriuFeatures)(nil), // 15: criu_features
+ (*CriuReq)(nil), // 16: criu_req
+ (*CriuResp)(nil), // 17: criu_resp
+ (*CriuVersion)(nil), // 18: criu_version
+}
+var file_rpc_rpc_proto_depIdxs = []int32{
+ 4, // 0: criu_opts.ps:type_name -> criu_page_server_info
+ 5, // 1: criu_opts.veths:type_name -> criu_veth_pair
+ 6, // 2: criu_opts.ext_mnt:type_name -> ext_mount_map
+ 9, // 3: criu_opts.cg_root:type_name -> cgroup_root
+ 8, // 4: criu_opts.inherit_fd:type_name -> inherit_fd
+ 10, // 5: criu_opts.unix_sk_ino:type_name -> unix_sk
+ 0, // 6: criu_opts.manage_cgroups_mode:type_name -> criu_cg_mode
+ 7, // 7: criu_opts.join_ns:type_name -> join_namespace
+ 2, // 8: criu_opts.pre_dump_mode:type_name -> criu_pre_dump_mode
+ 1, // 9: criu_opts.network_lock:type_name -> criu_network_lock_method
+ 3, // 10: criu_req.type:type_name -> criu_req_type
+ 11, // 11: criu_req.opts:type_name -> criu_opts
+ 15, // 12: criu_req.features:type_name -> criu_features
+ 3, // 13: criu_resp.type:type_name -> criu_req_type
+ 12, // 14: criu_resp.dump:type_name -> criu_dump_resp
+ 13, // 15: criu_resp.restore:type_name -> criu_restore_resp
+ 14, // 16: criu_resp.notify:type_name -> criu_notify
+ 4, // 17: criu_resp.ps:type_name -> criu_page_server_info
+ 15, // 18: criu_resp.features:type_name -> criu_features
+ 18, // 19: criu_resp.version:type_name -> criu_version
+ 20, // [20:20] is the sub-list for method output_type
+ 20, // [20:20] is the sub-list for method input_type
+ 20, // [20:20] is the sub-list for extension type_name
+ 20, // [20:20] is the sub-list for extension extendee
+ 0, // [0:20] is the sub-list for field type_name
+}
+
+func init() { file_rpc_rpc_proto_init() }
+func file_rpc_rpc_proto_init() {
+ if File_rpc_rpc_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_rpc_rpc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CriuPageServerInfo); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CriuVethPair); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*ExtMountMap); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*JoinNamespace); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*InheritFd); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CgroupRoot); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*UnixSk); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CriuOpts); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CriuDumpResp); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CriuRestoreResp); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CriuNotify); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CriuFeatures); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CriuReq); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CriuResp); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_rpc_rpc_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CriuVersion); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_rpc_rpc_proto_rawDesc,
+ NumEnums: 4,
+ NumMessages: 15,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_rpc_rpc_proto_goTypes,
+ DependencyIndexes: file_rpc_rpc_proto_depIdxs,
+ EnumInfos: file_rpc_rpc_proto_enumTypes,
+ MessageInfos: file_rpc_rpc_proto_msgTypes,
+ }.Build()
+ File_rpc_rpc_proto = out.File
+ file_rpc_rpc_proto_rawDesc = nil
+ file_rpc_rpc_proto_goTypes = nil
+ file_rpc_rpc_proto_depIdxs = nil
+}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/rpc/rpc.proto b/vendor/github.com/checkpoint-restore/go-criu/v7/rpc/rpc.proto
new file mode 100644
index 00000000..1a4722a9
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/rpc/rpc.proto
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: MIT
+
+syntax = "proto2";
+
+message criu_page_server_info {
+ optional string address = 1;
+ optional int32 port = 2;
+ optional int32 pid = 3;
+ optional int32 fd = 4;
+}
+
+message criu_veth_pair {
+ required string if_in = 1;
+ required string if_out = 2;
+};
+
+message ext_mount_map {
+ required string key = 1;
+ required string val = 2;
+};
+
+message join_namespace {
+ required string ns = 1;
+ required string ns_file = 2;
+ optional string extra_opt = 3;
+}
+
+message inherit_fd {
+ required string key = 1;
+ required int32 fd = 2;
+};
+
+message cgroup_root {
+ optional string ctrl = 1;
+ required string path = 2;
+};
+
+message unix_sk {
+ required uint32 inode = 1;
+};
+
+enum criu_cg_mode {
+ IGNORE = 0;
+ CG_NONE = 1;
+ PROPS = 2;
+ SOFT = 3;
+ FULL = 4;
+ STRICT = 5;
+ DEFAULT = 6;
+};
+
+enum criu_network_lock_method {
+ IPTABLES = 1;
+ NFTABLES = 2;
+ SKIP = 3;
+};
+
+enum criu_pre_dump_mode {
+ SPLICE = 1;
+ VM_READ = 2;
+};
+
+message criu_opts {
+ required int32 images_dir_fd = 1 [default = -1];
+ optional string images_dir = 68; /* used only if images_dir_fd == -1 */
+ optional int32 pid = 2; /* if not set on dump, will dump requesting process */
+
+ optional bool leave_running = 3;
+ optional bool ext_unix_sk = 4;
+ optional bool tcp_established = 5;
+ optional bool evasive_devices = 6;
+ optional bool shell_job = 7;
+ optional bool file_locks = 8;
+ optional int32 log_level = 9 [default = 2];
+ optional string log_file = 10; /* No subdirs are allowed. Consider using work-dir */
+
+ optional criu_page_server_info ps = 11;
+
+ optional bool notify_scripts = 12;
+
+ optional string root = 13;
+ optional string parent_img = 14;
+ optional bool track_mem = 15;
+ optional bool auto_dedup = 16;
+
+ optional int32 work_dir_fd = 17;
+ optional bool link_remap = 18;
+ repeated criu_veth_pair veths = 19; /* DEPRECATED, use external instead */
+
+ optional uint32 cpu_cap = 20 [default = 0xffffffff];
+ optional bool force_irmap = 21;
+ repeated string exec_cmd = 22;
+
+ repeated ext_mount_map ext_mnt = 23; /* DEPRECATED, use external instead */
+ optional bool manage_cgroups = 24; /* backward compatibility */
+ repeated cgroup_root cg_root = 25;
+
+ optional bool rst_sibling = 26; /* swrk only */
+ repeated inherit_fd inherit_fd = 27; /* swrk only */
+
+ optional bool auto_ext_mnt = 28;
+ optional bool ext_sharing = 29;
+ optional bool ext_masters = 30;
+
+ repeated string skip_mnt = 31;
+ repeated string enable_fs = 32;
+
+ repeated unix_sk unix_sk_ino = 33; /* DEPRECATED, use external instead */
+
+ optional criu_cg_mode manage_cgroups_mode = 34;
+ optional uint32 ghost_limit = 35 [default = 0x100000];
+ repeated string irmap_scan_paths = 36;
+ repeated string external = 37;
+ optional uint32 empty_ns = 38;
+ repeated join_namespace join_ns = 39;
+
+ optional string cgroup_props = 41;
+ optional string cgroup_props_file = 42;
+ repeated string cgroup_dump_controller = 43;
+
+ optional string freeze_cgroup = 44;
+ optional uint32 timeout = 45;
+ optional bool tcp_skip_in_flight = 46;
+ optional bool weak_sysctls = 47;
+ optional bool lazy_pages = 48;
+ optional int32 status_fd = 49;
+ optional bool orphan_pts_master = 50;
+ optional string config_file = 51;
+ optional bool tcp_close = 52;
+ optional string lsm_profile = 53;
+ optional string tls_cacert = 54;
+ optional string tls_cacrl = 55;
+ optional string tls_cert = 56;
+ optional string tls_key = 57;
+ optional bool tls = 58;
+ optional bool tls_no_cn_verify = 59;
+ optional string cgroup_yard = 60;
+ optional criu_pre_dump_mode pre_dump_mode = 61 [default = SPLICE];
+ optional int32 pidfd_store_sk = 62;
+ optional string lsm_mount_context = 63;
+ optional criu_network_lock_method network_lock = 64 [default = IPTABLES];
+ optional bool mntns_compat_mode = 65;
+ optional bool skip_file_rwx_check = 66;
+ optional bool unprivileged = 67;
+ optional bool leave_stopped = 69;
+ optional bool display_stats = 70;
+ optional bool log_to_stderr = 71;
+/* optional bool check_mounts = 128; */
+}
+
+message criu_dump_resp {
+ optional bool restored = 1;
+}
+
+message criu_restore_resp {
+ required int32 pid = 1;
+}
+
+message criu_notify {
+ optional string script = 1;
+ optional int32 pid = 2;
+}
+
+enum criu_req_type {
+ EMPTY = 0;
+ DUMP = 1;
+ RESTORE = 2;
+ CHECK = 3;
+ PRE_DUMP = 4;
+ PAGE_SERVER = 5;
+
+ NOTIFY = 6;
+
+ CPUINFO_DUMP = 7;
+ CPUINFO_CHECK = 8;
+
+ FEATURE_CHECK = 9;
+
+ VERSION = 10;
+
+ WAIT_PID = 11;
+ PAGE_SERVER_CHLD = 12;
+
+ SINGLE_PRE_DUMP = 13;
+}
+
+/*
+ * List of features which can queried via
+ * CRIU_REQ_TYPE__FEATURE_CHECK
+ */
+message criu_features {
+ optional bool mem_track = 1;
+ optional bool lazy_pages = 2;
+ optional bool pidfd_store = 3;
+}
+
+/*
+ * Request -- each type corresponds to must-be-there
+ * request arguments of respective type
+ */
+
+message criu_req {
+ required criu_req_type type = 1;
+
+ optional criu_opts opts = 2;
+ optional bool notify_success = 3;
+
+ /*
+ * When set service won't close the connection but
+ * will wait for more req-s to appear. Works not
+ * for all request types.
+ */
+ optional bool keep_open = 4;
+ /*
+ * 'features' can be used to query which features
+ * are supported by the installed criu/kernel
+ * via RPC.
+ */
+ optional criu_features features = 5;
+
+ /* 'pid' is used for WAIT_PID */
+ optional uint32 pid = 6;
+}
+
+/*
+ * Response -- it states whether the request was served
+ * and additional request-specific information
+ */
+
+message criu_resp {
+ required criu_req_type type = 1;
+ required bool success = 2;
+
+ optional criu_dump_resp dump = 3;
+ optional criu_restore_resp restore = 4;
+ optional criu_notify notify = 5;
+ optional criu_page_server_info ps = 6;
+
+ optional int32 cr_errno = 7;
+ optional criu_features features = 8;
+ optional string cr_errmsg = 9;
+ optional criu_version version = 10;
+
+ optional int32 status = 11;
+}
+
+/* Answer for criu_req_type.VERSION requests */
+message criu_version {
+ required int32 major_number = 1;
+ required int32 minor_number = 2;
+ optional string gitid = 3;
+ optional int32 sublevel = 4;
+ optional int32 extra = 5;
+ optional string name = 6;
+}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/stats/stats.pb.go b/vendor/github.com/checkpoint-restore/go-criu/v7/stats/stats.pb.go
new file mode 100644
index 00000000..ffec3809
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/stats/stats.pb.go
@@ -0,0 +1,462 @@
+// SPDX-License-Identifier: MIT
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.30.0
+// protoc v4.23.4
+// source: stats/stats.proto
+
+package stats
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// This one contains statistics about dump/restore process
+type DumpStatsEntry struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ FreezingTime *uint32 `protobuf:"varint,1,req,name=freezing_time,json=freezingTime" json:"freezing_time,omitempty"`
+ FrozenTime *uint32 `protobuf:"varint,2,req,name=frozen_time,json=frozenTime" json:"frozen_time,omitempty"`
+ MemdumpTime *uint32 `protobuf:"varint,3,req,name=memdump_time,json=memdumpTime" json:"memdump_time,omitempty"`
+ MemwriteTime *uint32 `protobuf:"varint,4,req,name=memwrite_time,json=memwriteTime" json:"memwrite_time,omitempty"`
+ PagesScanned *uint64 `protobuf:"varint,5,req,name=pages_scanned,json=pagesScanned" json:"pages_scanned,omitempty"`
+ PagesSkippedParent *uint64 `protobuf:"varint,6,req,name=pages_skipped_parent,json=pagesSkippedParent" json:"pages_skipped_parent,omitempty"`
+ PagesWritten *uint64 `protobuf:"varint,7,req,name=pages_written,json=pagesWritten" json:"pages_written,omitempty"`
+ IrmapResolve *uint32 `protobuf:"varint,8,opt,name=irmap_resolve,json=irmapResolve" json:"irmap_resolve,omitempty"`
+ PagesLazy *uint64 `protobuf:"varint,9,req,name=pages_lazy,json=pagesLazy" json:"pages_lazy,omitempty"`
+ PagePipes *uint64 `protobuf:"varint,10,opt,name=page_pipes,json=pagePipes" json:"page_pipes,omitempty"`
+ PagePipeBufs *uint64 `protobuf:"varint,11,opt,name=page_pipe_bufs,json=pagePipeBufs" json:"page_pipe_bufs,omitempty"`
+ ShpagesScanned *uint64 `protobuf:"varint,12,opt,name=shpages_scanned,json=shpagesScanned" json:"shpages_scanned,omitempty"`
+ ShpagesSkippedParent *uint64 `protobuf:"varint,13,opt,name=shpages_skipped_parent,json=shpagesSkippedParent" json:"shpages_skipped_parent,omitempty"`
+ ShpagesWritten *uint64 `protobuf:"varint,14,opt,name=shpages_written,json=shpagesWritten" json:"shpages_written,omitempty"`
+}
+
+func (x *DumpStatsEntry) Reset() {
+ *x = DumpStatsEntry{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_stats_stats_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *DumpStatsEntry) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DumpStatsEntry) ProtoMessage() {}
+
+func (x *DumpStatsEntry) ProtoReflect() protoreflect.Message {
+ mi := &file_stats_stats_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DumpStatsEntry.ProtoReflect.Descriptor instead.
+func (*DumpStatsEntry) Descriptor() ([]byte, []int) {
+ return file_stats_stats_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *DumpStatsEntry) GetFreezingTime() uint32 {
+ if x != nil && x.FreezingTime != nil {
+ return *x.FreezingTime
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetFrozenTime() uint32 {
+ if x != nil && x.FrozenTime != nil {
+ return *x.FrozenTime
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetMemdumpTime() uint32 {
+ if x != nil && x.MemdumpTime != nil {
+ return *x.MemdumpTime
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetMemwriteTime() uint32 {
+ if x != nil && x.MemwriteTime != nil {
+ return *x.MemwriteTime
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetPagesScanned() uint64 {
+ if x != nil && x.PagesScanned != nil {
+ return *x.PagesScanned
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetPagesSkippedParent() uint64 {
+ if x != nil && x.PagesSkippedParent != nil {
+ return *x.PagesSkippedParent
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetPagesWritten() uint64 {
+ if x != nil && x.PagesWritten != nil {
+ return *x.PagesWritten
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetIrmapResolve() uint32 {
+ if x != nil && x.IrmapResolve != nil {
+ return *x.IrmapResolve
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetPagesLazy() uint64 {
+ if x != nil && x.PagesLazy != nil {
+ return *x.PagesLazy
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetPagePipes() uint64 {
+ if x != nil && x.PagePipes != nil {
+ return *x.PagePipes
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetPagePipeBufs() uint64 {
+ if x != nil && x.PagePipeBufs != nil {
+ return *x.PagePipeBufs
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetShpagesScanned() uint64 {
+ if x != nil && x.ShpagesScanned != nil {
+ return *x.ShpagesScanned
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetShpagesSkippedParent() uint64 {
+ if x != nil && x.ShpagesSkippedParent != nil {
+ return *x.ShpagesSkippedParent
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetShpagesWritten() uint64 {
+ if x != nil && x.ShpagesWritten != nil {
+ return *x.ShpagesWritten
+ }
+ return 0
+}
+
+type RestoreStatsEntry struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ PagesCompared *uint64 `protobuf:"varint,1,req,name=pages_compared,json=pagesCompared" json:"pages_compared,omitempty"`
+ PagesSkippedCow *uint64 `protobuf:"varint,2,req,name=pages_skipped_cow,json=pagesSkippedCow" json:"pages_skipped_cow,omitempty"`
+ ForkingTime *uint32 `protobuf:"varint,3,req,name=forking_time,json=forkingTime" json:"forking_time,omitempty"`
+ RestoreTime *uint32 `protobuf:"varint,4,req,name=restore_time,json=restoreTime" json:"restore_time,omitempty"`
+ PagesRestored *uint64 `protobuf:"varint,5,opt,name=pages_restored,json=pagesRestored" json:"pages_restored,omitempty"`
+}
+
+func (x *RestoreStatsEntry) Reset() {
+ *x = RestoreStatsEntry{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_stats_stats_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *RestoreStatsEntry) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RestoreStatsEntry) ProtoMessage() {}
+
+func (x *RestoreStatsEntry) ProtoReflect() protoreflect.Message {
+ mi := &file_stats_stats_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use RestoreStatsEntry.ProtoReflect.Descriptor instead.
+func (*RestoreStatsEntry) Descriptor() ([]byte, []int) {
+ return file_stats_stats_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *RestoreStatsEntry) GetPagesCompared() uint64 {
+ if x != nil && x.PagesCompared != nil {
+ return *x.PagesCompared
+ }
+ return 0
+}
+
+func (x *RestoreStatsEntry) GetPagesSkippedCow() uint64 {
+ if x != nil && x.PagesSkippedCow != nil {
+ return *x.PagesSkippedCow
+ }
+ return 0
+}
+
+func (x *RestoreStatsEntry) GetForkingTime() uint32 {
+ if x != nil && x.ForkingTime != nil {
+ return *x.ForkingTime
+ }
+ return 0
+}
+
+func (x *RestoreStatsEntry) GetRestoreTime() uint32 {
+ if x != nil && x.RestoreTime != nil {
+ return *x.RestoreTime
+ }
+ return 0
+}
+
+func (x *RestoreStatsEntry) GetPagesRestored() uint64 {
+ if x != nil && x.PagesRestored != nil {
+ return *x.PagesRestored
+ }
+ return 0
+}
+
+type StatsEntry struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Dump *DumpStatsEntry `protobuf:"bytes,1,opt,name=dump" json:"dump,omitempty"`
+ Restore *RestoreStatsEntry `protobuf:"bytes,2,opt,name=restore" json:"restore,omitempty"`
+}
+
+func (x *StatsEntry) Reset() {
+ *x = StatsEntry{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_stats_stats_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *StatsEntry) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*StatsEntry) ProtoMessage() {}
+
+func (x *StatsEntry) ProtoReflect() protoreflect.Message {
+ mi := &file_stats_stats_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use StatsEntry.ProtoReflect.Descriptor instead.
+func (*StatsEntry) Descriptor() ([]byte, []int) {
+ return file_stats_stats_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *StatsEntry) GetDump() *DumpStatsEntry {
+ if x != nil {
+ return x.Dump
+ }
+ return nil
+}
+
+func (x *StatsEntry) GetRestore() *RestoreStatsEntry {
+ if x != nil {
+ return x.Restore
+ }
+ return nil
+}
+
+var File_stats_stats_proto protoreflect.FileDescriptor
+
+var file_stats_stats_proto_rawDesc = []byte{
+ 0x0a, 0x11, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x22, 0xad, 0x04, 0x0a, 0x10, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x73, 0x74, 0x61,
+ 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x72, 0x65, 0x65,
+ 0x7a, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28, 0x0d, 0x52,
+ 0x0c, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x69, 0x6e, 0x67, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1f, 0x0a,
+ 0x0b, 0x66, 0x72, 0x6f, 0x7a, 0x65, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x02,
+ 0x28, 0x0d, 0x52, 0x0a, 0x66, 0x72, 0x6f, 0x7a, 0x65, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21,
+ 0x0a, 0x0c, 0x6d, 0x65, 0x6d, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03,
+ 0x20, 0x02, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x64, 0x75, 0x6d, 0x70, 0x54, 0x69, 0x6d,
+ 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x6d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x74, 0x69,
+ 0x6d, 0x65, 0x18, 0x04, 0x20, 0x02, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, 0x6d, 0x77, 0x72, 0x69,
+ 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f,
+ 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x18, 0x05, 0x20, 0x02, 0x28, 0x04, 0x52, 0x0c, 0x70,
+ 0x61, 0x67, 0x65, 0x73, 0x53, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x70,
+ 0x61, 0x67, 0x65, 0x73, 0x5f, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72,
+ 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x02, 0x28, 0x04, 0x52, 0x12, 0x70, 0x61, 0x67, 0x65, 0x73,
+ 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a,
+ 0x0d, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x18, 0x07,
+ 0x20, 0x02, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x61, 0x67, 0x65, 0x73, 0x57, 0x72, 0x69, 0x74, 0x74,
+ 0x65, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x72, 0x6d, 0x61, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x6f,
+ 0x6c, 0x76, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x69, 0x72, 0x6d, 0x61, 0x70,
+ 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x73,
+ 0x5f, 0x6c, 0x61, 0x7a, 0x79, 0x18, 0x09, 0x20, 0x02, 0x28, 0x04, 0x52, 0x09, 0x70, 0x61, 0x67,
+ 0x65, 0x73, 0x4c, 0x61, 0x7a, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x70,
+ 0x69, 0x70, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65,
+ 0x50, 0x69, 0x70, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x69,
+ 0x70, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70,
+ 0x61, 0x67, 0x65, 0x50, 0x69, 0x70, 0x65, 0x42, 0x75, 0x66, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73,
+ 0x68, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x18, 0x0c,
+ 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x68, 0x70, 0x61, 0x67, 0x65, 0x73, 0x53, 0x63, 0x61,
+ 0x6e, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x68, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f,
+ 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x0d,
+ 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x73, 0x68, 0x70, 0x61, 0x67, 0x65, 0x73, 0x53, 0x6b, 0x69,
+ 0x70, 0x70, 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x68,
+ 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x18, 0x0e, 0x20,
+ 0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x68, 0x70, 0x61, 0x67, 0x65, 0x73, 0x57, 0x72, 0x69, 0x74,
+ 0x74, 0x65, 0x6e, 0x22, 0xd5, 0x01, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f,
+ 0x73, 0x74, 0x61, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x70,
+ 0x61, 0x67, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20,
+ 0x02, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x61, 0x67, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72,
+ 0x65, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x73, 0x6b, 0x69, 0x70,
+ 0x70, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x02, 0x28, 0x04, 0x52, 0x0f, 0x70,
+ 0x61, 0x67, 0x65, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x43, 0x6f, 0x77, 0x12, 0x21,
+ 0x0a, 0x0c, 0x66, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03,
+ 0x20, 0x02, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x54, 0x69, 0x6d,
+ 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x69, 0x6d,
+ 0x65, 0x18, 0x04, 0x20, 0x02, 0x28, 0x0d, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65,
+ 0x54, 0x69, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x72, 0x65,
+ 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x61,
+ 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x22, 0x64, 0x0a, 0x0b, 0x73,
+ 0x74, 0x61, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x04, 0x64, 0x75,
+ 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x5f,
+ 0x73, 0x74, 0x61, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x64, 0x75, 0x6d,
+ 0x70, 0x12, 0x2e, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x73, 0x74, 0x61,
+ 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72,
+ 0x65,
+}
+
+var (
+ file_stats_stats_proto_rawDescOnce sync.Once
+ file_stats_stats_proto_rawDescData = file_stats_stats_proto_rawDesc
+)
+
+func file_stats_stats_proto_rawDescGZIP() []byte {
+ file_stats_stats_proto_rawDescOnce.Do(func() {
+ file_stats_stats_proto_rawDescData = protoimpl.X.CompressGZIP(file_stats_stats_proto_rawDescData)
+ })
+ return file_stats_stats_proto_rawDescData
+}
+
+var file_stats_stats_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_stats_stats_proto_goTypes = []interface{}{
+ (*DumpStatsEntry)(nil), // 0: dump_stats_entry
+ (*RestoreStatsEntry)(nil), // 1: restore_stats_entry
+ (*StatsEntry)(nil), // 2: stats_entry
+}
+var file_stats_stats_proto_depIdxs = []int32{
+ 0, // 0: stats_entry.dump:type_name -> dump_stats_entry
+ 1, // 1: stats_entry.restore:type_name -> restore_stats_entry
+ 2, // [2:2] is the sub-list for method output_type
+ 2, // [2:2] is the sub-list for method input_type
+ 2, // [2:2] is the sub-list for extension type_name
+ 2, // [2:2] is the sub-list for extension extendee
+ 0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_stats_stats_proto_init() }
+func file_stats_stats_proto_init() {
+ if File_stats_stats_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_stats_stats_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*DumpStatsEntry); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_stats_stats_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*RestoreStatsEntry); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_stats_stats_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*StatsEntry); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_stats_stats_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 3,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_stats_stats_proto_goTypes,
+ DependencyIndexes: file_stats_stats_proto_depIdxs,
+ MessageInfos: file_stats_stats_proto_msgTypes,
+ }.Build()
+ File_stats_stats_proto = out.File
+ file_stats_stats_proto_rawDesc = nil
+ file_stats_stats_proto_goTypes = nil
+ file_stats_stats_proto_depIdxs = nil
+}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/stats/stats.proto b/vendor/github.com/checkpoint-restore/go-criu/v7/stats/stats.proto
new file mode 100644
index 00000000..64e46181
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/stats/stats.proto
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: MIT
+
+syntax = "proto2";
+
+// This one contains statistics about dump/restore process
+message dump_stats_entry {
+ required uint32 freezing_time = 1;
+ required uint32 frozen_time = 2;
+ required uint32 memdump_time = 3;
+ required uint32 memwrite_time = 4;
+
+ required uint64 pages_scanned = 5;
+ required uint64 pages_skipped_parent = 6;
+ required uint64 pages_written = 7;
+
+ optional uint32 irmap_resolve = 8;
+
+ required uint64 pages_lazy = 9;
+ optional uint64 page_pipes = 10;
+ optional uint64 page_pipe_bufs = 11;
+
+ optional uint64 shpages_scanned = 12;
+ optional uint64 shpages_skipped_parent = 13;
+ optional uint64 shpages_written = 14;
+}
+
+message restore_stats_entry {
+ required uint64 pages_compared = 1;
+ required uint64 pages_skipped_cow = 2;
+
+ required uint32 forking_time = 3;
+ required uint32 restore_time = 4;
+
+ optional uint64 pages_restored = 5;
+}
+
+message stats_entry {
+ optional dump_stats_entry dump = 1;
+ optional restore_stats_entry restore = 2;
+}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/stats/types.go b/vendor/github.com/checkpoint-restore/go-criu/v7/stats/types.go
new file mode 100644
index 00000000..69d5b0ce
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/stats/types.go
@@ -0,0 +1,14 @@
+package stats
+
+const (
+ StatsDump = "stats-dump"
+ StatsRestore = "stats-restore"
+
+ ImgServiceMagic = 0x55105940 /* Zlatoust */
+ StatsMagic = 0x57093306 /* Ostashkov */
+
+ PrimaryMagicOffset = 0x0
+ SecondaryMagicOffset = 0x4
+ SizeOffset = 0x8
+ PayloadOffset = 0xC
+)
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v7/stats/utils.go b/vendor/github.com/checkpoint-restore/go-criu/v7/stats/utils.go
new file mode 100644
index 00000000..2b65cc2c
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v7/stats/utils.go
@@ -0,0 +1,52 @@
+package stats
+
+import (
+ "encoding/binary"
+ "errors"
+ "os"
+ "path/filepath"
+
+ "google.golang.org/protobuf/proto"
+)
+
+func readStatisticsFile(imgDir *os.File, fileName string) (*StatsEntry, error) {
+ buf, err := os.ReadFile(filepath.Join(imgDir.Name(), fileName))
+ if err != nil {
+ return nil, err
+ }
+
+ if binary.LittleEndian.Uint32(buf[PrimaryMagicOffset:SecondaryMagicOffset]) != ImgServiceMagic {
+ return nil, errors.New("primary magic not found")
+ }
+
+ if binary.LittleEndian.Uint32(buf[SecondaryMagicOffset:SizeOffset]) != StatsMagic {
+ return nil, errors.New("secondary magic not found")
+ }
+
+ payloadSize := binary.LittleEndian.Uint32(buf[SizeOffset:PayloadOffset])
+
+ st := &StatsEntry{}
+ if err := proto.Unmarshal(buf[PayloadOffset:PayloadOffset+payloadSize], st); err != nil {
+ return nil, err
+ }
+
+ return st, nil
+}
+
+func CriuGetDumpStats(imgDir *os.File) (*DumpStatsEntry, error) {
+ st, err := readStatisticsFile(imgDir, StatsDump)
+ if err != nil {
+ return nil, err
+ }
+
+ return st.GetDump(), nil
+}
+
+func CriuGetRestoreStats(imgDir *os.File) (*RestoreStatsEntry, error) {
+ st, err := readStatisticsFile(imgDir, StatsRestore)
+ if err != nil {
+ return nil, err
+ }
+
+ return st.GetRestore(), nil
+}
diff --git a/vendor/github.com/chzyer/readline/.gitignore b/vendor/github.com/chzyer/readline/.gitignore
new file mode 100644
index 00000000..a3062bea
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/.gitignore
@@ -0,0 +1 @@
+.vscode/*
diff --git a/vendor/github.com/chzyer/readline/.travis.yml b/vendor/github.com/chzyer/readline/.travis.yml
new file mode 100644
index 00000000..9c359554
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/.travis.yml
@@ -0,0 +1,8 @@
+language: go
+go:
+ - 1.x
+script:
+ - GOOS=windows go install github.com/chzyer/readline/example/...
+ - GOOS=linux go install github.com/chzyer/readline/example/...
+ - GOOS=darwin go install github.com/chzyer/readline/example/...
+ - go test -race -v
diff --git a/vendor/github.com/chzyer/readline/CHANGELOG.md b/vendor/github.com/chzyer/readline/CHANGELOG.md
new file mode 100644
index 00000000..14ff5be1
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/CHANGELOG.md
@@ -0,0 +1,58 @@
+# ChangeLog
+
+### 1.4 - 2016-07-25
+
+* [#60][60] Support dynamic autocompletion
+* Fix ANSI parser on Windows
+* Fix wrong column width in complete mode on Windows
+* Remove dependent package "golang.org/x/crypto/ssh/terminal"
+
+### 1.3 - 2016-05-09
+
+* [#38][38] add SetChildren for prefix completer interface
+* [#42][42] improve multiple lines compatibility
+* [#43][43] remove sub-package(runes) for gopkg compatibility
+* [#46][46] Auto complete with space prefixed line
+* [#48][48] support suspend process (ctrl+Z)
+* [#49][49] fix bug that check equals with previous command
+* [#53][53] Fix bug which causes integer divide by zero panicking when input buffer is empty
+
+### 1.2 - 2016-03-05
+
+* Add a demo for checking password strength [example/readline-pass-strength](https://github.com/chzyer/readline/blob/master/example/readline-pass-strength/readline-pass-strength.go), , written by [@sahib](https://github.com/sahib)
+* [#23][23], support stdin remapping
+* [#27][27], add a `UniqueEditLine` to `Config`, which will erase the editing line after user submited it, usually use in IM.
+* Add a demo for multiline [example/readline-multiline](https://github.com/chzyer/readline/blob/master/example/readline-multiline/readline-multiline.go) which can submit one SQL by multiple lines.
+* Supports performs even stdin/stdout is not a tty.
+* Add a new simple apis for single instance, check by [here](https://github.com/chzyer/readline/blob/master/std.go). It need to save history manually if using this api.
+* [#28][28], fixes the history is not working as expected.
+* [#33][33], vim mode now support `c`, `d`, `x (delete character)`, `r (replace character)`
+
+### 1.1 - 2015-11-20
+
+* [#12][12] Add support for key ``/``/``
+* Only enter raw mode as needed (calling `Readline()`), program will receive signal(e.g. Ctrl+C) if not interact with `readline`.
+* Bugs fixed for `PrefixCompleter`
+* Press `Ctrl+D` in empty line will cause `io.EOF` in error, Press `Ctrl+C` in anytime will cause `ErrInterrupt` instead of `io.EOF`, this will privodes a shell-like user experience.
+* Customable Interrupt/EOF prompt in `Config`
+* [#17][17] Change atomic package to use 32bit function to let it runnable on arm 32bit devices
+* Provides a new password user experience(`readline.ReadPasswordEx()`).
+
+### 1.0 - 2015-10-14
+
+* Initial public release.
+
+[12]: https://github.com/chzyer/readline/pull/12
+[17]: https://github.com/chzyer/readline/pull/17
+[23]: https://github.com/chzyer/readline/pull/23
+[27]: https://github.com/chzyer/readline/pull/27
+[28]: https://github.com/chzyer/readline/pull/28
+[33]: https://github.com/chzyer/readline/pull/33
+[38]: https://github.com/chzyer/readline/pull/38
+[42]: https://github.com/chzyer/readline/pull/42
+[43]: https://github.com/chzyer/readline/pull/43
+[46]: https://github.com/chzyer/readline/pull/46
+[48]: https://github.com/chzyer/readline/pull/48
+[49]: https://github.com/chzyer/readline/pull/49
+[53]: https://github.com/chzyer/readline/pull/53
+[60]: https://github.com/chzyer/readline/pull/60
diff --git a/vendor/github.com/fvbommel/sortorder/LICENSE b/vendor/github.com/chzyer/readline/LICENSE
similarity index 96%
rename from vendor/github.com/fvbommel/sortorder/LICENSE
rename to vendor/github.com/chzyer/readline/LICENSE
index 5c695fb5..c9afab3d 100644
--- a/vendor/github.com/fvbommel/sortorder/LICENSE
+++ b/vendor/github.com/chzyer/readline/LICENSE
@@ -1,13 +1,17 @@
The MIT License (MIT)
-Copyright (c) 2015 Frits van Bommel
+
+Copyright (c) 2015 Chzyer
+
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
+
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -15,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+
diff --git a/vendor/github.com/chzyer/readline/README.md b/vendor/github.com/chzyer/readline/README.md
new file mode 100644
index 00000000..4b0a5ff5
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/README.md
@@ -0,0 +1,114 @@
+[](https://travis-ci.org/chzyer/readline)
+[](LICENSE.md)
+[](https://github.com/chzyer/readline/releases)
+[](https://godoc.org/github.com/chzyer/readline)
+[](#backers)
+[](#sponsors)
+
+
+
+
+
+
+
+A powerful readline library in `Linux` `macOS` `Windows` `Solaris` `AIX`
+
+## Guide
+
+* [Demo](example/readline-demo/readline-demo.go)
+* [Shortcut](doc/shortcut.md)
+
+## Repos using readline
+
+[](https://github.com/cockroachdb/cockroach)
+[](https://github.com/robertkrimen/otto)
+[](https://github.com/remind101/empire)
+[](https://github.com/mehrdadrad/mylg)
+[](https://github.com/knq/usql)
+[](https://github.com/youtube/doorman)
+[](https://github.com/bom-d-van/harp)
+[](https://github.com/abiosoft/ishell)
+[](https://github.com/Netflix/hal-9001)
+[](https://github.com/docker/go-p9p)
+
+
+## Feedback
+
+If you have any questions, please submit a github issue and any pull requests is welcomed :)
+
+* [https://twitter.com/chzyer](https://twitter.com/chzyer)
+* [http://weibo.com/2145262190](http://weibo.com/2145262190)
+
+
+## Backers
+
+Love Readline? Help me keep it alive by donating funds to cover project expenses!
+[[Become a backer](https://opencollective.com/readline#backer)]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Sponsors
+
+Become a sponsor and get your logo here on our Github page. [[Become a sponsor](https://opencollective.com/readline#sponsor)]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vendor/github.com/chzyer/readline/ansi_windows.go b/vendor/github.com/chzyer/readline/ansi_windows.go
new file mode 100644
index 00000000..63b908c1
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/ansi_windows.go
@@ -0,0 +1,249 @@
+// +build windows
+
+package readline
+
+import (
+ "bufio"
+ "io"
+ "strconv"
+ "strings"
+ "sync"
+ "unicode/utf8"
+ "unsafe"
+)
+
+const (
+ _ = uint16(0)
+ COLOR_FBLUE = 0x0001
+ COLOR_FGREEN = 0x0002
+ COLOR_FRED = 0x0004
+ COLOR_FINTENSITY = 0x0008
+
+ COLOR_BBLUE = 0x0010
+ COLOR_BGREEN = 0x0020
+ COLOR_BRED = 0x0040
+ COLOR_BINTENSITY = 0x0080
+
+ COMMON_LVB_UNDERSCORE = 0x8000
+ COMMON_LVB_BOLD = 0x0007
+)
+
+var ColorTableFg = []word{
+ 0, // 30: Black
+ COLOR_FRED, // 31: Red
+ COLOR_FGREEN, // 32: Green
+ COLOR_FRED | COLOR_FGREEN, // 33: Yellow
+ COLOR_FBLUE, // 34: Blue
+ COLOR_FRED | COLOR_FBLUE, // 35: Magenta
+ COLOR_FGREEN | COLOR_FBLUE, // 36: Cyan
+ COLOR_FRED | COLOR_FBLUE | COLOR_FGREEN, // 37: White
+}
+
+var ColorTableBg = []word{
+ 0, // 40: Black
+ COLOR_BRED, // 41: Red
+ COLOR_BGREEN, // 42: Green
+ COLOR_BRED | COLOR_BGREEN, // 43: Yellow
+ COLOR_BBLUE, // 44: Blue
+ COLOR_BRED | COLOR_BBLUE, // 45: Magenta
+ COLOR_BGREEN | COLOR_BBLUE, // 46: Cyan
+ COLOR_BRED | COLOR_BBLUE | COLOR_BGREEN, // 47: White
+}
+
+type ANSIWriter struct {
+ target io.Writer
+ wg sync.WaitGroup
+ ctx *ANSIWriterCtx
+ sync.Mutex
+}
+
+func NewANSIWriter(w io.Writer) *ANSIWriter {
+ a := &ANSIWriter{
+ target: w,
+ ctx: NewANSIWriterCtx(w),
+ }
+ return a
+}
+
+func (a *ANSIWriter) Close() error {
+ a.wg.Wait()
+ return nil
+}
+
+type ANSIWriterCtx struct {
+ isEsc bool
+ isEscSeq bool
+ arg []string
+ target *bufio.Writer
+ wantFlush bool
+}
+
+func NewANSIWriterCtx(target io.Writer) *ANSIWriterCtx {
+ return &ANSIWriterCtx{
+ target: bufio.NewWriter(target),
+ }
+}
+
+func (a *ANSIWriterCtx) Flush() {
+ a.target.Flush()
+}
+
+func (a *ANSIWriterCtx) process(r rune) bool {
+ if a.wantFlush {
+ if r == 0 || r == CharEsc {
+ a.wantFlush = false
+ a.target.Flush()
+ }
+ }
+ if a.isEscSeq {
+ a.isEscSeq = a.ioloopEscSeq(a.target, r, &a.arg)
+ return true
+ }
+
+ switch r {
+ case CharEsc:
+ a.isEsc = true
+ case '[':
+ if a.isEsc {
+ a.arg = nil
+ a.isEscSeq = true
+ a.isEsc = false
+ break
+ }
+ fallthrough
+ default:
+ a.target.WriteRune(r)
+ a.wantFlush = true
+ }
+ return true
+}
+
+func (a *ANSIWriterCtx) ioloopEscSeq(w *bufio.Writer, r rune, argptr *[]string) bool {
+ arg := *argptr
+ var err error
+
+ if r >= 'A' && r <= 'D' {
+ count := short(GetInt(arg, 1))
+ info, err := GetConsoleScreenBufferInfo()
+ if err != nil {
+ return false
+ }
+ switch r {
+ case 'A': // up
+ info.dwCursorPosition.y -= count
+ case 'B': // down
+ info.dwCursorPosition.y += count
+ case 'C': // right
+ info.dwCursorPosition.x += count
+ case 'D': // left
+ info.dwCursorPosition.x -= count
+ }
+ SetConsoleCursorPosition(&info.dwCursorPosition)
+ return false
+ }
+
+ switch r {
+ case 'J':
+ killLines()
+ case 'K':
+ eraseLine()
+ case 'm':
+ color := word(0)
+ for _, item := range arg {
+ var c int
+ c, err = strconv.Atoi(item)
+ if err != nil {
+ w.WriteString("[" + strings.Join(arg, ";") + "m")
+ break
+ }
+ if c >= 30 && c < 40 {
+ color ^= COLOR_FINTENSITY
+ color |= ColorTableFg[c-30]
+ } else if c >= 40 && c < 50 {
+ color ^= COLOR_BINTENSITY
+ color |= ColorTableBg[c-40]
+ } else if c == 4 {
+ color |= COMMON_LVB_UNDERSCORE | ColorTableFg[7]
+ } else if c == 1 {
+ color |= COMMON_LVB_BOLD | COLOR_FINTENSITY
+ } else { // unknown code treat as reset
+ color = ColorTableFg[7]
+ }
+ }
+ if err != nil {
+ break
+ }
+ kernel.SetConsoleTextAttribute(stdout, uintptr(color))
+ case '\007': // set title
+ case ';':
+ if len(arg) == 0 || arg[len(arg)-1] != "" {
+ arg = append(arg, "")
+ *argptr = arg
+ }
+ return true
+ default:
+ if len(arg) == 0 {
+ arg = append(arg, "")
+ }
+ arg[len(arg)-1] += string(r)
+ *argptr = arg
+ return true
+ }
+ *argptr = nil
+ return false
+}
+
+func (a *ANSIWriter) Write(b []byte) (int, error) {
+ a.Lock()
+ defer a.Unlock()
+
+ off := 0
+ for len(b) > off {
+ r, size := utf8.DecodeRune(b[off:])
+ if size == 0 {
+ return off, io.ErrShortWrite
+ }
+ off += size
+ a.ctx.process(r)
+ }
+ a.ctx.Flush()
+ return off, nil
+}
+
+func killLines() error {
+ sbi, err := GetConsoleScreenBufferInfo()
+ if err != nil {
+ return err
+ }
+
+ size := (sbi.dwCursorPosition.y - sbi.dwSize.y) * sbi.dwSize.x
+ size += sbi.dwCursorPosition.x
+
+ var written int
+ kernel.FillConsoleOutputAttribute(stdout, uintptr(ColorTableFg[7]),
+ uintptr(size),
+ sbi.dwCursorPosition.ptr(),
+ uintptr(unsafe.Pointer(&written)),
+ )
+ return kernel.FillConsoleOutputCharacterW(stdout, uintptr(' '),
+ uintptr(size),
+ sbi.dwCursorPosition.ptr(),
+ uintptr(unsafe.Pointer(&written)),
+ )
+}
+
+func eraseLine() error {
+ sbi, err := GetConsoleScreenBufferInfo()
+ if err != nil {
+ return err
+ }
+
+ size := sbi.dwSize.x
+ sbi.dwCursorPosition.x = 0
+ var written int
+ return kernel.FillConsoleOutputCharacterW(stdout, uintptr(' '),
+ uintptr(size),
+ sbi.dwCursorPosition.ptr(),
+ uintptr(unsafe.Pointer(&written)),
+ )
+}
diff --git a/vendor/github.com/chzyer/readline/complete.go b/vendor/github.com/chzyer/readline/complete.go
new file mode 100644
index 00000000..c08c9941
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/complete.go
@@ -0,0 +1,285 @@
+package readline
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+)
+
+type AutoCompleter interface {
+ // Readline will pass the whole line and current offset to it
+ // Completer need to pass all the candidates, and how long they shared the same characters in line
+ // Example:
+ // [go, git, git-shell, grep]
+ // Do("g", 1) => ["o", "it", "it-shell", "rep"], 1
+ // Do("gi", 2) => ["t", "t-shell"], 2
+ // Do("git", 3) => ["", "-shell"], 3
+ Do(line []rune, pos int) (newLine [][]rune, length int)
+}
+
+type TabCompleter struct{}
+
+func (t *TabCompleter) Do([]rune, int) ([][]rune, int) {
+ return [][]rune{[]rune("\t")}, 0
+}
+
+type opCompleter struct {
+ w io.Writer
+ op *Operation
+ width int
+
+ inCompleteMode bool
+ inSelectMode bool
+ candidate [][]rune
+ candidateSource []rune
+ candidateOff int
+ candidateChoise int
+ candidateColNum int
+}
+
+func newOpCompleter(w io.Writer, op *Operation, width int) *opCompleter {
+ return &opCompleter{
+ w: w,
+ op: op,
+ width: width,
+ }
+}
+
+func (o *opCompleter) doSelect() {
+ if len(o.candidate) == 1 {
+ o.op.buf.WriteRunes(o.candidate[0])
+ o.ExitCompleteMode(false)
+ return
+ }
+ o.nextCandidate(1)
+ o.CompleteRefresh()
+}
+
+func (o *opCompleter) nextCandidate(i int) {
+ o.candidateChoise += i
+ o.candidateChoise = o.candidateChoise % len(o.candidate)
+ if o.candidateChoise < 0 {
+ o.candidateChoise = len(o.candidate) + o.candidateChoise
+ }
+}
+
+func (o *opCompleter) OnComplete() bool {
+ if o.width == 0 {
+ return false
+ }
+ if o.IsInCompleteSelectMode() {
+ o.doSelect()
+ return true
+ }
+
+ buf := o.op.buf
+ rs := buf.Runes()
+
+ if o.IsInCompleteMode() && o.candidateSource != nil && runes.Equal(rs, o.candidateSource) {
+ o.EnterCompleteSelectMode()
+ o.doSelect()
+ return true
+ }
+
+ o.ExitCompleteSelectMode()
+ o.candidateSource = rs
+ newLines, offset := o.op.cfg.AutoComplete.Do(rs, buf.idx)
+ if len(newLines) == 0 {
+ o.ExitCompleteMode(false)
+ return true
+ }
+
+ // only Aggregate candidates in non-complete mode
+ if !o.IsInCompleteMode() {
+ if len(newLines) == 1 {
+ buf.WriteRunes(newLines[0])
+ o.ExitCompleteMode(false)
+ return true
+ }
+
+ same, size := runes.Aggregate(newLines)
+ if size > 0 {
+ buf.WriteRunes(same)
+ o.ExitCompleteMode(false)
+ return true
+ }
+ }
+
+ o.EnterCompleteMode(offset, newLines)
+ return true
+}
+
+func (o *opCompleter) IsInCompleteSelectMode() bool {
+ return o.inSelectMode
+}
+
+func (o *opCompleter) IsInCompleteMode() bool {
+ return o.inCompleteMode
+}
+
+func (o *opCompleter) HandleCompleteSelect(r rune) bool {
+ next := true
+ switch r {
+ case CharEnter, CharCtrlJ:
+ next = false
+ o.op.buf.WriteRunes(o.op.candidate[o.op.candidateChoise])
+ o.ExitCompleteMode(false)
+ case CharLineStart:
+ num := o.candidateChoise % o.candidateColNum
+ o.nextCandidate(-num)
+ case CharLineEnd:
+ num := o.candidateColNum - o.candidateChoise%o.candidateColNum - 1
+ o.candidateChoise += num
+ if o.candidateChoise >= len(o.candidate) {
+ o.candidateChoise = len(o.candidate) - 1
+ }
+ case CharBackspace:
+ o.ExitCompleteSelectMode()
+ next = false
+ case CharTab, CharForward:
+ o.doSelect()
+ case CharBell, CharInterrupt:
+ o.ExitCompleteMode(true)
+ next = false
+ case CharNext:
+ tmpChoise := o.candidateChoise + o.candidateColNum
+ if tmpChoise >= o.getMatrixSize() {
+ tmpChoise -= o.getMatrixSize()
+ } else if tmpChoise >= len(o.candidate) {
+ tmpChoise += o.candidateColNum
+ tmpChoise -= o.getMatrixSize()
+ }
+ o.candidateChoise = tmpChoise
+ case CharBackward:
+ o.nextCandidate(-1)
+ case CharPrev:
+ tmpChoise := o.candidateChoise - o.candidateColNum
+ if tmpChoise < 0 {
+ tmpChoise += o.getMatrixSize()
+ if tmpChoise >= len(o.candidate) {
+ tmpChoise -= o.candidateColNum
+ }
+ }
+ o.candidateChoise = tmpChoise
+ default:
+ next = false
+ o.ExitCompleteSelectMode()
+ }
+ if next {
+ o.CompleteRefresh()
+ return true
+ }
+ return false
+}
+
+func (o *opCompleter) getMatrixSize() int {
+ line := len(o.candidate) / o.candidateColNum
+ if len(o.candidate)%o.candidateColNum != 0 {
+ line++
+ }
+ return line * o.candidateColNum
+}
+
+func (o *opCompleter) OnWidthChange(newWidth int) {
+ o.width = newWidth
+}
+
+func (o *opCompleter) CompleteRefresh() {
+ if !o.inCompleteMode {
+ return
+ }
+ lineCnt := o.op.buf.CursorLineCount()
+ colWidth := 0
+ for _, c := range o.candidate {
+ w := runes.WidthAll(c)
+ if w > colWidth {
+ colWidth = w
+ }
+ }
+ colWidth += o.candidateOff + 1
+ same := o.op.buf.RuneSlice(-o.candidateOff)
+
+ // -1 to avoid reach the end of line
+ width := o.width - 1
+ colNum := width / colWidth
+ if colNum != 0 {
+ colWidth += (width - (colWidth * colNum)) / colNum
+ }
+
+ o.candidateColNum = colNum
+ buf := bufio.NewWriter(o.w)
+ buf.Write(bytes.Repeat([]byte("\n"), lineCnt))
+
+ colIdx := 0
+ lines := 1
+ buf.WriteString("\033[J")
+ for idx, c := range o.candidate {
+ inSelect := idx == o.candidateChoise && o.IsInCompleteSelectMode()
+ if inSelect {
+ buf.WriteString("\033[30;47m")
+ }
+ buf.WriteString(string(same))
+ buf.WriteString(string(c))
+ buf.Write(bytes.Repeat([]byte(" "), colWidth-runes.WidthAll(c)-runes.WidthAll(same)))
+
+ if inSelect {
+ buf.WriteString("\033[0m")
+ }
+
+ colIdx++
+ if colIdx == colNum {
+ buf.WriteString("\n")
+ lines++
+ colIdx = 0
+ }
+ }
+
+ // move back
+ fmt.Fprintf(buf, "\033[%dA\r", lineCnt-1+lines)
+ fmt.Fprintf(buf, "\033[%dC", o.op.buf.idx+o.op.buf.PromptLen())
+ buf.Flush()
+}
+
+func (o *opCompleter) aggCandidate(candidate [][]rune) int {
+ offset := 0
+ for i := 0; i < len(candidate[0]); i++ {
+ for j := 0; j < len(candidate)-1; j++ {
+ if i > len(candidate[j]) {
+ goto aggregate
+ }
+ if candidate[j][i] != candidate[j+1][i] {
+ goto aggregate
+ }
+ }
+ offset = i
+ }
+aggregate:
+ return offset
+}
+
+func (o *opCompleter) EnterCompleteSelectMode() {
+ o.inSelectMode = true
+ o.candidateChoise = -1
+ o.CompleteRefresh()
+}
+
+func (o *opCompleter) EnterCompleteMode(offset int, candidate [][]rune) {
+ o.inCompleteMode = true
+ o.candidate = candidate
+ o.candidateOff = offset
+ o.CompleteRefresh()
+}
+
+func (o *opCompleter) ExitCompleteSelectMode() {
+ o.inSelectMode = false
+ o.candidate = nil
+ o.candidateChoise = -1
+ o.candidateOff = -1
+ o.candidateSource = nil
+}
+
+func (o *opCompleter) ExitCompleteMode(revent bool) {
+ o.inCompleteMode = false
+ o.ExitCompleteSelectMode()
+}
diff --git a/vendor/github.com/chzyer/readline/complete_helper.go b/vendor/github.com/chzyer/readline/complete_helper.go
new file mode 100644
index 00000000..58d72487
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/complete_helper.go
@@ -0,0 +1,165 @@
+package readline
+
+import (
+ "bytes"
+ "strings"
+)
+
+// Caller type for dynamic completion
+type DynamicCompleteFunc func(string) []string
+
+type PrefixCompleterInterface interface {
+ Print(prefix string, level int, buf *bytes.Buffer)
+ Do(line []rune, pos int) (newLine [][]rune, length int)
+ GetName() []rune
+ GetChildren() []PrefixCompleterInterface
+ SetChildren(children []PrefixCompleterInterface)
+}
+
+type DynamicPrefixCompleterInterface interface {
+ PrefixCompleterInterface
+ IsDynamic() bool
+ GetDynamicNames(line []rune) [][]rune
+}
+
+type PrefixCompleter struct {
+ Name []rune
+ Dynamic bool
+ Callback DynamicCompleteFunc
+ Children []PrefixCompleterInterface
+}
+
+func (p *PrefixCompleter) Tree(prefix string) string {
+ buf := bytes.NewBuffer(nil)
+ p.Print(prefix, 0, buf)
+ return buf.String()
+}
+
+func Print(p PrefixCompleterInterface, prefix string, level int, buf *bytes.Buffer) {
+ if strings.TrimSpace(string(p.GetName())) != "" {
+ buf.WriteString(prefix)
+ if level > 0 {
+ buf.WriteString("├")
+ buf.WriteString(strings.Repeat("─", (level*4)-2))
+ buf.WriteString(" ")
+ }
+ buf.WriteString(string(p.GetName()) + "\n")
+ level++
+ }
+ for _, ch := range p.GetChildren() {
+ ch.Print(prefix, level, buf)
+ }
+}
+
+func (p *PrefixCompleter) Print(prefix string, level int, buf *bytes.Buffer) {
+ Print(p, prefix, level, buf)
+}
+
+func (p *PrefixCompleter) IsDynamic() bool {
+ return p.Dynamic
+}
+
+func (p *PrefixCompleter) GetName() []rune {
+ return p.Name
+}
+
+func (p *PrefixCompleter) GetDynamicNames(line []rune) [][]rune {
+ var names = [][]rune{}
+ for _, name := range p.Callback(string(line)) {
+ names = append(names, []rune(name+" "))
+ }
+ return names
+}
+
+func (p *PrefixCompleter) GetChildren() []PrefixCompleterInterface {
+ return p.Children
+}
+
+func (p *PrefixCompleter) SetChildren(children []PrefixCompleterInterface) {
+ p.Children = children
+}
+
+func NewPrefixCompleter(pc ...PrefixCompleterInterface) *PrefixCompleter {
+ return PcItem("", pc...)
+}
+
+func PcItem(name string, pc ...PrefixCompleterInterface) *PrefixCompleter {
+ name += " "
+ return &PrefixCompleter{
+ Name: []rune(name),
+ Dynamic: false,
+ Children: pc,
+ }
+}
+
+func PcItemDynamic(callback DynamicCompleteFunc, pc ...PrefixCompleterInterface) *PrefixCompleter {
+ return &PrefixCompleter{
+ Callback: callback,
+ Dynamic: true,
+ Children: pc,
+ }
+}
+
+func (p *PrefixCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int) {
+ return doInternal(p, line, pos, line)
+}
+
+func Do(p PrefixCompleterInterface, line []rune, pos int) (newLine [][]rune, offset int) {
+ return doInternal(p, line, pos, line)
+}
+
+func doInternal(p PrefixCompleterInterface, line []rune, pos int, origLine []rune) (newLine [][]rune, offset int) {
+ line = runes.TrimSpaceLeft(line[:pos])
+ goNext := false
+ var lineCompleter PrefixCompleterInterface
+ for _, child := range p.GetChildren() {
+ childNames := make([][]rune, 1)
+
+ childDynamic, ok := child.(DynamicPrefixCompleterInterface)
+ if ok && childDynamic.IsDynamic() {
+ childNames = childDynamic.GetDynamicNames(origLine)
+ } else {
+ childNames[0] = child.GetName()
+ }
+
+ for _, childName := range childNames {
+ if len(line) >= len(childName) {
+ if runes.HasPrefix(line, childName) {
+ if len(line) == len(childName) {
+ newLine = append(newLine, []rune{' '})
+ } else {
+ newLine = append(newLine, childName)
+ }
+ offset = len(childName)
+ lineCompleter = child
+ goNext = true
+ }
+ } else {
+ if runes.HasPrefix(childName, line) {
+ newLine = append(newLine, childName[len(line):])
+ offset = len(line)
+ lineCompleter = child
+ }
+ }
+ }
+ }
+
+ if len(newLine) != 1 {
+ return
+ }
+
+ tmpLine := make([]rune, 0, len(line))
+ for i := offset; i < len(line); i++ {
+ if line[i] == ' ' {
+ continue
+ }
+
+ tmpLine = append(tmpLine, line[i:]...)
+ return doInternal(lineCompleter, tmpLine, len(tmpLine), origLine)
+ }
+
+ if goNext {
+ return doInternal(lineCompleter, nil, 0, origLine)
+ }
+ return
+}
diff --git a/vendor/github.com/chzyer/readline/complete_segment.go b/vendor/github.com/chzyer/readline/complete_segment.go
new file mode 100644
index 00000000..5ceadd80
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/complete_segment.go
@@ -0,0 +1,82 @@
+package readline
+
+type SegmentCompleter interface {
+ // a
+ // |- a1
+ // |--- a11
+ // |- a2
+ // b
+ // input:
+ // DoTree([], 0) [a, b]
+ // DoTree([a], 1) [a]
+ // DoTree([a, ], 0) [a1, a2]
+ // DoTree([a, a], 1) [a1, a2]
+ // DoTree([a, a1], 2) [a1]
+ // DoTree([a, a1, ], 0) [a11]
+ // DoTree([a, a1, a], 1) [a11]
+ DoSegment([][]rune, int) [][]rune
+}
+
+type dumpSegmentCompleter struct {
+ f func([][]rune, int) [][]rune
+}
+
+func (d *dumpSegmentCompleter) DoSegment(segment [][]rune, n int) [][]rune {
+ return d.f(segment, n)
+}
+
+func SegmentFunc(f func([][]rune, int) [][]rune) AutoCompleter {
+ return &SegmentComplete{&dumpSegmentCompleter{f}}
+}
+
+func SegmentAutoComplete(completer SegmentCompleter) *SegmentComplete {
+ return &SegmentComplete{
+ SegmentCompleter: completer,
+ }
+}
+
+type SegmentComplete struct {
+ SegmentCompleter
+}
+
+func RetSegment(segments [][]rune, cands [][]rune, idx int) ([][]rune, int) {
+ ret := make([][]rune, 0, len(cands))
+ lastSegment := segments[len(segments)-1]
+ for _, cand := range cands {
+ if !runes.HasPrefix(cand, lastSegment) {
+ continue
+ }
+ ret = append(ret, cand[len(lastSegment):])
+ }
+ return ret, idx
+}
+
+func SplitSegment(line []rune, pos int) ([][]rune, int) {
+ segs := [][]rune{}
+ lastIdx := -1
+ line = line[:pos]
+ pos = 0
+ for idx, l := range line {
+ if l == ' ' {
+ pos = 0
+ segs = append(segs, line[lastIdx+1:idx])
+ lastIdx = idx
+ } else {
+ pos++
+ }
+ }
+ segs = append(segs, line[lastIdx+1:])
+ return segs, pos
+}
+
+func (c *SegmentComplete) Do(line []rune, pos int) (newLine [][]rune, offset int) {
+
+ segment, idx := SplitSegment(line, pos)
+
+ cands := c.DoSegment(segment, idx)
+ newLine, offset = RetSegment(segment, cands, idx)
+ for idx := range newLine {
+ newLine[idx] = append(newLine[idx], ' ')
+ }
+ return newLine, offset
+}
diff --git a/vendor/github.com/chzyer/readline/history.go b/vendor/github.com/chzyer/readline/history.go
new file mode 100644
index 00000000..6b17c464
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/history.go
@@ -0,0 +1,330 @@
+package readline
+
+import (
+ "bufio"
+ "container/list"
+ "fmt"
+ "os"
+ "strings"
+ "sync"
+)
+
+type hisItem struct {
+ Source []rune
+ Version int64
+ Tmp []rune
+}
+
+func (h *hisItem) Clean() {
+ h.Source = nil
+ h.Tmp = nil
+}
+
+type opHistory struct {
+ cfg *Config
+ history *list.List
+ historyVer int64
+ current *list.Element
+ fd *os.File
+ fdLock sync.Mutex
+ enable bool
+}
+
+func newOpHistory(cfg *Config) (o *opHistory) {
+ o = &opHistory{
+ cfg: cfg,
+ history: list.New(),
+ enable: true,
+ }
+ return o
+}
+
+func (o *opHistory) Reset() {
+ o.history = list.New()
+ o.current = nil
+}
+
+func (o *opHistory) IsHistoryClosed() bool {
+ o.fdLock.Lock()
+ defer o.fdLock.Unlock()
+ return o.fd.Fd() == ^(uintptr(0))
+}
+
+func (o *opHistory) Init() {
+ if o.IsHistoryClosed() {
+ o.initHistory()
+ }
+}
+
+func (o *opHistory) initHistory() {
+ if o.cfg.HistoryFile != "" {
+ o.historyUpdatePath(o.cfg.HistoryFile)
+ }
+}
+
+// only called by newOpHistory
+func (o *opHistory) historyUpdatePath(path string) {
+ o.fdLock.Lock()
+ defer o.fdLock.Unlock()
+ f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
+ if err != nil {
+ return
+ }
+ o.fd = f
+ r := bufio.NewReader(o.fd)
+ total := 0
+ for ; ; total++ {
+ line, err := r.ReadString('\n')
+ if err != nil {
+ break
+ }
+ // ignore the empty line
+ line = strings.TrimSpace(line)
+ if len(line) == 0 {
+ continue
+ }
+ o.Push([]rune(line))
+ o.Compact()
+ }
+ if total > o.cfg.HistoryLimit {
+ o.rewriteLocked()
+ }
+ o.historyVer++
+ o.Push(nil)
+ return
+}
+
+func (o *opHistory) Compact() {
+ for o.history.Len() > o.cfg.HistoryLimit && o.history.Len() > 0 {
+ o.history.Remove(o.history.Front())
+ }
+}
+
+func (o *opHistory) Rewrite() {
+ o.fdLock.Lock()
+ defer o.fdLock.Unlock()
+ o.rewriteLocked()
+}
+
+func (o *opHistory) rewriteLocked() {
+ if o.cfg.HistoryFile == "" {
+ return
+ }
+
+ tmpFile := o.cfg.HistoryFile + ".tmp"
+ fd, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_APPEND, 0666)
+ if err != nil {
+ return
+ }
+
+ buf := bufio.NewWriter(fd)
+ for elem := o.history.Front(); elem != nil; elem = elem.Next() {
+ buf.WriteString(string(elem.Value.(*hisItem).Source) + "\n")
+ }
+ buf.Flush()
+
+ // replace history file
+ if err = os.Rename(tmpFile, o.cfg.HistoryFile); err != nil {
+ fd.Close()
+ return
+ }
+
+ if o.fd != nil {
+ o.fd.Close()
+ }
+ // fd is write only, just satisfy what we need.
+ o.fd = fd
+}
+
+func (o *opHistory) Close() {
+ o.fdLock.Lock()
+ defer o.fdLock.Unlock()
+ if o.fd != nil {
+ o.fd.Close()
+ }
+}
+
+func (o *opHistory) FindBck(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
+ for elem := o.current; elem != nil; elem = elem.Prev() {
+ item := o.showItem(elem.Value)
+ if isNewSearch {
+ start += len(rs)
+ }
+ if elem == o.current {
+ if len(item) >= start {
+ item = item[:start]
+ }
+ }
+ idx := runes.IndexAllBckEx(item, rs, o.cfg.HistorySearchFold)
+ if idx < 0 {
+ continue
+ }
+ return idx, elem
+ }
+ return -1, nil
+}
+
+func (o *opHistory) FindFwd(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
+ for elem := o.current; elem != nil; elem = elem.Next() {
+ item := o.showItem(elem.Value)
+ if isNewSearch {
+ start -= len(rs)
+ if start < 0 {
+ start = 0
+ }
+ }
+ if elem == o.current {
+ if len(item)-1 >= start {
+ item = item[start:]
+ } else {
+ continue
+ }
+ }
+ idx := runes.IndexAllEx(item, rs, o.cfg.HistorySearchFold)
+ if idx < 0 {
+ continue
+ }
+ if elem == o.current {
+ idx += start
+ }
+ return idx, elem
+ }
+ return -1, nil
+}
+
+func (o *opHistory) showItem(obj interface{}) []rune {
+ item := obj.(*hisItem)
+ if item.Version == o.historyVer {
+ return item.Tmp
+ }
+ return item.Source
+}
+
+func (o *opHistory) Prev() []rune {
+ if o.current == nil {
+ return nil
+ }
+ current := o.current.Prev()
+ if current == nil {
+ return nil
+ }
+ o.current = current
+ return runes.Copy(o.showItem(current.Value))
+}
+
+func (o *opHistory) Next() ([]rune, bool) {
+ if o.current == nil {
+ return nil, false
+ }
+ current := o.current.Next()
+ if current == nil {
+ return nil, false
+ }
+
+ o.current = current
+ return runes.Copy(o.showItem(current.Value)), true
+}
+
+// Disable the current history
+func (o *opHistory) Disable() {
+ o.enable = false
+}
+
+// Enable the current history
+func (o *opHistory) Enable() {
+ o.enable = true
+}
+
+func (o *opHistory) debug() {
+ Debug("-------")
+ for item := o.history.Front(); item != nil; item = item.Next() {
+ Debug(fmt.Sprintf("%+v", item.Value))
+ }
+}
+
+// save history
+func (o *opHistory) New(current []rune) (err error) {
+
+ // history deactivated
+ if !o.enable {
+ return nil
+ }
+
+ current = runes.Copy(current)
+
+ // if just use last command without modify
+ // just clean lastest history
+ if back := o.history.Back(); back != nil {
+ prev := back.Prev()
+ if prev != nil {
+ if runes.Equal(current, prev.Value.(*hisItem).Source) {
+ o.current = o.history.Back()
+ o.current.Value.(*hisItem).Clean()
+ o.historyVer++
+ return nil
+ }
+ }
+ }
+
+ if len(current) == 0 {
+ o.current = o.history.Back()
+ if o.current != nil {
+ o.current.Value.(*hisItem).Clean()
+ o.historyVer++
+ return nil
+ }
+ }
+
+ if o.current != o.history.Back() {
+ // move history item to current command
+ currentItem := o.current.Value.(*hisItem)
+ // set current to last item
+ o.current = o.history.Back()
+
+ current = runes.Copy(currentItem.Tmp)
+ }
+
+ // err only can be a IO error, just report
+ err = o.Update(current, true)
+
+ // push a new one to commit current command
+ o.historyVer++
+ o.Push(nil)
+ return
+}
+
+func (o *opHistory) Revert() {
+ o.historyVer++
+ o.current = o.history.Back()
+}
+
+func (o *opHistory) Update(s []rune, commit bool) (err error) {
+ o.fdLock.Lock()
+ defer o.fdLock.Unlock()
+ s = runes.Copy(s)
+ if o.current == nil {
+ o.Push(s)
+ o.Compact()
+ return
+ }
+ r := o.current.Value.(*hisItem)
+ r.Version = o.historyVer
+ if commit {
+ r.Source = s
+ if o.fd != nil {
+ // just report the error
+ _, err = o.fd.Write([]byte(string(r.Source) + "\n"))
+ }
+ } else {
+ r.Tmp = append(r.Tmp[:0], s...)
+ }
+ o.current.Value = r
+ o.Compact()
+ return
+}
+
+func (o *opHistory) Push(s []rune) {
+ s = runes.Copy(s)
+ elem := o.history.PushBack(&hisItem{Source: s})
+ o.current = elem
+}
diff --git a/vendor/github.com/chzyer/readline/operation.go b/vendor/github.com/chzyer/readline/operation.go
new file mode 100644
index 00000000..b60939a9
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/operation.go
@@ -0,0 +1,537 @@
+package readline
+
+import (
+ "errors"
+ "io"
+ "sync"
+)
+
+var (
+ ErrInterrupt = errors.New("Interrupt")
+)
+
+type InterruptError struct {
+ Line []rune
+}
+
+func (*InterruptError) Error() string {
+ return "Interrupted"
+}
+
+type Operation struct {
+ m sync.Mutex
+ cfg *Config
+ t *Terminal
+ buf *RuneBuffer
+ outchan chan []rune
+ errchan chan error
+ w io.Writer
+
+ history *opHistory
+ *opSearch
+ *opCompleter
+ *opPassword
+ *opVim
+}
+
+func (o *Operation) SetBuffer(what string) {
+ o.buf.Set([]rune(what))
+}
+
+type wrapWriter struct {
+ r *Operation
+ t *Terminal
+ target io.Writer
+}
+
+func (w *wrapWriter) Write(b []byte) (int, error) {
+ if !w.t.IsReading() {
+ return w.target.Write(b)
+ }
+
+ var (
+ n int
+ err error
+ )
+ w.r.buf.Refresh(func() {
+ n, err = w.target.Write(b)
+ })
+
+ if w.r.IsSearchMode() {
+ w.r.SearchRefresh(-1)
+ }
+ if w.r.IsInCompleteMode() {
+ w.r.CompleteRefresh()
+ }
+ return n, err
+}
+
+func NewOperation(t *Terminal, cfg *Config) *Operation {
+ width := cfg.FuncGetWidth()
+ op := &Operation{
+ t: t,
+ buf: NewRuneBuffer(t, cfg.Prompt, cfg, width),
+ outchan: make(chan []rune),
+ errchan: make(chan error, 1),
+ }
+ op.w = op.buf.w
+ op.SetConfig(cfg)
+ op.opVim = newVimMode(op)
+ op.opCompleter = newOpCompleter(op.buf.w, op, width)
+ op.opPassword = newOpPassword(op)
+ op.cfg.FuncOnWidthChanged(func() {
+ newWidth := cfg.FuncGetWidth()
+ op.opCompleter.OnWidthChange(newWidth)
+ op.opSearch.OnWidthChange(newWidth)
+ op.buf.OnWidthChange(newWidth)
+ })
+ go op.ioloop()
+ return op
+}
+
+func (o *Operation) SetPrompt(s string) {
+ o.buf.SetPrompt(s)
+}
+
+func (o *Operation) SetMaskRune(r rune) {
+ o.buf.SetMask(r)
+}
+
+func (o *Operation) GetConfig() *Config {
+ o.m.Lock()
+ cfg := *o.cfg
+ o.m.Unlock()
+ return &cfg
+}
+
+func (o *Operation) ioloop() {
+ for {
+ keepInSearchMode := false
+ keepInCompleteMode := false
+ r := o.t.ReadRune()
+
+ if o.GetConfig().FuncFilterInputRune != nil {
+ var process bool
+ r, process = o.GetConfig().FuncFilterInputRune(r)
+ if !process {
+ o.t.KickRead()
+ o.buf.Refresh(nil) // to refresh the line
+ continue // ignore this rune
+ }
+ }
+
+ if r == 0 { // io.EOF
+ if o.buf.Len() == 0 {
+ o.buf.Clean()
+ select {
+ case o.errchan <- io.EOF:
+ }
+ break
+ } else {
+ // if stdin got io.EOF and there is something left in buffer,
+ // let's flush them by sending CharEnter.
+ // And we will got io.EOF int next loop.
+ r = CharEnter
+ }
+ }
+ isUpdateHistory := true
+
+ if o.IsInCompleteSelectMode() {
+ keepInCompleteMode = o.HandleCompleteSelect(r)
+ if keepInCompleteMode {
+ continue
+ }
+
+ o.buf.Refresh(nil)
+ switch r {
+ case CharEnter, CharCtrlJ:
+ o.history.Update(o.buf.Runes(), false)
+ fallthrough
+ case CharInterrupt:
+ o.t.KickRead()
+ fallthrough
+ case CharBell:
+ continue
+ }
+ }
+
+ if o.IsEnableVimMode() {
+ r = o.HandleVim(r, o.t.ReadRune)
+ if r == 0 {
+ continue
+ }
+ }
+
+ switch r {
+ case CharBell:
+ if o.IsSearchMode() {
+ o.ExitSearchMode(true)
+ o.buf.Refresh(nil)
+ }
+ if o.IsInCompleteMode() {
+ o.ExitCompleteMode(true)
+ o.buf.Refresh(nil)
+ }
+ case CharTab:
+ if o.GetConfig().AutoComplete == nil {
+ o.t.Bell()
+ break
+ }
+ if o.OnComplete() {
+ keepInCompleteMode = true
+ } else {
+ o.t.Bell()
+ break
+ }
+
+ case CharBckSearch:
+ if !o.SearchMode(S_DIR_BCK) {
+ o.t.Bell()
+ break
+ }
+ keepInSearchMode = true
+ case CharCtrlU:
+ o.buf.KillFront()
+ case CharFwdSearch:
+ if !o.SearchMode(S_DIR_FWD) {
+ o.t.Bell()
+ break
+ }
+ keepInSearchMode = true
+ case CharKill:
+ o.buf.Kill()
+ keepInCompleteMode = true
+ case MetaForward:
+ o.buf.MoveToNextWord()
+ case CharTranspose:
+ o.buf.Transpose()
+ case MetaBackward:
+ o.buf.MoveToPrevWord()
+ case MetaDelete:
+ o.buf.DeleteWord()
+ case CharLineStart:
+ o.buf.MoveToLineStart()
+ case CharLineEnd:
+ o.buf.MoveToLineEnd()
+ case CharBackspace, CharCtrlH:
+ if o.IsSearchMode() {
+ o.SearchBackspace()
+ keepInSearchMode = true
+ break
+ }
+
+ if o.buf.Len() == 0 {
+ o.t.Bell()
+ break
+ }
+ o.buf.Backspace()
+ if o.IsInCompleteMode() {
+ o.OnComplete()
+ }
+ case CharCtrlZ:
+ o.buf.Clean()
+ o.t.SleepToResume()
+ o.Refresh()
+ case CharCtrlL:
+ ClearScreen(o.w)
+ o.Refresh()
+ case MetaBackspace, CharCtrlW:
+ o.buf.BackEscapeWord()
+ case CharCtrlY:
+ o.buf.Yank()
+ case CharEnter, CharCtrlJ:
+ if o.IsSearchMode() {
+ o.ExitSearchMode(false)
+ }
+ o.buf.MoveToLineEnd()
+ var data []rune
+ if !o.GetConfig().UniqueEditLine {
+ o.buf.WriteRune('\n')
+ data = o.buf.Reset()
+ data = data[:len(data)-1] // trim \n
+ } else {
+ o.buf.Clean()
+ data = o.buf.Reset()
+ }
+ o.outchan <- data
+ if !o.GetConfig().DisableAutoSaveHistory {
+ // ignore IO error
+ _ = o.history.New(data)
+ } else {
+ isUpdateHistory = false
+ }
+ case CharBackward:
+ o.buf.MoveBackward()
+ case CharForward:
+ o.buf.MoveForward()
+ case CharPrev:
+ buf := o.history.Prev()
+ if buf != nil {
+ o.buf.Set(buf)
+ } else {
+ o.t.Bell()
+ }
+ case CharNext:
+ buf, ok := o.history.Next()
+ if ok {
+ o.buf.Set(buf)
+ } else {
+ o.t.Bell()
+ }
+ case CharDelete:
+ if o.buf.Len() > 0 || !o.IsNormalMode() {
+ o.t.KickRead()
+ if !o.buf.Delete() {
+ o.t.Bell()
+ }
+ break
+ }
+
+ // treat as EOF
+ if !o.GetConfig().UniqueEditLine {
+ o.buf.WriteString(o.GetConfig().EOFPrompt + "\n")
+ }
+ o.buf.Reset()
+ isUpdateHistory = false
+ o.history.Revert()
+ o.errchan <- io.EOF
+ if o.GetConfig().UniqueEditLine {
+ o.buf.Clean()
+ }
+ case CharInterrupt:
+ if o.IsSearchMode() {
+ o.t.KickRead()
+ o.ExitSearchMode(true)
+ break
+ }
+ if o.IsInCompleteMode() {
+ o.t.KickRead()
+ o.ExitCompleteMode(true)
+ o.buf.Refresh(nil)
+ break
+ }
+ o.buf.MoveToLineEnd()
+ o.buf.Refresh(nil)
+ hint := o.GetConfig().InterruptPrompt + "\n"
+ if !o.GetConfig().UniqueEditLine {
+ o.buf.WriteString(hint)
+ }
+ remain := o.buf.Reset()
+ if !o.GetConfig().UniqueEditLine {
+ remain = remain[:len(remain)-len([]rune(hint))]
+ }
+ isUpdateHistory = false
+ o.history.Revert()
+ o.errchan <- &InterruptError{remain}
+ default:
+ if o.IsSearchMode() {
+ o.SearchChar(r)
+ keepInSearchMode = true
+ break
+ }
+ o.buf.WriteRune(r)
+ if o.IsInCompleteMode() {
+ o.OnComplete()
+ keepInCompleteMode = true
+ }
+ }
+
+ listener := o.GetConfig().Listener
+ if listener != nil {
+ newLine, newPos, ok := listener.OnChange(o.buf.Runes(), o.buf.Pos(), r)
+ if ok {
+ o.buf.SetWithIdx(newPos, newLine)
+ }
+ }
+
+ o.m.Lock()
+ if !keepInSearchMode && o.IsSearchMode() {
+ o.ExitSearchMode(false)
+ o.buf.Refresh(nil)
+ } else if o.IsInCompleteMode() {
+ if !keepInCompleteMode {
+ o.ExitCompleteMode(false)
+ o.Refresh()
+ } else {
+ o.buf.Refresh(nil)
+ o.CompleteRefresh()
+ }
+ }
+ if isUpdateHistory && !o.IsSearchMode() {
+ // it will cause null history
+ o.history.Update(o.buf.Runes(), false)
+ }
+ o.m.Unlock()
+ }
+}
+
+func (o *Operation) Stderr() io.Writer {
+ return &wrapWriter{target: o.GetConfig().Stderr, r: o, t: o.t}
+}
+
+func (o *Operation) Stdout() io.Writer {
+ return &wrapWriter{target: o.GetConfig().Stdout, r: o, t: o.t}
+}
+
+func (o *Operation) String() (string, error) {
+ r, err := o.Runes()
+ return string(r), err
+}
+
+func (o *Operation) Runes() ([]rune, error) {
+ o.t.EnterRawMode()
+ defer o.t.ExitRawMode()
+
+ listener := o.GetConfig().Listener
+ if listener != nil {
+ listener.OnChange(nil, 0, 0)
+ }
+
+ o.buf.Refresh(nil) // print prompt
+ o.t.KickRead()
+ select {
+ case r := <-o.outchan:
+ return r, nil
+ case err := <-o.errchan:
+ if e, ok := err.(*InterruptError); ok {
+ return e.Line, ErrInterrupt
+ }
+ return nil, err
+ }
+}
+
+func (o *Operation) PasswordEx(prompt string, l Listener) ([]byte, error) {
+ cfg := o.GenPasswordConfig()
+ cfg.Prompt = prompt
+ cfg.Listener = l
+ return o.PasswordWithConfig(cfg)
+}
+
+func (o *Operation) GenPasswordConfig() *Config {
+ return o.opPassword.PasswordConfig()
+}
+
+func (o *Operation) PasswordWithConfig(cfg *Config) ([]byte, error) {
+ if err := o.opPassword.EnterPasswordMode(cfg); err != nil {
+ return nil, err
+ }
+ defer o.opPassword.ExitPasswordMode()
+ return o.Slice()
+}
+
+func (o *Operation) Password(prompt string) ([]byte, error) {
+ return o.PasswordEx(prompt, nil)
+}
+
+func (o *Operation) SetTitle(t string) {
+ o.w.Write([]byte("\033[2;" + t + "\007"))
+}
+
+func (o *Operation) Slice() ([]byte, error) {
+ r, err := o.Runes()
+ if err != nil {
+ return nil, err
+ }
+ return []byte(string(r)), nil
+}
+
+func (o *Operation) Close() {
+ select {
+ case o.errchan <- io.EOF:
+ default:
+ }
+ o.history.Close()
+}
+
+func (o *Operation) SetHistoryPath(path string) {
+ if o.history != nil {
+ o.history.Close()
+ }
+ o.cfg.HistoryFile = path
+ o.history = newOpHistory(o.cfg)
+}
+
+func (o *Operation) IsNormalMode() bool {
+ return !o.IsInCompleteMode() && !o.IsSearchMode()
+}
+
+func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
+ op.m.Lock()
+ defer op.m.Unlock()
+ if op.cfg == cfg {
+ return op.cfg, nil
+ }
+ if err := cfg.Init(); err != nil {
+ return op.cfg, err
+ }
+ old := op.cfg
+ op.cfg = cfg
+ op.SetPrompt(cfg.Prompt)
+ op.SetMaskRune(cfg.MaskRune)
+ op.buf.SetConfig(cfg)
+ width := op.cfg.FuncGetWidth()
+
+ if cfg.opHistory == nil {
+ op.SetHistoryPath(cfg.HistoryFile)
+ cfg.opHistory = op.history
+ cfg.opSearch = newOpSearch(op.buf.w, op.buf, op.history, cfg, width)
+ }
+ op.history = cfg.opHistory
+
+ // SetHistoryPath will close opHistory which already exists
+ // so if we use it next time, we need to reopen it by `InitHistory()`
+ op.history.Init()
+
+ if op.cfg.AutoComplete != nil {
+ op.opCompleter = newOpCompleter(op.buf.w, op, width)
+ }
+
+ op.opSearch = cfg.opSearch
+ return old, nil
+}
+
+func (o *Operation) ResetHistory() {
+ o.history.Reset()
+}
+
+// if err is not nil, it just mean it fail to write to file
+// other things goes fine.
+func (o *Operation) SaveHistory(content string) error {
+ return o.history.New([]rune(content))
+}
+
+func (o *Operation) Refresh() {
+ if o.t.IsReading() {
+ o.buf.Refresh(nil)
+ }
+}
+
+func (o *Operation) Clean() {
+ o.buf.Clean()
+}
+
+func FuncListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) Listener {
+ return &DumpListener{f: f}
+}
+
+type DumpListener struct {
+ f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)
+}
+
+func (d *DumpListener) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
+ return d.f(line, pos, key)
+}
+
+type Listener interface {
+ OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)
+}
+
+type Painter interface {
+ Paint(line []rune, pos int) []rune
+}
+
+type defaultPainter struct{}
+
+func (p *defaultPainter) Paint(line []rune, _ int) []rune {
+ return line
+}
diff --git a/vendor/github.com/chzyer/readline/password.go b/vendor/github.com/chzyer/readline/password.go
new file mode 100644
index 00000000..414288c2
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/password.go
@@ -0,0 +1,33 @@
+package readline
+
+type opPassword struct {
+ o *Operation
+ backupCfg *Config
+}
+
+func newOpPassword(o *Operation) *opPassword {
+ return &opPassword{o: o}
+}
+
+func (o *opPassword) ExitPasswordMode() {
+ o.o.SetConfig(o.backupCfg)
+ o.backupCfg = nil
+}
+
+func (o *opPassword) EnterPasswordMode(cfg *Config) (err error) {
+ o.backupCfg, err = o.o.SetConfig(cfg)
+ return
+}
+
+func (o *opPassword) PasswordConfig() *Config {
+ return &Config{
+ EnableMask: true,
+ InterruptPrompt: "\n",
+ EOFPrompt: "\n",
+ HistoryLimit: -1,
+ Painter: &defaultPainter{},
+
+ Stdout: o.o.cfg.Stdout,
+ Stderr: o.o.cfg.Stderr,
+ }
+}
diff --git a/vendor/github.com/chzyer/readline/rawreader_windows.go b/vendor/github.com/chzyer/readline/rawreader_windows.go
new file mode 100644
index 00000000..073ef150
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/rawreader_windows.go
@@ -0,0 +1,125 @@
+// +build windows
+
+package readline
+
+import "unsafe"
+
+const (
+ VK_CANCEL = 0x03
+ VK_BACK = 0x08
+ VK_TAB = 0x09
+ VK_RETURN = 0x0D
+ VK_SHIFT = 0x10
+ VK_CONTROL = 0x11
+ VK_MENU = 0x12
+ VK_ESCAPE = 0x1B
+ VK_LEFT = 0x25
+ VK_UP = 0x26
+ VK_RIGHT = 0x27
+ VK_DOWN = 0x28
+ VK_DELETE = 0x2E
+ VK_LSHIFT = 0xA0
+ VK_RSHIFT = 0xA1
+ VK_LCONTROL = 0xA2
+ VK_RCONTROL = 0xA3
+)
+
+// RawReader translate input record to ANSI escape sequence.
+// To provides same behavior as unix terminal.
+type RawReader struct {
+ ctrlKey bool
+ altKey bool
+}
+
+func NewRawReader() *RawReader {
+ r := new(RawReader)
+ return r
+}
+
+// only process one action in one read
+func (r *RawReader) Read(buf []byte) (int, error) {
+ ir := new(_INPUT_RECORD)
+ var read int
+ var err error
+next:
+ err = kernel.ReadConsoleInputW(stdin,
+ uintptr(unsafe.Pointer(ir)),
+ 1,
+ uintptr(unsafe.Pointer(&read)),
+ )
+ if err != nil {
+ return 0, err
+ }
+ if ir.EventType != EVENT_KEY {
+ goto next
+ }
+ ker := (*_KEY_EVENT_RECORD)(unsafe.Pointer(&ir.Event[0]))
+ if ker.bKeyDown == 0 { // keyup
+ if r.ctrlKey || r.altKey {
+ switch ker.wVirtualKeyCode {
+ case VK_RCONTROL, VK_LCONTROL:
+ r.ctrlKey = false
+ case VK_MENU: //alt
+ r.altKey = false
+ }
+ }
+ goto next
+ }
+
+ if ker.unicodeChar == 0 {
+ var target rune
+ switch ker.wVirtualKeyCode {
+ case VK_RCONTROL, VK_LCONTROL:
+ r.ctrlKey = true
+ case VK_MENU: //alt
+ r.altKey = true
+ case VK_LEFT:
+ target = CharBackward
+ case VK_RIGHT:
+ target = CharForward
+ case VK_UP:
+ target = CharPrev
+ case VK_DOWN:
+ target = CharNext
+ }
+ if target != 0 {
+ return r.write(buf, target)
+ }
+ goto next
+ }
+ char := rune(ker.unicodeChar)
+ if r.ctrlKey {
+ switch char {
+ case 'A':
+ char = CharLineStart
+ case 'E':
+ char = CharLineEnd
+ case 'R':
+ char = CharBckSearch
+ case 'S':
+ char = CharFwdSearch
+ }
+ } else if r.altKey {
+ switch char {
+ case VK_BACK:
+ char = CharBackspace
+ }
+ return r.writeEsc(buf, char)
+ }
+ return r.write(buf, char)
+}
+
+func (r *RawReader) writeEsc(b []byte, char rune) (int, error) {
+ b[0] = '\033'
+ n := copy(b[1:], []byte(string(char)))
+ return n + 1, nil
+}
+
+func (r *RawReader) write(b []byte, char rune) (int, error) {
+ n := copy(b, []byte(string(char)))
+ return n, nil
+}
+
+func (r *RawReader) Close() error {
+ return nil
+}
diff --git a/vendor/github.com/chzyer/readline/readline.go b/vendor/github.com/chzyer/readline/readline.go
new file mode 100644
index 00000000..63b91710
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/readline.go
@@ -0,0 +1,338 @@
+// Readline is a pure go implementation for GNU-Readline kind library.
+//
+// example:
+// rl, err := readline.New("> ")
+// if err != nil {
+// panic(err)
+// }
+// defer rl.Close()
+//
+// for {
+// line, err := rl.Readline()
+// if err != nil { // io.EOF
+// break
+// }
+// println(line)
+// }
+//
+package readline
+
+import (
+ "io"
+)
+
+type Instance struct {
+ Config *Config
+ Terminal *Terminal
+ Operation *Operation
+}
+
+type Config struct {
+ // prompt supports ANSI escape sequence, so we can color some characters even in windows
+ Prompt string
+
+ // readline will persist historys to file where HistoryFile specified
+ HistoryFile string
+ // specify the max length of historys, it's 500 by default, set it to -1 to disable history
+ HistoryLimit int
+ DisableAutoSaveHistory bool
+ // enable case-insensitive history searching
+ HistorySearchFold bool
+
+ // AutoCompleter will called once user press TAB
+ AutoComplete AutoCompleter
+
+ // Any key press will pass to Listener
+ // NOTE: Listener will be triggered by (nil, 0, 0) immediately
+ Listener Listener
+
+ Painter Painter
+
+ // If VimMode is true, readline will in vim.insert mode by default
+ VimMode bool
+
+ InterruptPrompt string
+ EOFPrompt string
+
+ FuncGetWidth func() int
+
+ Stdin io.ReadCloser
+ StdinWriter io.Writer
+ Stdout io.Writer
+ Stderr io.Writer
+
+ EnableMask bool
+ MaskRune rune
+
+ // erase the editing line after user submited it
+ // it use in IM usually.
+ UniqueEditLine bool
+
+ // filter input runes (may be used to disable CtrlZ or for translating some keys to different actions)
+ // -> output = new (translated) rune and true/false if continue with processing this one
+ FuncFilterInputRune func(rune) (rune, bool)
+
+ // force use interactive even stdout is not a tty
+ FuncIsTerminal func() bool
+ FuncMakeRaw func() error
+ FuncExitRaw func() error
+ FuncOnWidthChanged func(func())
+ ForceUseInteractive bool
+
+ // private fields
+ inited bool
+ opHistory *opHistory
+ opSearch *opSearch
+}
+
+func (c *Config) useInteractive() bool {
+ if c.ForceUseInteractive {
+ return true
+ }
+ return c.FuncIsTerminal()
+}
+
+func (c *Config) Init() error {
+ if c.inited {
+ return nil
+ }
+ c.inited = true
+ if c.Stdin == nil {
+ c.Stdin = NewCancelableStdin(Stdin)
+ }
+
+ c.Stdin, c.StdinWriter = NewFillableStdin(c.Stdin)
+
+ if c.Stdout == nil {
+ c.Stdout = Stdout
+ }
+ if c.Stderr == nil {
+ c.Stderr = Stderr
+ }
+ if c.HistoryLimit == 0 {
+ c.HistoryLimit = 500
+ }
+
+ if c.InterruptPrompt == "" {
+ c.InterruptPrompt = "^C"
+ } else if c.InterruptPrompt == "\n" {
+ c.InterruptPrompt = ""
+ }
+ if c.EOFPrompt == "" {
+ c.EOFPrompt = "^D"
+ } else if c.EOFPrompt == "\n" {
+ c.EOFPrompt = ""
+ }
+
+ if c.AutoComplete == nil {
+ c.AutoComplete = &TabCompleter{}
+ }
+ if c.FuncGetWidth == nil {
+ c.FuncGetWidth = GetScreenWidth
+ }
+ if c.FuncIsTerminal == nil {
+ c.FuncIsTerminal = DefaultIsTerminal
+ }
+ rm := new(RawMode)
+ if c.FuncMakeRaw == nil {
+ c.FuncMakeRaw = rm.Enter
+ }
+ if c.FuncExitRaw == nil {
+ c.FuncExitRaw = rm.Exit
+ }
+ if c.FuncOnWidthChanged == nil {
+ c.FuncOnWidthChanged = DefaultOnWidthChanged
+ }
+
+ return nil
+}
+
+func (c Config) Clone() *Config {
+ c.opHistory = nil
+ c.opSearch = nil
+ return &c
+}
+
+func (c *Config) SetListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) {
+ c.Listener = FuncListener(f)
+}
+
+func (c *Config) SetPainter(p Painter) {
+ c.Painter = p
+}
+
+func NewEx(cfg *Config) (*Instance, error) {
+ t, err := NewTerminal(cfg)
+ if err != nil {
+ return nil, err
+ }
+ rl := t.Readline()
+ if cfg.Painter == nil {
+ cfg.Painter = &defaultPainter{}
+ }
+ return &Instance{
+ Config: cfg,
+ Terminal: t,
+ Operation: rl,
+ }, nil
+}
+
+func New(prompt string) (*Instance, error) {
+ return NewEx(&Config{Prompt: prompt})
+}
+
+func (i *Instance) ResetHistory() {
+ i.Operation.ResetHistory()
+}
+
+func (i *Instance) SetPrompt(s string) {
+ i.Operation.SetPrompt(s)
+}
+
+func (i *Instance) SetMaskRune(r rune) {
+ i.Operation.SetMaskRune(r)
+}
+
+// change history persistence in runtime
+func (i *Instance) SetHistoryPath(p string) {
+ i.Operation.SetHistoryPath(p)
+}
+
+// readline will refresh automatic when write through Stdout()
+func (i *Instance) Stdout() io.Writer {
+ return i.Operation.Stdout()
+}
+
+// readline will refresh automatic when write through Stdout()
+func (i *Instance) Stderr() io.Writer {
+ return i.Operation.Stderr()
+}
+
+// switch VimMode in runtime
+func (i *Instance) SetVimMode(on bool) {
+ i.Operation.SetVimMode(on)
+}
+
+func (i *Instance) IsVimMode() bool {
+ return i.Operation.IsEnableVimMode()
+}
+
+func (i *Instance) GenPasswordConfig() *Config {
+ return i.Operation.GenPasswordConfig()
+}
+
+// we can generate a config by `i.GenPasswordConfig()`
+func (i *Instance) ReadPasswordWithConfig(cfg *Config) ([]byte, error) {
+ return i.Operation.PasswordWithConfig(cfg)
+}
+
+func (i *Instance) ReadPasswordEx(prompt string, l Listener) ([]byte, error) {
+ return i.Operation.PasswordEx(prompt, l)
+}
+
+func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
+ return i.Operation.Password(prompt)
+}
+
+type Result struct {
+ Line string
+ Error error
+}
+
+func (l *Result) CanContinue() bool {
+ return len(l.Line) != 0 && l.Error == ErrInterrupt
+}
+
+func (l *Result) CanBreak() bool {
+ return !l.CanContinue() && l.Error != nil
+}
+
+func (i *Instance) Line() *Result {
+ ret, err := i.Readline()
+ return &Result{ret, err}
+}
+
+// err is one of (nil, io.EOF, readline.ErrInterrupt)
+func (i *Instance) Readline() (string, error) {
+ return i.Operation.String()
+}
+
+func (i *Instance) ReadlineWithDefault(what string) (string, error) {
+ i.Operation.SetBuffer(what)
+ return i.Operation.String()
+}
+
+func (i *Instance) SaveHistory(content string) error {
+ return i.Operation.SaveHistory(content)
+}
+
+// same as readline
+func (i *Instance) ReadSlice() ([]byte, error) {
+ return i.Operation.Slice()
+}
+
+// we must make sure that call Close() before process exit.
+// if there has a pending reading operation, that reading will be interrupted.
+// so you can capture the signal and call Instance.Close(), it's thread-safe.
+func (i *Instance) Close() error {
+ i.Config.Stdin.Close()
+ i.Operation.Close()
+ if err := i.Terminal.Close(); err != nil {
+ return err
+ }
+ return nil
+}
+
+// call CaptureExitSignal when you want readline exit gracefully.
+func (i *Instance) CaptureExitSignal() {
+ CaptureExitSignal(func() {
+ i.Close()
+ })
+}
+
+func (i *Instance) Clean() {
+ i.Operation.Clean()
+}
+
+func (i *Instance) Write(b []byte) (int, error) {
+ return i.Stdout().Write(b)
+}
+
+// WriteStdin prefill the next Stdin fetch
+// Next time you call ReadLine() this value will be writen before the user input
+// ie :
+// i := readline.New()
+// i.WriteStdin([]byte("test"))
+// _, _= i.Readline()
+//
+// gives
+//
+// > test[cursor]
+func (i *Instance) WriteStdin(val []byte) (int, error) {
+ return i.Terminal.WriteStdin(val)
+}
+
+func (i *Instance) SetConfig(cfg *Config) *Config {
+ if i.Config == cfg {
+ return cfg
+ }
+ old := i.Config
+ i.Config = cfg
+ i.Operation.SetConfig(cfg)
+ i.Terminal.SetConfig(cfg)
+ return old
+}
+
+func (i *Instance) Refresh() {
+ i.Operation.Refresh()
+}
+
+// HistoryDisable the save of the commands into the history
+func (i *Instance) HistoryDisable() {
+ i.Operation.history.Disable()
+}
+
+// HistoryEnable the save of the commands into the history (default on)
+func (i *Instance) HistoryEnable() {
+ i.Operation.history.Enable()
+}
diff --git a/vendor/github.com/chzyer/readline/remote.go b/vendor/github.com/chzyer/readline/remote.go
new file mode 100644
index 00000000..74dbf569
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/remote.go
@@ -0,0 +1,475 @@
+package readline
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net"
+ "os"
+ "sync"
+ "sync/atomic"
+)
+
+type MsgType int16
+
+const (
+ T_DATA = MsgType(iota)
+ T_WIDTH
+ T_WIDTH_REPORT
+ T_ISTTY_REPORT
+ T_RAW
+ T_ERAW // exit raw
+ T_EOF
+)
+
+type RemoteSvr struct {
+ eof int32
+ closed int32
+ width int32
+ reciveChan chan struct{}
+ writeChan chan *writeCtx
+ conn net.Conn
+ isTerminal bool
+ funcWidthChan func()
+ stopChan chan struct{}
+
+ dataBufM sync.Mutex
+ dataBuf bytes.Buffer
+}
+
+type writeReply struct {
+ n int
+ err error
+}
+
+type writeCtx struct {
+ msg *Message
+ reply chan *writeReply
+}
+
+func newWriteCtx(msg *Message) *writeCtx {
+ return &writeCtx{
+ msg: msg,
+ reply: make(chan *writeReply),
+ }
+}
+
+func NewRemoteSvr(conn net.Conn) (*RemoteSvr, error) {
+ rs := &RemoteSvr{
+ width: -1,
+ conn: conn,
+ writeChan: make(chan *writeCtx),
+ reciveChan: make(chan struct{}),
+ stopChan: make(chan struct{}),
+ }
+ buf := bufio.NewReader(rs.conn)
+
+ if err := rs.init(buf); err != nil {
+ return nil, err
+ }
+
+ go rs.readLoop(buf)
+ go rs.writeLoop()
+ return rs, nil
+}
+
+func (r *RemoteSvr) init(buf *bufio.Reader) error {
+ m, err := ReadMessage(buf)
+ if err != nil {
+ return err
+ }
+ // receive isTerminal
+ if m.Type != T_ISTTY_REPORT {
+ return fmt.Errorf("unexpected init message")
+ }
+ r.GotIsTerminal(m.Data)
+
+ // receive width
+ m, err = ReadMessage(buf)
+ if err != nil {
+ return err
+ }
+ if m.Type != T_WIDTH_REPORT {
+ return fmt.Errorf("unexpected init message")
+ }
+ r.GotReportWidth(m.Data)
+
+ return nil
+}
+
+func (r *RemoteSvr) HandleConfig(cfg *Config) {
+ cfg.Stderr = r
+ cfg.Stdout = r
+ cfg.Stdin = r
+ cfg.FuncExitRaw = r.ExitRawMode
+ cfg.FuncIsTerminal = r.IsTerminal
+ cfg.FuncMakeRaw = r.EnterRawMode
+ cfg.FuncExitRaw = r.ExitRawMode
+ cfg.FuncGetWidth = r.GetWidth
+ cfg.FuncOnWidthChanged = func(f func()) {
+ r.funcWidthChan = f
+ }
+}
+
+func (r *RemoteSvr) IsTerminal() bool {
+ return r.isTerminal
+}
+
+func (r *RemoteSvr) checkEOF() error {
+ if atomic.LoadInt32(&r.eof) == 1 {
+ return io.EOF
+ }
+ return nil
+}
+
+func (r *RemoteSvr) Read(b []byte) (int, error) {
+ r.dataBufM.Lock()
+ n, err := r.dataBuf.Read(b)
+ r.dataBufM.Unlock()
+ if n == 0 {
+ if err := r.checkEOF(); err != nil {
+ return 0, err
+ }
+ }
+
+ if n == 0 && err == io.EOF {
+ <-r.reciveChan
+ r.dataBufM.Lock()
+ n, err = r.dataBuf.Read(b)
+ r.dataBufM.Unlock()
+ }
+ if n == 0 {
+ if err := r.checkEOF(); err != nil {
+ return 0, err
+ }
+ }
+
+ return n, err
+}
+
+func (r *RemoteSvr) writeMsg(m *Message) error {
+ ctx := newWriteCtx(m)
+ r.writeChan <- ctx
+ reply := <-ctx.reply
+ return reply.err
+}
+
+func (r *RemoteSvr) Write(b []byte) (int, error) {
+ ctx := newWriteCtx(NewMessage(T_DATA, b))
+ r.writeChan <- ctx
+ reply := <-ctx.reply
+ return reply.n, reply.err
+}
+
+func (r *RemoteSvr) EnterRawMode() error {
+ return r.writeMsg(NewMessage(T_RAW, nil))
+}
+
+func (r *RemoteSvr) ExitRawMode() error {
+ return r.writeMsg(NewMessage(T_ERAW, nil))
+}
+
+func (r *RemoteSvr) writeLoop() {
+ defer r.Close()
+
+loop:
+ for {
+ select {
+ case ctx, ok := <-r.writeChan:
+ if !ok {
+ break
+ }
+ n, err := ctx.msg.WriteTo(r.conn)
+ ctx.reply <- &writeReply{n, err}
+ case <-r.stopChan:
+ break loop
+ }
+ }
+}
+
+func (r *RemoteSvr) Close() error {
+ if atomic.CompareAndSwapInt32(&r.closed, 0, 1) {
+ close(r.stopChan)
+ r.conn.Close()
+ }
+ return nil
+}
+
+func (r *RemoteSvr) readLoop(buf *bufio.Reader) {
+ defer r.Close()
+ for {
+ m, err := ReadMessage(buf)
+ if err != nil {
+ break
+ }
+ switch m.Type {
+ case T_EOF:
+ atomic.StoreInt32(&r.eof, 1)
+ select {
+ case r.reciveChan <- struct{}{}:
+ default:
+ }
+ case T_DATA:
+ r.dataBufM.Lock()
+ r.dataBuf.Write(m.Data)
+ r.dataBufM.Unlock()
+ select {
+ case r.reciveChan <- struct{}{}:
+ default:
+ }
+ case T_WIDTH_REPORT:
+ r.GotReportWidth(m.Data)
+ case T_ISTTY_REPORT:
+ r.GotIsTerminal(m.Data)
+ }
+ }
+}
+
+func (r *RemoteSvr) GotIsTerminal(data []byte) {
+ if binary.BigEndian.Uint16(data) == 0 {
+ r.isTerminal = false
+ } else {
+ r.isTerminal = true
+ }
+}
+
+func (r *RemoteSvr) GotReportWidth(data []byte) {
+ atomic.StoreInt32(&r.width, int32(binary.BigEndian.Uint16(data)))
+ if r.funcWidthChan != nil {
+ r.funcWidthChan()
+ }
+}
+
+func (r *RemoteSvr) GetWidth() int {
+ return int(atomic.LoadInt32(&r.width))
+}
+
+// -----------------------------------------------------------------------------
+
+type Message struct {
+ Type MsgType
+ Data []byte
+}
+
+func ReadMessage(r io.Reader) (*Message, error) {
+ m := new(Message)
+ var length int32
+ if err := binary.Read(r, binary.BigEndian, &length); err != nil {
+ return nil, err
+ }
+ if err := binary.Read(r, binary.BigEndian, &m.Type); err != nil {
+ return nil, err
+ }
+ m.Data = make([]byte, int(length)-2)
+ if _, err := io.ReadFull(r, m.Data); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func NewMessage(t MsgType, data []byte) *Message {
+ return &Message{t, data}
+}
+
+func (m *Message) WriteTo(w io.Writer) (int, error) {
+ buf := bytes.NewBuffer(make([]byte, 0, len(m.Data)+2+4))
+ binary.Write(buf, binary.BigEndian, int32(len(m.Data)+2))
+ binary.Write(buf, binary.BigEndian, m.Type)
+ buf.Write(m.Data)
+ n, err := buf.WriteTo(w)
+ return int(n), err
+}
+
+// -----------------------------------------------------------------------------
+
+type RemoteCli struct {
+ conn net.Conn
+ raw RawMode
+ receiveChan chan struct{}
+ inited int32
+ isTerminal *bool
+
+ data bytes.Buffer
+ dataM sync.Mutex
+}
+
+func NewRemoteCli(conn net.Conn) (*RemoteCli, error) {
+ r := &RemoteCli{
+ conn: conn,
+ receiveChan: make(chan struct{}),
+ }
+ return r, nil
+}
+
+func (r *RemoteCli) MarkIsTerminal(is bool) {
+ r.isTerminal = &is
+}
+
+func (r *RemoteCli) init() error {
+ if !atomic.CompareAndSwapInt32(&r.inited, 0, 1) {
+ return nil
+ }
+
+ if err := r.reportIsTerminal(); err != nil {
+ return err
+ }
+
+ if err := r.reportWidth(); err != nil {
+ return err
+ }
+
+ // register sig for width changed
+ DefaultOnWidthChanged(func() {
+ r.reportWidth()
+ })
+ return nil
+}
+
+func (r *RemoteCli) writeMsg(m *Message) error {
+ r.dataM.Lock()
+ _, err := m.WriteTo(r.conn)
+ r.dataM.Unlock()
+ return err
+}
+
+func (r *RemoteCli) Write(b []byte) (int, error) {
+ m := NewMessage(T_DATA, b)
+ r.dataM.Lock()
+ _, err := m.WriteTo(r.conn)
+ r.dataM.Unlock()
+ return len(b), err
+}
+
+func (r *RemoteCli) reportWidth() error {
+ screenWidth := GetScreenWidth()
+ data := make([]byte, 2)
+ binary.BigEndian.PutUint16(data, uint16(screenWidth))
+ msg := NewMessage(T_WIDTH_REPORT, data)
+
+ if err := r.writeMsg(msg); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (r *RemoteCli) reportIsTerminal() error {
+ var isTerminal bool
+ if r.isTerminal != nil {
+ isTerminal = *r.isTerminal
+ } else {
+ isTerminal = DefaultIsTerminal()
+ }
+ data := make([]byte, 2)
+ if isTerminal {
+ binary.BigEndian.PutUint16(data, 1)
+ } else {
+ binary.BigEndian.PutUint16(data, 0)
+ }
+ msg := NewMessage(T_ISTTY_REPORT, data)
+ if err := r.writeMsg(msg); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (r *RemoteCli) readLoop() {
+ buf := bufio.NewReader(r.conn)
+ for {
+ msg, err := ReadMessage(buf)
+ if err != nil {
+ break
+ }
+ switch msg.Type {
+ case T_ERAW:
+ r.raw.Exit()
+ case T_RAW:
+ r.raw.Enter()
+ case T_DATA:
+ os.Stdout.Write(msg.Data)
+ }
+ }
+}
+
+func (r *RemoteCli) ServeBy(source io.Reader) error {
+ if err := r.init(); err != nil {
+ return err
+ }
+
+ go func() {
+ defer r.Close()
+ for {
+ n, _ := io.Copy(r, source)
+ if n == 0 {
+ break
+ }
+ }
+ }()
+ defer r.raw.Exit()
+ r.readLoop()
+ return nil
+}
+
+func (r *RemoteCli) Close() {
+ r.writeMsg(NewMessage(T_EOF, nil))
+}
+
+func (r *RemoteCli) Serve() error {
+ return r.ServeBy(os.Stdin)
+}
+
+func ListenRemote(n, addr string, cfg *Config, h func(*Instance), onListen ...func(net.Listener) error) error {
+ ln, err := net.Listen(n, addr)
+ if err != nil {
+ return err
+ }
+ if len(onListen) > 0 {
+ if err := onListen[0](ln); err != nil {
+ return err
+ }
+ }
+ for {
+ conn, err := ln.Accept()
+ if err != nil {
+ break
+ }
+ go func() {
+ defer conn.Close()
+ rl, err := HandleConn(*cfg, conn)
+ if err != nil {
+ return
+ }
+ h(rl)
+ }()
+ }
+ return nil
+}
+
+func HandleConn(cfg Config, conn net.Conn) (*Instance, error) {
+ r, err := NewRemoteSvr(conn)
+ if err != nil {
+ return nil, err
+ }
+ r.HandleConfig(&cfg)
+
+ rl, err := NewEx(&cfg)
+ if err != nil {
+ return nil, err
+ }
+ return rl, nil
+}
+
+func DialRemote(n, addr string) error {
+ conn, err := net.Dial(n, addr)
+ if err != nil {
+ return err
+ }
+ defer conn.Close()
+
+ cli, err := NewRemoteCli(conn)
+ if err != nil {
+ return err
+ }
+ return cli.Serve()
+}
diff --git a/vendor/github.com/chzyer/readline/runebuf.go b/vendor/github.com/chzyer/readline/runebuf.go
new file mode 100644
index 00000000..d95df1e3
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/runebuf.go
@@ -0,0 +1,629 @@
+package readline
+
+import (
+ "bufio"
+ "bytes"
+ "io"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+type runeBufferBck struct {
+ buf []rune
+ idx int
+}
+
+type RuneBuffer struct {
+ buf []rune
+ idx int
+ prompt []rune
+ w io.Writer
+
+ hadClean bool
+ interactive bool
+ cfg *Config
+
+ width int
+
+ bck *runeBufferBck
+
+ offset string
+
+ lastKill []rune
+
+ sync.Mutex
+}
+
+func (r *RuneBuffer) pushKill(text []rune) {
+ r.lastKill = append([]rune{}, text...)
+}
+
+func (r *RuneBuffer) OnWidthChange(newWidth int) {
+ r.Lock()
+ r.width = newWidth
+ r.Unlock()
+}
+
+func (r *RuneBuffer) Backup() {
+ r.Lock()
+ r.bck = &runeBufferBck{r.buf, r.idx}
+ r.Unlock()
+}
+
+func (r *RuneBuffer) Restore() {
+ r.Refresh(func() {
+ if r.bck == nil {
+ return
+ }
+ r.buf = r.bck.buf
+ r.idx = r.bck.idx
+ })
+}
+
+func NewRuneBuffer(w io.Writer, prompt string, cfg *Config, width int) *RuneBuffer {
+ rb := &RuneBuffer{
+ w: w,
+ interactive: cfg.useInteractive(),
+ cfg: cfg,
+ width: width,
+ }
+ rb.SetPrompt(prompt)
+ return rb
+}
+
+func (r *RuneBuffer) SetConfig(cfg *Config) {
+ r.Lock()
+ r.cfg = cfg
+ r.interactive = cfg.useInteractive()
+ r.Unlock()
+}
+
+func (r *RuneBuffer) SetMask(m rune) {
+ r.Lock()
+ r.cfg.MaskRune = m
+ r.Unlock()
+}
+
+func (r *RuneBuffer) CurrentWidth(x int) int {
+ r.Lock()
+ defer r.Unlock()
+ return runes.WidthAll(r.buf[:x])
+}
+
+func (r *RuneBuffer) PromptLen() int {
+ r.Lock()
+ width := r.promptLen()
+ r.Unlock()
+ return width
+}
+
+func (r *RuneBuffer) promptLen() int {
+ return runes.WidthAll(runes.ColorFilter(r.prompt))
+}
+
+func (r *RuneBuffer) RuneSlice(i int) []rune {
+ r.Lock()
+ defer r.Unlock()
+
+ if i > 0 {
+ rs := make([]rune, i)
+ copy(rs, r.buf[r.idx:r.idx+i])
+ return rs
+ }
+ rs := make([]rune, -i)
+ copy(rs, r.buf[r.idx+i:r.idx])
+ return rs
+}
+
+func (r *RuneBuffer) Runes() []rune {
+ r.Lock()
+ newr := make([]rune, len(r.buf))
+ copy(newr, r.buf)
+ r.Unlock()
+ return newr
+}
+
+func (r *RuneBuffer) Pos() int {
+ r.Lock()
+ defer r.Unlock()
+ return r.idx
+}
+
+func (r *RuneBuffer) Len() int {
+ r.Lock()
+ defer r.Unlock()
+ return len(r.buf)
+}
+
+func (r *RuneBuffer) MoveToLineStart() {
+ r.Refresh(func() {
+ if r.idx == 0 {
+ return
+ }
+ r.idx = 0
+ })
+}
+
+func (r *RuneBuffer) MoveBackward() {
+ r.Refresh(func() {
+ if r.idx == 0 {
+ return
+ }
+ r.idx--
+ })
+}
+
+func (r *RuneBuffer) WriteString(s string) {
+ r.WriteRunes([]rune(s))
+}
+
+func (r *RuneBuffer) WriteRune(s rune) {
+ r.WriteRunes([]rune{s})
+}
+
+func (r *RuneBuffer) WriteRunes(s []rune) {
+ r.Refresh(func() {
+ tail := append(s, r.buf[r.idx:]...)
+ r.buf = append(r.buf[:r.idx], tail...)
+ r.idx += len(s)
+ })
+}
+
+func (r *RuneBuffer) MoveForward() {
+ r.Refresh(func() {
+ if r.idx == len(r.buf) {
+ return
+ }
+ r.idx++
+ })
+}
+
+func (r *RuneBuffer) IsCursorInEnd() bool {
+ r.Lock()
+ defer r.Unlock()
+ return r.idx == len(r.buf)
+}
+
+func (r *RuneBuffer) Replace(ch rune) {
+ r.Refresh(func() {
+ r.buf[r.idx] = ch
+ })
+}
+
+func (r *RuneBuffer) Erase() {
+ r.Refresh(func() {
+ r.idx = 0
+ r.pushKill(r.buf[:])
+ r.buf = r.buf[:0]
+ })
+}
+
+func (r *RuneBuffer) Delete() (success bool) {
+ r.Refresh(func() {
+ if r.idx == len(r.buf) {
+ return
+ }
+ r.pushKill(r.buf[r.idx : r.idx+1])
+ r.buf = append(r.buf[:r.idx], r.buf[r.idx+1:]...)
+ success = true
+ })
+ return
+}
+
+func (r *RuneBuffer) DeleteWord() {
+ if r.idx == len(r.buf) {
+ return
+ }
+ init := r.idx
+ for init < len(r.buf) && IsWordBreak(r.buf[init]) {
+ init++
+ }
+ for i := init + 1; i < len(r.buf); i++ {
+ if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
+ r.pushKill(r.buf[r.idx : i-1])
+ r.Refresh(func() {
+ r.buf = append(r.buf[:r.idx], r.buf[i-1:]...)
+ })
+ return
+ }
+ }
+ r.Kill()
+}
+
+func (r *RuneBuffer) MoveToPrevWord() (success bool) {
+ r.Refresh(func() {
+ if r.idx == 0 {
+ return
+ }
+
+ for i := r.idx - 1; i > 0; i-- {
+ if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
+ r.idx = i
+ success = true
+ return
+ }
+ }
+ r.idx = 0
+ success = true
+ })
+ return
+}
+
+func (r *RuneBuffer) KillFront() {
+ r.Refresh(func() {
+ if r.idx == 0 {
+ return
+ }
+
+ length := len(r.buf) - r.idx
+ r.pushKill(r.buf[:r.idx])
+ copy(r.buf[:length], r.buf[r.idx:])
+ r.idx = 0
+ r.buf = r.buf[:length]
+ })
+}
+
+func (r *RuneBuffer) Kill() {
+ r.Refresh(func() {
+ r.pushKill(r.buf[r.idx:])
+ r.buf = r.buf[:r.idx]
+ })
+}
+
+func (r *RuneBuffer) Transpose() {
+ r.Refresh(func() {
+ if len(r.buf) == 1 {
+ r.idx++
+ }
+
+ if len(r.buf) < 2 {
+ return
+ }
+
+ if r.idx == 0 {
+ r.idx = 1
+ } else if r.idx >= len(r.buf) {
+ r.idx = len(r.buf) - 1
+ }
+ r.buf[r.idx], r.buf[r.idx-1] = r.buf[r.idx-1], r.buf[r.idx]
+ r.idx++
+ })
+}
+
+func (r *RuneBuffer) MoveToNextWord() {
+ r.Refresh(func() {
+ for i := r.idx + 1; i < len(r.buf); i++ {
+ if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
+ r.idx = i
+ return
+ }
+ }
+
+ r.idx = len(r.buf)
+ })
+}
+
+func (r *RuneBuffer) MoveToEndWord() {
+ r.Refresh(func() {
+ // already at the end, so do nothing
+ if r.idx == len(r.buf) {
+ return
+ }
+ // if we are at the end of a word already, go to next
+ if !IsWordBreak(r.buf[r.idx]) && IsWordBreak(r.buf[r.idx+1]) {
+ r.idx++
+ }
+
+ // keep going until at the end of a word
+ for i := r.idx + 1; i < len(r.buf); i++ {
+ if IsWordBreak(r.buf[i]) && !IsWordBreak(r.buf[i-1]) {
+ r.idx = i - 1
+ return
+ }
+ }
+ r.idx = len(r.buf)
+ })
+}
+
+func (r *RuneBuffer) BackEscapeWord() {
+ r.Refresh(func() {
+ if r.idx == 0 {
+ return
+ }
+ for i := r.idx - 1; i > 0; i-- {
+ if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
+ r.pushKill(r.buf[i:r.idx])
+ r.buf = append(r.buf[:i], r.buf[r.idx:]...)
+ r.idx = i
+ return
+ }
+ }
+
+ r.buf = r.buf[:0]
+ r.idx = 0
+ })
+}
+
+func (r *RuneBuffer) Yank() {
+ if len(r.lastKill) == 0 {
+ return
+ }
+ r.Refresh(func() {
+ buf := make([]rune, 0, len(r.buf)+len(r.lastKill))
+ buf = append(buf, r.buf[:r.idx]...)
+ buf = append(buf, r.lastKill...)
+ buf = append(buf, r.buf[r.idx:]...)
+ r.buf = buf
+ r.idx += len(r.lastKill)
+ })
+}
+
+func (r *RuneBuffer) Backspace() {
+ r.Refresh(func() {
+ if r.idx == 0 {
+ return
+ }
+
+ r.idx--
+ r.buf = append(r.buf[:r.idx], r.buf[r.idx+1:]...)
+ })
+}
+
+func (r *RuneBuffer) MoveToLineEnd() {
+ r.Refresh(func() {
+ if r.idx == len(r.buf) {
+ return
+ }
+
+ r.idx = len(r.buf)
+ })
+}
+
+func (r *RuneBuffer) LineCount(width int) int {
+ if width == -1 {
+ width = r.width
+ }
+ return LineCount(width,
+ runes.WidthAll(r.buf)+r.PromptLen())
+}
+
+func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success bool) {
+ r.Refresh(func() {
+ if reverse {
+ for i := r.idx - 1; i >= 0; i-- {
+ if r.buf[i] == ch {
+ r.idx = i
+ if prevChar {
+ r.idx++
+ }
+ success = true
+ return
+ }
+ }
+ return
+ }
+ for i := r.idx + 1; i < len(r.buf); i++ {
+ if r.buf[i] == ch {
+ r.idx = i
+ if prevChar {
+ r.idx--
+ }
+ success = true
+ return
+ }
+ }
+ })
+ return
+}
+
+func (r *RuneBuffer) isInLineEdge() bool {
+ if isWindows {
+ return false
+ }
+ sp := r.getSplitByLine(r.buf)
+ return len(sp[len(sp)-1]) == 0
+}
+
+func (r *RuneBuffer) getSplitByLine(rs []rune) []string {
+ return SplitByLine(r.promptLen(), r.width, rs)
+}
+
+func (r *RuneBuffer) IdxLine(width int) int {
+ r.Lock()
+ defer r.Unlock()
+ return r.idxLine(width)
+}
+
+func (r *RuneBuffer) idxLine(width int) int {
+ if width == 0 {
+ return 0
+ }
+ sp := r.getSplitByLine(r.buf[:r.idx])
+ return len(sp) - 1
+}
+
+func (r *RuneBuffer) CursorLineCount() int {
+ return r.LineCount(r.width) - r.IdxLine(r.width)
+}
+
+func (r *RuneBuffer) Refresh(f func()) {
+ r.Lock()
+ defer r.Unlock()
+
+ if !r.interactive {
+ if f != nil {
+ f()
+ }
+ return
+ }
+
+ r.clean()
+ if f != nil {
+ f()
+ }
+ r.print()
+}
+
+func (r *RuneBuffer) SetOffset(offset string) {
+ r.Lock()
+ r.offset = offset
+ r.Unlock()
+}
+
+func (r *RuneBuffer) print() {
+ r.w.Write(r.output())
+ r.hadClean = false
+}
+
+func (r *RuneBuffer) output() []byte {
+ buf := bytes.NewBuffer(nil)
+ buf.WriteString(string(r.prompt))
+ if r.cfg.EnableMask && len(r.buf) > 0 {
+ buf.Write([]byte(strings.Repeat(string(r.cfg.MaskRune), len(r.buf)-1)))
+ if r.buf[len(r.buf)-1] == '\n' {
+ buf.Write([]byte{'\n'})
+ } else {
+ buf.Write([]byte(string(r.cfg.MaskRune)))
+ }
+ if len(r.buf) > r.idx {
+ buf.Write(r.getBackspaceSequence())
+ }
+
+ } else {
+ for _, e := range r.cfg.Painter.Paint(r.buf, r.idx) {
+ if e == '\t' {
+ buf.WriteString(strings.Repeat(" ", TabWidth))
+ } else {
+ buf.WriteRune(e)
+ }
+ }
+ if r.isInLineEdge() {
+ buf.Write([]byte(" \b"))
+ }
+ }
+ // cursor position
+ if len(r.buf) > r.idx {
+ buf.Write(r.getBackspaceSequence())
+ }
+ return buf.Bytes()
+}
+
+func (r *RuneBuffer) getBackspaceSequence() []byte {
+ var sep = map[int]bool{}
+
+ var i int
+ for {
+ if i >= runes.WidthAll(r.buf) {
+ break
+ }
+
+ if i == 0 {
+ i -= r.promptLen()
+ }
+ i += r.width
+
+ sep[i] = true
+ }
+ var buf []byte
+ for i := len(r.buf); i > r.idx; i-- {
+ // move input to the left of one
+ buf = append(buf, '\b')
+ if sep[i] {
+ // up one line, go to the start of the line and move cursor right to the end (r.width)
+ buf = append(buf, "\033[A\r"+"\033["+strconv.Itoa(r.width)+"C"...)
+ }
+ }
+
+ return buf
+
+}
+
+func (r *RuneBuffer) Reset() []rune {
+ ret := runes.Copy(r.buf)
+ r.buf = r.buf[:0]
+ r.idx = 0
+ return ret
+}
+
+func (r *RuneBuffer) calWidth(m int) int {
+ if m > 0 {
+ return runes.WidthAll(r.buf[r.idx : r.idx+m])
+ }
+ return runes.WidthAll(r.buf[r.idx+m : r.idx])
+}
+
+func (r *RuneBuffer) SetStyle(start, end int, style string) {
+ if end < start {
+ panic("end < start")
+ }
+
+ // goto start
+ move := start - r.idx
+ if move > 0 {
+ r.w.Write([]byte(string(r.buf[r.idx : r.idx+move])))
+ } else {
+ r.w.Write(bytes.Repeat([]byte("\b"), r.calWidth(move)))
+ }
+ r.w.Write([]byte("\033[" + style + "m"))
+ r.w.Write([]byte(string(r.buf[start:end])))
+ r.w.Write([]byte("\033[0m"))
+ // TODO: move back
+}
+
+func (r *RuneBuffer) SetWithIdx(idx int, buf []rune) {
+ r.Refresh(func() {
+ r.buf = buf
+ r.idx = idx
+ })
+}
+
+func (r *RuneBuffer) Set(buf []rune) {
+ r.SetWithIdx(len(buf), buf)
+}
+
+func (r *RuneBuffer) SetPrompt(prompt string) {
+ r.Lock()
+ r.prompt = []rune(prompt)
+ r.Unlock()
+}
+
+func (r *RuneBuffer) cleanOutput(w io.Writer, idxLine int) {
+ buf := bufio.NewWriter(w)
+
+ if r.width == 0 {
+ buf.WriteString(strings.Repeat("\r\b", len(r.buf)+r.promptLen()))
+ buf.Write([]byte("\033[J"))
+ } else {
+ buf.Write([]byte("\033[J")) // just like ^k :)
+ if idxLine == 0 {
+ buf.WriteString("\033[2K")
+ buf.WriteString("\r")
+ } else {
+ for i := 0; i < idxLine; i++ {
+ io.WriteString(buf, "\033[2K\r\033[A")
+ }
+ io.WriteString(buf, "\033[2K\r")
+ }
+ }
+ buf.Flush()
+ return
+}
+
+func (r *RuneBuffer) Clean() {
+ r.Lock()
+ r.clean()
+ r.Unlock()
+}
+
+func (r *RuneBuffer) clean() {
+ r.cleanWithIdxLine(r.idxLine(r.width))
+}
+
+func (r *RuneBuffer) cleanWithIdxLine(idxLine int) {
+ if r.hadClean || !r.interactive {
+ return
+ }
+ r.hadClean = true
+ r.cleanOutput(r.w, idxLine)
+}
diff --git a/vendor/github.com/chzyer/readline/runes.go b/vendor/github.com/chzyer/readline/runes.go
new file mode 100644
index 00000000..a669bc48
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/runes.go
@@ -0,0 +1,223 @@
+package readline
+
+import (
+ "bytes"
+ "unicode"
+ "unicode/utf8"
+)
+
+var runes = Runes{}
+var TabWidth = 4
+
+type Runes struct{}
+
+func (Runes) EqualRune(a, b rune, fold bool) bool {
+ if a == b {
+ return true
+ }
+ if !fold {
+ return false
+ }
+ if a > b {
+ a, b = b, a
+ }
+ if b < utf8.RuneSelf && 'A' <= a && a <= 'Z' {
+ if b == a+'a'-'A' {
+ return true
+ }
+ }
+ return false
+}
+
+func (r Runes) EqualRuneFold(a, b rune) bool {
+ return r.EqualRune(a, b, true)
+}
+
+func (r Runes) EqualFold(a, b []rune) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i := 0; i < len(a); i++ {
+ if r.EqualRuneFold(a[i], b[i]) {
+ continue
+ }
+ return false
+ }
+
+ return true
+}
+
+func (Runes) Equal(a, b []rune) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i := 0; i < len(a); i++ {
+ if a[i] != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func (rs Runes) IndexAllBckEx(r, sub []rune, fold bool) int {
+ for i := len(r) - len(sub); i >= 0; i-- {
+ found := true
+ for j := 0; j < len(sub); j++ {
+ if !rs.EqualRune(r[i+j], sub[j], fold) {
+ found = false
+ break
+ }
+ }
+ if found {
+ return i
+ }
+ }
+ return -1
+}
+
+// Search in runes from end to front
+func (rs Runes) IndexAllBck(r, sub []rune) int {
+ return rs.IndexAllBckEx(r, sub, false)
+}
+
+// Search in runes from front to end
+func (rs Runes) IndexAll(r, sub []rune) int {
+ return rs.IndexAllEx(r, sub, false)
+}
+
+func (rs Runes) IndexAllEx(r, sub []rune, fold bool) int {
+ for i := 0; i < len(r); i++ {
+ found := true
+ if len(r[i:]) < len(sub) {
+ return -1
+ }
+ for j := 0; j < len(sub); j++ {
+ if !rs.EqualRune(r[i+j], sub[j], fold) {
+ found = false
+ break
+ }
+ }
+ if found {
+ return i
+ }
+ }
+ return -1
+}
+
+func (Runes) Index(r rune, rs []rune) int {
+ for i := 0; i < len(rs); i++ {
+ if rs[i] == r {
+ return i
+ }
+ }
+ return -1
+}
+
+func (Runes) ColorFilter(r []rune) []rune {
+ newr := make([]rune, 0, len(r))
+ for pos := 0; pos < len(r); pos++ {
+ if r[pos] == '\033' && r[pos+1] == '[' {
+ idx := runes.Index('m', r[pos+2:])
+ if idx == -1 {
+ continue
+ }
+ pos += idx + 2
+ continue
+ }
+ newr = append(newr, r[pos])
+ }
+ return newr
+}
+
+var zeroWidth = []*unicode.RangeTable{
+ unicode.Mn,
+ unicode.Me,
+ unicode.Cc,
+ unicode.Cf,
+}
+
+var doubleWidth = []*unicode.RangeTable{
+ unicode.Han,
+ unicode.Hangul,
+ unicode.Hiragana,
+ unicode.Katakana,
+}
+
+func (Runes) Width(r rune) int {
+ if r == '\t' {
+ return TabWidth
+ }
+ if unicode.IsOneOf(zeroWidth, r) {
+ return 0
+ }
+ if unicode.IsOneOf(doubleWidth, r) {
+ return 2
+ }
+ return 1
+}
+
+func (Runes) WidthAll(r []rune) (length int) {
+ for i := 0; i < len(r); i++ {
+ length += runes.Width(r[i])
+ }
+ return
+}
+
+func (Runes) Backspace(r []rune) []byte {
+ return bytes.Repeat([]byte{'\b'}, runes.WidthAll(r))
+}
+
+func (Runes) Copy(r []rune) []rune {
+ n := make([]rune, len(r))
+ copy(n, r)
+ return n
+}
+
+func (Runes) HasPrefixFold(r, prefix []rune) bool {
+ if len(r) < len(prefix) {
+ return false
+ }
+ return runes.EqualFold(r[:len(prefix)], prefix)
+}
+
+func (Runes) HasPrefix(r, prefix []rune) bool {
+ if len(r) < len(prefix) {
+ return false
+ }
+ return runes.Equal(r[:len(prefix)], prefix)
+}
+
+func (Runes) Aggregate(candicate [][]rune) (same []rune, size int) {
+ for i := 0; i < len(candicate[0]); i++ {
+ for j := 0; j < len(candicate)-1; j++ {
+ if i >= len(candicate[j]) || i >= len(candicate[j+1]) {
+ goto aggregate
+ }
+ if candicate[j][i] != candicate[j+1][i] {
+ goto aggregate
+ }
+ }
+ size = i + 1
+ }
+aggregate:
+ if size > 0 {
+ same = runes.Copy(candicate[0][:size])
+ for i := 0; i < len(candicate); i++ {
+ n := runes.Copy(candicate[i])
+ copy(n, n[size:])
+ candicate[i] = n[:len(n)-size]
+ }
+ }
+ return
+}
+
+func (Runes) TrimSpaceLeft(in []rune) []rune {
+ firstIndex := len(in)
+ for i, r := range in {
+ if unicode.IsSpace(r) == false {
+ firstIndex = i
+ break
+ }
+ }
+ return in[firstIndex:]
+}
diff --git a/vendor/github.com/chzyer/readline/search.go b/vendor/github.com/chzyer/readline/search.go
new file mode 100644
index 00000000..52e8ff09
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/search.go
@@ -0,0 +1,164 @@
+package readline
+
+import (
+ "bytes"
+ "container/list"
+ "fmt"
+ "io"
+)
+
+const (
+ S_STATE_FOUND = iota
+ S_STATE_FAILING
+)
+
+const (
+ S_DIR_BCK = iota
+ S_DIR_FWD
+)
+
+type opSearch struct {
+ inMode bool
+ state int
+ dir int
+ source *list.Element
+ w io.Writer
+ buf *RuneBuffer
+ data []rune
+ history *opHistory
+ cfg *Config
+ markStart int
+ markEnd int
+ width int
+}
+
+func newOpSearch(w io.Writer, buf *RuneBuffer, history *opHistory, cfg *Config, width int) *opSearch {
+ return &opSearch{
+ w: w,
+ buf: buf,
+ cfg: cfg,
+ history: history,
+ width: width,
+ }
+}
+
+func (o *opSearch) OnWidthChange(newWidth int) {
+ o.width = newWidth
+}
+
+func (o *opSearch) IsSearchMode() bool {
+ return o.inMode
+}
+
+func (o *opSearch) SearchBackspace() {
+ if len(o.data) > 0 {
+ o.data = o.data[:len(o.data)-1]
+ o.search(true)
+ }
+}
+
+func (o *opSearch) findHistoryBy(isNewSearch bool) (int, *list.Element) {
+ if o.dir == S_DIR_BCK {
+ return o.history.FindBck(isNewSearch, o.data, o.buf.idx)
+ }
+ return o.history.FindFwd(isNewSearch, o.data, o.buf.idx)
+}
+
+func (o *opSearch) search(isChange bool) bool {
+ if len(o.data) == 0 {
+ o.state = S_STATE_FOUND
+ o.SearchRefresh(-1)
+ return true
+ }
+ idx, elem := o.findHistoryBy(isChange)
+ if elem == nil {
+ o.SearchRefresh(-2)
+ return false
+ }
+ o.history.current = elem
+
+ item := o.history.showItem(o.history.current.Value)
+ start, end := 0, 0
+ if o.dir == S_DIR_BCK {
+ start, end = idx, idx+len(o.data)
+ } else {
+ start, end = idx, idx+len(o.data)
+ idx += len(o.data)
+ }
+ o.buf.SetWithIdx(idx, item)
+ o.markStart, o.markEnd = start, end
+ o.SearchRefresh(idx)
+ return true
+}
+
+func (o *opSearch) SearchChar(r rune) {
+ o.data = append(o.data, r)
+ o.search(true)
+}
+
+func (o *opSearch) SearchMode(dir int) bool {
+ if o.width == 0 {
+ return false
+ }
+ alreadyInMode := o.inMode
+ o.inMode = true
+ o.dir = dir
+ o.source = o.history.current
+ if alreadyInMode {
+ o.search(false)
+ } else {
+ o.SearchRefresh(-1)
+ }
+ return true
+}
+
+func (o *opSearch) ExitSearchMode(revert bool) {
+ if revert {
+ o.history.current = o.source
+ o.buf.Set(o.history.showItem(o.history.current.Value))
+ }
+ o.markStart, o.markEnd = 0, 0
+ o.state = S_STATE_FOUND
+ o.inMode = false
+ o.source = nil
+ o.data = nil
+}
+
+func (o *opSearch) SearchRefresh(x int) {
+ if x == -2 {
+ o.state = S_STATE_FAILING
+ } else if x >= 0 {
+ o.state = S_STATE_FOUND
+ }
+ if x < 0 {
+ x = o.buf.idx
+ }
+ x = o.buf.CurrentWidth(x)
+ x += o.buf.PromptLen()
+ x = x % o.width
+
+ if o.markStart > 0 {
+ o.buf.SetStyle(o.markStart, o.markEnd, "4")
+ }
+
+ lineCnt := o.buf.CursorLineCount()
+ buf := bytes.NewBuffer(nil)
+ buf.Write(bytes.Repeat([]byte("\n"), lineCnt))
+ buf.WriteString("\033[J")
+ if o.state == S_STATE_FAILING {
+ buf.WriteString("failing ")
+ }
+ if o.dir == S_DIR_BCK {
+ buf.WriteString("bck")
+ } else if o.dir == S_DIR_FWD {
+ buf.WriteString("fwd")
+ }
+ buf.WriteString("-i-search: ")
+ buf.WriteString(string(o.data)) // keyword
+ buf.WriteString("\033[4m \033[0m") // _
+ fmt.Fprintf(buf, "\r\033[%dA", lineCnt) // move prev
+ if x > 0 {
+ fmt.Fprintf(buf, "\033[%dC", x) // move forward
+ }
+ o.w.Write(buf.Bytes())
+}
diff --git a/vendor/github.com/chzyer/readline/std.go b/vendor/github.com/chzyer/readline/std.go
new file mode 100644
index 00000000..61d44b75
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/std.go
@@ -0,0 +1,197 @@
+package readline
+
+import (
+ "io"
+ "os"
+ "sync"
+ "sync/atomic"
+)
+
+var (
+ Stdin io.ReadCloser = os.Stdin
+ Stdout io.WriteCloser = os.Stdout
+ Stderr io.WriteCloser = os.Stderr
+)
+
+var (
+ std *Instance
+ stdOnce sync.Once
+)
+
+// global instance will not submit history automatic
+func getInstance() *Instance {
+ stdOnce.Do(func() {
+ std, _ = NewEx(&Config{
+ DisableAutoSaveHistory: true,
+ })
+ })
+ return std
+}
+
+// let readline load history from filepath
+// and try to persist history into disk
+// set fp to "" to prevent readline persisting history to disk
+// so the `AddHistory` will return nil error forever.
+func SetHistoryPath(fp string) {
+ ins := getInstance()
+ cfg := ins.Config.Clone()
+ cfg.HistoryFile = fp
+ ins.SetConfig(cfg)
+}
+
+// set auto completer to global instance
+func SetAutoComplete(completer AutoCompleter) {
+ ins := getInstance()
+ cfg := ins.Config.Clone()
+ cfg.AutoComplete = completer
+ ins.SetConfig(cfg)
+}
+
+// add history to global instance manually
+// raise error only if `SetHistoryPath` is set with a non-empty path
+func AddHistory(content string) error {
+ ins := getInstance()
+ return ins.SaveHistory(content)
+}
+
+func Password(prompt string) ([]byte, error) {
+ ins := getInstance()
+ return ins.ReadPassword(prompt)
+}
+
+// readline with global configs
+func Line(prompt string) (string, error) {
+ ins := getInstance()
+ ins.SetPrompt(prompt)
+ return ins.Readline()
+}
+
+type CancelableStdin struct {
+ r io.Reader
+ mutex sync.Mutex
+ stop chan struct{}
+ closed int32
+ notify chan struct{}
+ data []byte
+ read int
+ err error
+}
+
+func NewCancelableStdin(r io.Reader) *CancelableStdin {
+ c := &CancelableStdin{
+ r: r,
+ notify: make(chan struct{}),
+ stop: make(chan struct{}),
+ }
+ go c.ioloop()
+ return c
+}
+
+func (c *CancelableStdin) ioloop() {
+loop:
+ for {
+ select {
+ case <-c.notify:
+ c.read, c.err = c.r.Read(c.data)
+ select {
+ case c.notify <- struct{}{}:
+ case <-c.stop:
+ break loop
+ }
+ case <-c.stop:
+ break loop
+ }
+ }
+}
+
+func (c *CancelableStdin) Read(b []byte) (n int, err error) {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ if atomic.LoadInt32(&c.closed) == 1 {
+ return 0, io.EOF
+ }
+
+ c.data = b
+ select {
+ case c.notify <- struct{}{}:
+ case <-c.stop:
+ return 0, io.EOF
+ }
+ select {
+ case <-c.notify:
+ return c.read, c.err
+ case <-c.stop:
+ return 0, io.EOF
+ }
+}
+
+func (c *CancelableStdin) Close() error {
+ if atomic.CompareAndSwapInt32(&c.closed, 0, 1) {
+ close(c.stop)
+ }
+ return nil
+}
+
+// FillableStdin is a stdin reader which can prepend some data before
+// reading into the real stdin
+type FillableStdin struct {
+ sync.Mutex
+ stdin io.Reader
+ stdinBuffer io.ReadCloser
+ buf []byte
+ bufErr error
+}
+
+// NewFillableStdin gives you FillableStdin
+func NewFillableStdin(stdin io.Reader) (io.ReadCloser, io.Writer) {
+ r, w := io.Pipe()
+ s := &FillableStdin{
+ stdinBuffer: r,
+ stdin: stdin,
+ }
+ s.ioloop()
+ return s, w
+}
+
+func (s *FillableStdin) ioloop() {
+ go func() {
+ for {
+ bufR := make([]byte, 100)
+ var n int
+ n, s.bufErr = s.stdinBuffer.Read(bufR)
+ if s.bufErr != nil {
+ if s.bufErr == io.ErrClosedPipe {
+ break
+ }
+ }
+ s.Lock()
+ s.buf = append(s.buf, bufR[:n]...)
+ s.Unlock()
+ }
+ }()
+}
+
+// Read will read from the local buffer and if no data, read from stdin
+func (s *FillableStdin) Read(p []byte) (n int, err error) {
+ s.Lock()
+ i := len(s.buf)
+ if len(p) < i {
+ i = len(p)
+ }
+ if i > 0 {
+ n := copy(p, s.buf)
+ s.buf = s.buf[:0]
+ cerr := s.bufErr
+ s.bufErr = nil
+ s.Unlock()
+ return n, cerr
+ }
+ s.Unlock()
+ n, err = s.stdin.Read(p)
+ return n, err
+}
+
+func (s *FillableStdin) Close() error {
+ s.stdinBuffer.Close()
+ return nil
+}
diff --git a/vendor/github.com/chzyer/readline/std_windows.go b/vendor/github.com/chzyer/readline/std_windows.go
new file mode 100644
index 00000000..b10f91bc
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/std_windows.go
@@ -0,0 +1,9 @@
+// +build windows
+
+package readline
+
+func init() {
+ Stdin = NewRawReader()
+ Stdout = NewANSIWriter(Stdout)
+ Stderr = NewANSIWriter(Stderr)
+}
diff --git a/vendor/github.com/chzyer/readline/term.go b/vendor/github.com/chzyer/readline/term.go
new file mode 100644
index 00000000..ea5db934
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/term.go
@@ -0,0 +1,123 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build aix darwin dragonfly freebsd linux,!appengine netbsd openbsd os400 solaris
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// oldState, err := terminal.MakeRaw(0)
+// if err != nil {
+// panic(err)
+// }
+// defer terminal.Restore(0, oldState)
+package readline
+
+import (
+ "io"
+ "syscall"
+)
+
+// State contains the state of a terminal.
+type State struct {
+ termios Termios
+}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ _, err := getTermios(fd)
+ return err == nil
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+ var oldState State
+
+ if termios, err := getTermios(fd); err != nil {
+ return nil, err
+ } else {
+ oldState.termios = *termios
+ }
+
+ newState := oldState.termios
+ // This attempts to replicate the behaviour documented for cfmakeraw in
+ // the termios(3) manpage.
+ newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
+ // newState.Oflag &^= syscall.OPOST
+ newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
+ newState.Cflag &^= syscall.CSIZE | syscall.PARENB
+ newState.Cflag |= syscall.CS8
+
+ newState.Cc[syscall.VMIN] = 1
+ newState.Cc[syscall.VTIME] = 0
+
+ return &oldState, setTermios(fd, &newState)
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+ termios, err := getTermios(fd)
+ if err != nil {
+ return nil, err
+ }
+
+ return &State{termios: *termios}, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func restoreTerm(fd int, state *State) error {
+ return setTermios(fd, &state.termios)
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ oldState, err := getTermios(fd)
+ if err != nil {
+ return nil, err
+ }
+
+ newState := oldState
+ newState.Lflag &^= syscall.ECHO
+ newState.Lflag |= syscall.ICANON | syscall.ISIG
+ newState.Iflag |= syscall.ICRNL
+ if err := setTermios(fd, newState); err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ setTermios(fd, oldState)
+ }()
+
+ var buf [16]byte
+ var ret []byte
+ for {
+ n, err := syscall.Read(fd, buf[:])
+ if err != nil {
+ return nil, err
+ }
+ if n == 0 {
+ if len(ret) == 0 {
+ return nil, io.EOF
+ }
+ break
+ }
+ if buf[n-1] == '\n' {
+ n--
+ }
+ ret = append(ret, buf[:n]...)
+ if n < len(buf) {
+ break
+ }
+ }
+
+ return ret, nil
+}
diff --git a/vendor/github.com/chzyer/readline/term_bsd.go b/vendor/github.com/chzyer/readline/term_bsd.go
new file mode 100644
index 00000000..68b56ea6
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/term_bsd.go
@@ -0,0 +1,29 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package readline
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func getTermios(fd int) (*Termios, error) {
+ termios := new(Termios)
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), syscall.TIOCGETA, uintptr(unsafe.Pointer(termios)), 0, 0, 0)
+ if err != 0 {
+ return nil, err
+ }
+ return termios, nil
+}
+
+func setTermios(fd int, termios *Termios) error {
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), syscall.TIOCSETA, uintptr(unsafe.Pointer(termios)), 0, 0, 0)
+ if err != 0 {
+ return err
+ }
+ return nil
+}
diff --git a/vendor/github.com/chzyer/readline/term_linux.go b/vendor/github.com/chzyer/readline/term_linux.go
new file mode 100644
index 00000000..e3392b4a
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/term_linux.go
@@ -0,0 +1,33 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package readline
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// These constants are declared here, rather than importing
+// them from the syscall package as some syscall packages, even
+// on linux, for example gccgo, do not declare them.
+const ioctlReadTermios = 0x5401 // syscall.TCGETS
+const ioctlWriteTermios = 0x5402 // syscall.TCSETS
+
+func getTermios(fd int) (*Termios, error) {
+ termios := new(Termios)
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(termios)), 0, 0, 0)
+ if err != 0 {
+ return nil, err
+ }
+ return termios, nil
+}
+
+func setTermios(fd int, termios *Termios) error {
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(termios)), 0, 0, 0)
+ if err != 0 {
+ return err
+ }
+ return nil
+}
diff --git a/vendor/github.com/chzyer/readline/term_nosyscall6.go b/vendor/github.com/chzyer/readline/term_nosyscall6.go
new file mode 100644
index 00000000..df923393
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/term_nosyscall6.go
@@ -0,0 +1,32 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build aix os400 solaris
+
+package readline
+
+import "golang.org/x/sys/unix"
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (int, int, error) {
+ ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
+ if err != nil {
+ return 0, 0, err
+ }
+ return int(ws.Col), int(ws.Row), nil
+}
+
+type Termios unix.Termios
+
+func getTermios(fd int) (*Termios, error) {
+ termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
+ if err != nil {
+ return nil, err
+ }
+ return (*Termios)(termios), nil
+}
+
+func setTermios(fd int, termios *Termios) error {
+ return unix.IoctlSetTermios(fd, unix.TCSETSF, (*unix.Termios)(termios))
+}
diff --git a/vendor/github.com/chzyer/readline/term_unix.go b/vendor/github.com/chzyer/readline/term_unix.go
new file mode 100644
index 00000000..d3ea2424
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/term_unix.go
@@ -0,0 +1,24 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd
+
+package readline
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+type Termios syscall.Termios
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (int, int, error) {
+ var dimensions [4]uint16
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0)
+ if err != 0 {
+ return 0, 0, err
+ }
+ return int(dimensions[1]), int(dimensions[0]), nil
+}
diff --git a/vendor/github.com/chzyer/readline/term_windows.go b/vendor/github.com/chzyer/readline/term_windows.go
new file mode 100644
index 00000000..1290e00b
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/term_windows.go
@@ -0,0 +1,171 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build windows
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// oldState, err := terminal.MakeRaw(0)
+// if err != nil {
+// panic(err)
+// }
+// defer terminal.Restore(0, oldState)
+package readline
+
+import (
+ "io"
+ "syscall"
+ "unsafe"
+)
+
+const (
+ enableLineInput = 2
+ enableEchoInput = 4
+ enableProcessedInput = 1
+ enableWindowInput = 8
+ enableMouseInput = 16
+ enableInsertMode = 32
+ enableQuickEditMode = 64
+ enableExtendedFlags = 128
+ enableAutoPosition = 256
+ enableProcessedOutput = 1
+ enableWrapAtEolOutput = 2
+)
+
+var kernel32 = syscall.NewLazyDLL("kernel32.dll")
+
+var (
+ procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
+ procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
+ procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
+)
+
+type (
+ coord struct {
+ x short
+ y short
+ }
+ smallRect struct {
+ left short
+ top short
+ right short
+ bottom short
+ }
+ consoleScreenBufferInfo struct {
+ size coord
+ cursorPosition coord
+ attributes word
+ window smallRect
+ maximumWindowSize coord
+ }
+)
+
+type State struct {
+ mode uint32
+}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ var st uint32
+ r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+ return r != 0 && e == 0
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+ var st uint32
+ _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+ if e != 0 {
+ return nil, error(e)
+ }
+ raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
+ _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0)
+ if e != 0 {
+ return nil, error(e)
+ }
+ return &State{st}, nil
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+ var st uint32
+ _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+ if e != 0 {
+ return nil, error(e)
+ }
+ return &State{st}, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func restoreTerm(fd int, state *State) error {
+ _, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0)
+ return err
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+ var info consoleScreenBufferInfo
+ _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0)
+ if e != 0 {
+ return 0, 0, error(e)
+ }
+ return int(info.size.x), int(info.size.y), nil
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ var st uint32
+ _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+ if e != 0 {
+ return nil, error(e)
+ }
+ old := st
+
+ st &^= (enableEchoInput)
+ st |= (enableProcessedInput | enableLineInput | enableProcessedOutput)
+ _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0)
+ if e != 0 {
+ return nil, error(e)
+ }
+
+ defer func() {
+ syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0)
+ }()
+
+ var buf [16]byte
+ var ret []byte
+ for {
+ n, err := syscall.Read(syscall.Handle(fd), buf[:])
+ if err != nil {
+ return nil, err
+ }
+ if n == 0 {
+ if len(ret) == 0 {
+ return nil, io.EOF
+ }
+ break
+ }
+ if buf[n-1] == '\n' {
+ n--
+ }
+ if n > 0 && buf[n-1] == '\r' {
+ n--
+ }
+ ret = append(ret, buf[:n]...)
+ if n < len(buf) {
+ break
+ }
+ }
+
+ return ret, nil
+}
diff --git a/vendor/github.com/chzyer/readline/terminal.go b/vendor/github.com/chzyer/readline/terminal.go
new file mode 100644
index 00000000..38413d0c
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/terminal.go
@@ -0,0 +1,254 @@
+package readline
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "strings"
+ "sync"
+ "sync/atomic"
+)
+
+type Terminal struct {
+ m sync.Mutex
+ cfg *Config
+ outchan chan rune
+ closed int32
+ stopChan chan struct{}
+ kickChan chan struct{}
+ wg sync.WaitGroup
+ isReading int32
+ sleeping int32
+
+ sizeChan chan string
+}
+
+func NewTerminal(cfg *Config) (*Terminal, error) {
+ if err := cfg.Init(); err != nil {
+ return nil, err
+ }
+ t := &Terminal{
+ cfg: cfg,
+ kickChan: make(chan struct{}, 1),
+ outchan: make(chan rune),
+ stopChan: make(chan struct{}, 1),
+ sizeChan: make(chan string, 1),
+ }
+
+ go t.ioloop()
+ return t, nil
+}
+
+// SleepToResume will sleep myself, and return only if I'm resumed.
+func (t *Terminal) SleepToResume() {
+ if !atomic.CompareAndSwapInt32(&t.sleeping, 0, 1) {
+ return
+ }
+ defer atomic.StoreInt32(&t.sleeping, 0)
+
+ t.ExitRawMode()
+ ch := WaitForResume()
+ SuspendMe()
+ <-ch
+ t.EnterRawMode()
+}
+
+func (t *Terminal) EnterRawMode() (err error) {
+ return t.cfg.FuncMakeRaw()
+}
+
+func (t *Terminal) ExitRawMode() (err error) {
+ return t.cfg.FuncExitRaw()
+}
+
+func (t *Terminal) Write(b []byte) (int, error) {
+ return t.cfg.Stdout.Write(b)
+}
+
+// WriteStdin prefill the next Stdin fetch
+// Next time you call ReadLine() this value will be writen before the user input
+func (t *Terminal) WriteStdin(b []byte) (int, error) {
+ return t.cfg.StdinWriter.Write(b)
+}
+
+type termSize struct {
+ left int
+ top int
+}
+
+func (t *Terminal) GetOffset(f func(offset string)) {
+ go func() {
+ f(<-t.sizeChan)
+ }()
+ t.Write([]byte("\033[6n"))
+}
+
+func (t *Terminal) Print(s string) {
+ fmt.Fprintf(t.cfg.Stdout, "%s", s)
+}
+
+func (t *Terminal) PrintRune(r rune) {
+ fmt.Fprintf(t.cfg.Stdout, "%c", r)
+}
+
+func (t *Terminal) Readline() *Operation {
+ return NewOperation(t, t.cfg)
+}
+
+// return rune(0) if meet EOF
+func (t *Terminal) ReadRune() rune {
+ ch, ok := <-t.outchan
+ if !ok {
+ return rune(0)
+ }
+ return ch
+}
+
+func (t *Terminal) IsReading() bool {
+ return atomic.LoadInt32(&t.isReading) == 1
+}
+
+func (t *Terminal) KickRead() {
+ select {
+ case t.kickChan <- struct{}{}:
+ default:
+ }
+}
+
+func (t *Terminal) ioloop() {
+ t.wg.Add(1)
+ defer func() {
+ t.wg.Done()
+ close(t.outchan)
+ }()
+
+ var (
+ isEscape bool
+ isEscapeEx bool
+ isEscapeSS3 bool
+ expectNextChar bool
+ )
+
+ buf := bufio.NewReader(t.getStdin())
+ for {
+ if !expectNextChar {
+ atomic.StoreInt32(&t.isReading, 0)
+ select {
+ case <-t.kickChan:
+ atomic.StoreInt32(&t.isReading, 1)
+ case <-t.stopChan:
+ return
+ }
+ }
+ expectNextChar = false
+ r, _, err := buf.ReadRune()
+ if err != nil {
+ if strings.Contains(err.Error(), "interrupted system call") {
+ expectNextChar = true
+ continue
+ }
+ break
+ }
+
+ if isEscape {
+ isEscape = false
+ if r == CharEscapeEx {
+ // ^][
+ expectNextChar = true
+ isEscapeEx = true
+ continue
+ } else if r == CharO {
+ // ^]O
+ expectNextChar = true
+ isEscapeSS3 = true
+ continue
+ }
+ r = escapeKey(r, buf)
+ } else if isEscapeEx {
+ isEscapeEx = false
+ if key := readEscKey(r, buf); key != nil {
+ r = escapeExKey(key)
+ // offset
+ if key.typ == 'R' {
+ if _, _, ok := key.Get2(); ok {
+ select {
+ case t.sizeChan <- key.attr:
+ default:
+ }
+ }
+ expectNextChar = true
+ continue
+ }
+ }
+ if r == 0 {
+ expectNextChar = true
+ continue
+ }
+ } else if isEscapeSS3 {
+ isEscapeSS3 = false
+ if key := readEscKey(r, buf); key != nil {
+ r = escapeSS3Key(key)
+ }
+ if r == 0 {
+ expectNextChar = true
+ continue
+ }
+ }
+
+ expectNextChar = true
+ switch r {
+ case CharEsc:
+ if t.cfg.VimMode {
+ t.outchan <- r
+ break
+ }
+ isEscape = true
+ case CharInterrupt, CharEnter, CharCtrlJ, CharDelete:
+ expectNextChar = false
+ fallthrough
+ default:
+ t.outchan <- r
+ }
+ }
+
+}
+
+func (t *Terminal) Bell() {
+ fmt.Fprintf(t, "%c", CharBell)
+}
+
+func (t *Terminal) Close() error {
+ if atomic.SwapInt32(&t.closed, 1) != 0 {
+ return nil
+ }
+ if closer, ok := t.cfg.Stdin.(io.Closer); ok {
+ closer.Close()
+ }
+ close(t.stopChan)
+ t.wg.Wait()
+ return t.ExitRawMode()
+}
+
+func (t *Terminal) GetConfig() *Config {
+ t.m.Lock()
+ cfg := *t.cfg
+ t.m.Unlock()
+ return &cfg
+}
+
+func (t *Terminal) getStdin() io.Reader {
+ t.m.Lock()
+ r := t.cfg.Stdin
+ t.m.Unlock()
+ return r
+}
+
+func (t *Terminal) SetConfig(c *Config) error {
+ if err := c.Init(); err != nil {
+ return err
+ }
+ t.m.Lock()
+ t.cfg = c
+ t.m.Unlock()
+ return nil
+}
diff --git a/vendor/github.com/chzyer/readline/utils.go b/vendor/github.com/chzyer/readline/utils.go
new file mode 100644
index 00000000..0706dd4e
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/utils.go
@@ -0,0 +1,311 @@
+package readline
+
+import (
+ "bufio"
+ "bytes"
+ "container/list"
+ "fmt"
+ "os"
+ "os/signal"
+ "strconv"
+ "strings"
+ "sync"
+ "syscall"
+ "time"
+ "unicode"
+)
+
+var (
+ isWindows = false
+)
+
+const (
+ CharLineStart = 1
+ CharBackward = 2
+ CharInterrupt = 3
+ CharDelete = 4
+ CharLineEnd = 5
+ CharForward = 6
+ CharBell = 7
+ CharCtrlH = 8
+ CharTab = 9
+ CharCtrlJ = 10
+ CharKill = 11
+ CharCtrlL = 12
+ CharEnter = 13
+ CharNext = 14
+ CharPrev = 16
+ CharBckSearch = 18
+ CharFwdSearch = 19
+ CharTranspose = 20
+ CharCtrlU = 21
+ CharCtrlW = 23
+ CharCtrlY = 25
+ CharCtrlZ = 26
+ CharEsc = 27
+ CharO = 79
+ CharEscapeEx = 91
+ CharBackspace = 127
+)
+
+const (
+ MetaBackward rune = -iota - 1
+ MetaForward
+ MetaDelete
+ MetaBackspace
+ MetaTranspose
+)
+
+// WaitForResume need to call before current process got suspend.
+// It will run a ticker until a long duration is occurs,
+// which means this process is resumed.
+func WaitForResume() chan struct{} {
+ ch := make(chan struct{})
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ ticker := time.NewTicker(10 * time.Millisecond)
+ t := time.Now()
+ wg.Done()
+ for {
+ now := <-ticker.C
+ if now.Sub(t) > 100*time.Millisecond {
+ break
+ }
+ t = now
+ }
+ ticker.Stop()
+ ch <- struct{}{}
+ }()
+ wg.Wait()
+ return ch
+}
+
+func Restore(fd int, state *State) error {
+ err := restoreTerm(fd, state)
+ if err != nil {
+ // errno 0 means everything is ok :)
+ if err.Error() == "errno 0" {
+ return nil
+ } else {
+ return err
+ }
+ }
+ return nil
+}
+
+func IsPrintable(key rune) bool {
+ isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
+ return key >= 32 && !isInSurrogateArea
+}
+
+// translate Esc[X
+func escapeExKey(key *escapeKeyPair) rune {
+ var r rune
+ switch key.typ {
+ case 'D':
+ r = CharBackward
+ case 'C':
+ r = CharForward
+ case 'A':
+ r = CharPrev
+ case 'B':
+ r = CharNext
+ case 'H':
+ r = CharLineStart
+ case 'F':
+ r = CharLineEnd
+ case '~':
+ if key.attr == "3" {
+ r = CharDelete
+ }
+ default:
+ }
+ return r
+}
+
+// translate EscOX SS3 codes for up/down/etc.
+func escapeSS3Key(key *escapeKeyPair) rune {
+ var r rune
+ switch key.typ {
+ case 'D':
+ r = CharBackward
+ case 'C':
+ r = CharForward
+ case 'A':
+ r = CharPrev
+ case 'B':
+ r = CharNext
+ case 'H':
+ r = CharLineStart
+ case 'F':
+ r = CharLineEnd
+ default:
+ }
+ return r
+}
+
+type escapeKeyPair struct {
+ attr string
+ typ rune
+}
+
+func (e *escapeKeyPair) Get2() (int, int, bool) {
+ sp := strings.Split(e.attr, ";")
+ if len(sp) < 2 {
+ return -1, -1, false
+ }
+ s1, err := strconv.Atoi(sp[0])
+ if err != nil {
+ return -1, -1, false
+ }
+ s2, err := strconv.Atoi(sp[1])
+ if err != nil {
+ return -1, -1, false
+ }
+ return s1, s2, true
+}
+
+func readEscKey(r rune, reader *bufio.Reader) *escapeKeyPair {
+ p := escapeKeyPair{}
+ buf := bytes.NewBuffer(nil)
+ for {
+ if r == ';' {
+ } else if unicode.IsNumber(r) {
+ } else {
+ p.typ = r
+ break
+ }
+ buf.WriteRune(r)
+ r, _, _ = reader.ReadRune()
+ }
+ p.attr = buf.String()
+ return &p
+}
+
+// translate EscX to Meta+X
+func escapeKey(r rune, reader *bufio.Reader) rune {
+ switch r {
+ case 'b':
+ r = MetaBackward
+ case 'f':
+ r = MetaForward
+ case 'd':
+ r = MetaDelete
+ case CharTranspose:
+ r = MetaTranspose
+ case CharBackspace:
+ r = MetaBackspace
+ case 'O':
+ d, _, _ := reader.ReadRune()
+ switch d {
+ case 'H':
+ r = CharLineStart
+ case 'F':
+ r = CharLineEnd
+ default:
+ reader.UnreadRune()
+ }
+ case CharEsc:
+
+ }
+ return r
+}
+
+func SplitByLine(start, screenWidth int, rs []rune) []string {
+ var ret []string
+ buf := bytes.NewBuffer(nil)
+ currentWidth := start
+ for _, r := range rs {
+ w := runes.Width(r)
+ currentWidth += w
+ buf.WriteRune(r)
+ if currentWidth >= screenWidth {
+ ret = append(ret, buf.String())
+ buf.Reset()
+ currentWidth = 0
+ }
+ }
+ ret = append(ret, buf.String())
+ return ret
+}
+
+// calculate how many lines for N character
+func LineCount(screenWidth, w int) int {
+ r := w / screenWidth
+ if w%screenWidth != 0 {
+ r++
+ }
+ return r
+}
+
+func IsWordBreak(i rune) bool {
+ switch {
+ case i >= 'a' && i <= 'z':
+ case i >= 'A' && i <= 'Z':
+ case i >= '0' && i <= '9':
+ default:
+ return true
+ }
+ return false
+}
+
+func GetInt(s []string, def int) int {
+ if len(s) == 0 {
+ return def
+ }
+ c, err := strconv.Atoi(s[0])
+ if err != nil {
+ return def
+ }
+ return c
+}
+
+type RawMode struct {
+ state *State
+}
+
+func (r *RawMode) Enter() (err error) {
+ r.state, err = MakeRaw(GetStdin())
+ return err
+}
+
+func (r *RawMode) Exit() error {
+ if r.state == nil {
+ return nil
+ }
+ return Restore(GetStdin(), r.state)
+}
+
+// -----------------------------------------------------------------------------
+
+func sleep(n int) {
+ Debug(n)
+ time.Sleep(2000 * time.Millisecond)
+}
+
+// print a linked list to Debug()
+func debugList(l *list.List) {
+ idx := 0
+ for e := l.Front(); e != nil; e = e.Next() {
+ Debug(idx, fmt.Sprintf("%+v", e.Value))
+ idx++
+ }
+}
+
+// append log info to another file
+func Debug(o ...interface{}) {
+ f, _ := os.OpenFile("debug.tmp", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
+ fmt.Fprintln(f, o...)
+ f.Close()
+}
+
+func CaptureExitSignal(f func()) {
+ cSignal := make(chan os.Signal, 1)
+ signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
+ go func() {
+ for range cSignal {
+ f()
+ }
+ }()
+}
diff --git a/vendor/github.com/chzyer/readline/utils_unix.go b/vendor/github.com/chzyer/readline/utils_unix.go
new file mode 100644
index 00000000..fc494923
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/utils_unix.go
@@ -0,0 +1,83 @@
+// +build aix darwin dragonfly freebsd linux,!appengine netbsd openbsd os400 solaris
+
+package readline
+
+import (
+ "io"
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+)
+
+type winsize struct {
+ Row uint16
+ Col uint16
+ Xpixel uint16
+ Ypixel uint16
+}
+
+// SuspendMe use to send suspend signal to myself, when we in the raw mode.
+// For OSX it need to send to parent's pid
+// For Linux it need to send to myself
+func SuspendMe() {
+ p, _ := os.FindProcess(os.Getppid())
+ p.Signal(syscall.SIGTSTP)
+ p, _ = os.FindProcess(os.Getpid())
+ p.Signal(syscall.SIGTSTP)
+}
+
+// get width of the terminal
+func getWidth(stdoutFd int) int {
+ cols, _, err := GetSize(stdoutFd)
+ if err != nil {
+ return -1
+ }
+ return cols
+}
+
+func GetScreenWidth() int {
+ w := getWidth(syscall.Stdout)
+ if w < 0 {
+ w = getWidth(syscall.Stderr)
+ }
+ return w
+}
+
+// ClearScreen clears the console screen
+func ClearScreen(w io.Writer) (int, error) {
+ return w.Write([]byte("\033[H"))
+}
+
+func DefaultIsTerminal() bool {
+ return IsTerminal(syscall.Stdin) && (IsTerminal(syscall.Stdout) || IsTerminal(syscall.Stderr))
+}
+
+func GetStdin() int {
+ return syscall.Stdin
+}
+
+// -----------------------------------------------------------------------------
+
+var (
+ widthChange sync.Once
+ widthChangeCallback func()
+)
+
+func DefaultOnWidthChanged(f func()) {
+ widthChangeCallback = f
+ widthChange.Do(func() {
+ ch := make(chan os.Signal, 1)
+ signal.Notify(ch, syscall.SIGWINCH)
+
+ go func() {
+ for {
+ _, ok := <-ch
+ if !ok {
+ break
+ }
+ widthChangeCallback()
+ }
+ }()
+ })
+}
diff --git a/vendor/github.com/chzyer/readline/utils_windows.go b/vendor/github.com/chzyer/readline/utils_windows.go
new file mode 100644
index 00000000..5bfa55dc
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/utils_windows.go
@@ -0,0 +1,41 @@
+// +build windows
+
+package readline
+
+import (
+ "io"
+ "syscall"
+)
+
+func SuspendMe() {
+}
+
+func GetStdin() int {
+ return int(syscall.Stdin)
+}
+
+func init() {
+ isWindows = true
+}
+
+// get width of the terminal
+func GetScreenWidth() int {
+ info, _ := GetConsoleScreenBufferInfo()
+ if info == nil {
+ return -1
+ }
+ return int(info.dwSize.x)
+}
+
+// ClearScreen clears the console screen
+func ClearScreen(_ io.Writer) error {
+ return SetConsoleCursorPosition(&_COORD{0, 0})
+}
+
+func DefaultIsTerminal() bool {
+ return true
+}
+
+func DefaultOnWidthChanged(func()) {
+
+}
diff --git a/vendor/github.com/chzyer/readline/vim.go b/vendor/github.com/chzyer/readline/vim.go
new file mode 100644
index 00000000..bedf2c1a
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/vim.go
@@ -0,0 +1,176 @@
+package readline
+
+const (
+ VIM_NORMAL = iota
+ VIM_INSERT
+ VIM_VISUAL
+)
+
+type opVim struct {
+ cfg *Config
+ op *Operation
+ vimMode int
+}
+
+func newVimMode(op *Operation) *opVim {
+ ov := &opVim{
+ cfg: op.cfg,
+ op: op,
+ }
+ ov.SetVimMode(ov.cfg.VimMode)
+ return ov
+}
+
+func (o *opVim) SetVimMode(on bool) {
+ if o.cfg.VimMode && !on { // turn off
+ o.ExitVimMode()
+ }
+ o.cfg.VimMode = on
+ o.vimMode = VIM_INSERT
+}
+
+func (o *opVim) ExitVimMode() {
+ o.vimMode = VIM_INSERT
+}
+
+func (o *opVim) IsEnableVimMode() bool {
+ return o.cfg.VimMode
+}
+
+func (o *opVim) handleVimNormalMovement(r rune, readNext func() rune) (t rune, handled bool) {
+ rb := o.op.buf
+ handled = true
+ switch r {
+ case 'h':
+ t = CharBackward
+ case 'j':
+ t = CharNext
+ case 'k':
+ t = CharPrev
+ case 'l':
+ t = CharForward
+ case '0', '^':
+ rb.MoveToLineStart()
+ case '$':
+ rb.MoveToLineEnd()
+ case 'x':
+ rb.Delete()
+ if rb.IsCursorInEnd() {
+ rb.MoveBackward()
+ }
+ case 'r':
+ rb.Replace(readNext())
+ case 'd':
+ next := readNext()
+ switch next {
+ case 'd':
+ rb.Erase()
+ case 'w':
+ rb.DeleteWord()
+ case 'h':
+ rb.Backspace()
+ case 'l':
+ rb.Delete()
+ }
+ case 'p':
+ rb.Yank()
+ case 'b', 'B':
+ rb.MoveToPrevWord()
+ case 'w', 'W':
+ rb.MoveToNextWord()
+ case 'e', 'E':
+ rb.MoveToEndWord()
+ case 'f', 'F', 't', 'T':
+ next := readNext()
+ prevChar := r == 't' || r == 'T'
+ reverse := r == 'F' || r == 'T'
+ switch next {
+ case CharEsc:
+ default:
+ rb.MoveTo(next, prevChar, reverse)
+ }
+ default:
+ return r, false
+ }
+ return t, true
+}
+
+func (o *opVim) handleVimNormalEnterInsert(r rune, readNext func() rune) (t rune, handled bool) {
+ rb := o.op.buf
+ handled = true
+ switch r {
+ case 'i':
+ case 'I':
+ rb.MoveToLineStart()
+ case 'a':
+ rb.MoveForward()
+ case 'A':
+ rb.MoveToLineEnd()
+ case 's':
+ rb.Delete()
+ case 'S':
+ rb.Erase()
+ case 'c':
+ next := readNext()
+ switch next {
+ case 'c':
+ rb.Erase()
+ case 'w':
+ rb.DeleteWord()
+ case 'h':
+ rb.Backspace()
+ case 'l':
+ rb.Delete()
+ }
+ default:
+ return r, false
+ }
+
+ o.EnterVimInsertMode()
+ return
+}
+
+func (o *opVim) HandleVimNormal(r rune, readNext func() rune) (t rune) {
+ switch r {
+ case CharEnter, CharInterrupt:
+ o.ExitVimMode()
+ return r
+ }
+
+ if r, handled := o.handleVimNormalMovement(r, readNext); handled {
+ return r
+ }
+
+ if r, handled := o.handleVimNormalEnterInsert(r, readNext); handled {
+ return r
+ }
+
+ // invalid operation
+ o.op.t.Bell()
+ return 0
+}
+
+func (o *opVim) EnterVimInsertMode() {
+ o.vimMode = VIM_INSERT
+}
+
+func (o *opVim) ExitVimInsertMode() {
+ o.vimMode = VIM_NORMAL
+}
+
+func (o *opVim) HandleVim(r rune, readNext func() rune) rune {
+ if o.vimMode == VIM_NORMAL {
+ return o.HandleVimNormal(r, readNext)
+ }
+ if r == CharEsc {
+ o.ExitVimInsertMode()
+ return 0
+ }
+
+ switch o.vimMode {
+ case VIM_INSERT:
+ return r
+ case VIM_VISUAL:
+ }
+ return r
+}
diff --git a/vendor/github.com/chzyer/readline/windows_api.go b/vendor/github.com/chzyer/readline/windows_api.go
new file mode 100644
index 00000000..63f4f7b7
--- /dev/null
+++ b/vendor/github.com/chzyer/readline/windows_api.go
@@ -0,0 +1,152 @@
+// +build windows
+
+package readline
+
+import (
+ "reflect"
+ "syscall"
+ "unsafe"
+)
+
+var (
+ kernel = NewKernel()
+ stdout = uintptr(syscall.Stdout)
+ stdin = uintptr(syscall.Stdin)
+)
+
+type Kernel struct {
+ SetConsoleCursorPosition,
+ SetConsoleTextAttribute,
+ FillConsoleOutputCharacterW,
+ FillConsoleOutputAttribute,
+ ReadConsoleInputW,
+ GetConsoleScreenBufferInfo,
+ GetConsoleCursorInfo,
+ GetStdHandle CallFunc
+}
+
+type short int16
+type word uint16
+type dword uint32
+type wchar uint16
+
+type _COORD struct {
+ x short
+ y short
+}
+
+func (c *_COORD) ptr() uintptr {
+ return uintptr(*(*int32)(unsafe.Pointer(c)))
+}
+
+const (
+ EVENT_KEY = 0x0001
+ EVENT_MOUSE = 0x0002
+ EVENT_WINDOW_BUFFER_SIZE = 0x0004
+ EVENT_MENU = 0x0008
+ EVENT_FOCUS = 0x0010
+)
+
+type _KEY_EVENT_RECORD struct {
+ bKeyDown int32
+ wRepeatCount word
+ wVirtualKeyCode word
+ wVirtualScanCode word
+ unicodeChar wchar
+ dwControlKeyState dword
+}
+
+// KEY_EVENT_RECORD KeyEvent;
+// MOUSE_EVENT_RECORD MouseEvent;
+// WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
+// MENU_EVENT_RECORD MenuEvent;
+// FOCUS_EVENT_RECORD FocusEvent;
+type _INPUT_RECORD struct {
+ EventType word
+ Padding uint16
+ Event [16]byte
+}
+
+type _CONSOLE_SCREEN_BUFFER_INFO struct {
+ dwSize _COORD
+ dwCursorPosition _COORD
+ wAttributes word
+ srWindow _SMALL_RECT
+ dwMaximumWindowSize _COORD
+}
+
+type _SMALL_RECT struct {
+ left short
+ top short
+ right short
+ bottom short
+}
+
+type _CONSOLE_CURSOR_INFO struct {
+ dwSize dword
+ bVisible bool
+}
+
+type CallFunc func(u ...uintptr) error
+
+func NewKernel() *Kernel {
+ k := &Kernel{}
+ kernel32 := syscall.NewLazyDLL("kernel32.dll")
+ v := reflect.ValueOf(k).Elem()
+ t := v.Type()
+ for i := 0; i < t.NumField(); i++ {
+ name := t.Field(i).Name
+ f := kernel32.NewProc(name)
+ v.Field(i).Set(reflect.ValueOf(k.Wrap(f)))
+ }
+ return k
+}
+
+func (k *Kernel) Wrap(p *syscall.LazyProc) CallFunc {
+ return func(args ...uintptr) error {
+ var r0 uintptr
+ var e1 syscall.Errno
+ size := uintptr(len(args))
+ if len(args) <= 3 {
+ buf := make([]uintptr, 3)
+ copy(buf, args)
+ r0, _, e1 = syscall.Syscall(p.Addr(), size,
+ buf[0], buf[1], buf[2])
+ } else {
+ buf := make([]uintptr, 6)
+ copy(buf, args)
+ r0, _, e1 = syscall.Syscall6(p.Addr(), size,
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
+ )
+ }
+
+ if int(r0) == 0 {
+ if e1 != 0 {
+ return error(e1)
+ } else {
+ return syscall.EINVAL
+ }
+ }
+ return nil
+ }
+
+}
+
+func GetConsoleScreenBufferInfo() (*_CONSOLE_SCREEN_BUFFER_INFO, error) {
+ t := new(_CONSOLE_SCREEN_BUFFER_INFO)
+ err := kernel.GetConsoleScreenBufferInfo(
+ stdout,
+ uintptr(unsafe.Pointer(t)),
+ )
+ return t, err
+}
+
+func GetConsoleCursorInfo() (*_CONSOLE_CURSOR_INFO, error) {
+ t := new(_CONSOLE_CURSOR_INFO)
+ err := kernel.GetConsoleCursorInfo(stdout, uintptr(unsafe.Pointer(t)))
+ return t, err
+}
+
+func SetConsoleCursorPosition(c *_COORD) error {
+ return kernel.SetConsoleCursorPosition(stdout, c.ptr())
+}
diff --git a/vendor/github.com/containerd/platforms/.gitattributes b/vendor/github.com/containerd/platforms/.gitattributes
new file mode 100644
index 00000000..a0717e4b
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/.gitattributes
@@ -0,0 +1 @@
+*.go text eol=lf
\ No newline at end of file
diff --git a/vendor/github.com/containerd/platforms/.golangci.yml b/vendor/github.com/containerd/platforms/.golangci.yml
new file mode 100644
index 00000000..d574fe11
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/.golangci.yml
@@ -0,0 +1,32 @@
+linters:
+ enable:
+ - copyloopvar
+ - gofmt
+ - goimports
+ - gosec
+ - ineffassign
+ - misspell
+ - nolintlint
+ - revive
+ - staticcheck
+ - tenv # Detects using os.Setenv instead of t.Setenv since Go 1.17
+ - unconvert
+ - unused
+ - govet
+ - dupword # Checks for duplicate words in the source code
+ disable:
+ - errcheck
+
+run:
+ timeout: 5m
+
+issues:
+ exclude-dirs:
+ - api
+ - cluster
+ - design
+ - docs
+ - docs/man
+ - releases
+ - reports
+ - test # e2e scripts
diff --git a/vendor/github.com/docker/cli/LICENSE b/vendor/github.com/containerd/platforms/LICENSE
similarity index 99%
rename from vendor/github.com/docker/cli/LICENSE
rename to vendor/github.com/containerd/platforms/LICENSE
index 9c8e20ab..584149b6 100644
--- a/vendor/github.com/docker/cli/LICENSE
+++ b/vendor/github.com/containerd/platforms/LICENSE
@@ -176,7 +176,7 @@
END OF TERMS AND CONDITIONS
- Copyright 2013-2017 Docker, Inc.
+ Copyright The containerd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/vendor/github.com/containerd/platforms/README.md b/vendor/github.com/containerd/platforms/README.md
new file mode 100644
index 00000000..2059de77
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/README.md
@@ -0,0 +1,32 @@
+# platforms
+
+A Go package for formatting, normalizing and matching container platforms.
+
+This package is based on the Open Containers Image Spec definition of a [platform](https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/descriptor.go#L52).
+
+## Platform Specifier
+
+While the OCI platform specifications provide a tool for components to
+specify structured information, user input typically doesn't need the full
+context and much can be inferred. To solve this problem, this package introduces
+"specifiers". A specifier has the format
+`||/[/]`. The user can provide either the
+operating system or the architecture or both.
+
+An example of a common specifier is `linux/amd64`. If the host has a default
+runtime that matches this, the user can simply provide the component that
+matters. For example, if an image provides `amd64` and `arm64` support, the
+operating system, `linux` can be inferred, so they only have to provide
+`arm64` or `amd64`. Similar behavior is implemented for operating systems,
+where the architecture may be known but a runtime may support images from
+different operating systems.
+
+## Project details
+
+**platforms** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
+As a containerd sub-project, you will find the:
+ * [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
+ * [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
+ * and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
+
+information in our [`containerd/project`](https://github.com/containerd/project) repository.
\ No newline at end of file
diff --git a/vendor/github.com/containerd/platforms/compare.go b/vendor/github.com/containerd/platforms/compare.go
new file mode 100644
index 00000000..24403f3b
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/compare.go
@@ -0,0 +1,260 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms
+
+import (
+ "strconv"
+ "strings"
+
+ specs "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+// MatchComparer is able to match and compare platforms to
+// filter and sort platforms.
+type MatchComparer interface {
+ Matcher
+
+ Less(specs.Platform, specs.Platform) bool
+}
+
+type platformVersions struct {
+ major []int
+ minor []int
+}
+
+var arm64variantToVersion = map[string]platformVersions{
+ "v8": {[]int{8}, []int{0}},
+ "v8.0": {[]int{8}, []int{0}},
+ "v8.1": {[]int{8}, []int{1}},
+ "v8.2": {[]int{8}, []int{2}},
+ "v8.3": {[]int{8}, []int{3}},
+ "v8.4": {[]int{8}, []int{4}},
+ "v8.5": {[]int{8}, []int{5}},
+ "v8.6": {[]int{8}, []int{6}},
+ "v8.7": {[]int{8}, []int{7}},
+ "v8.8": {[]int{8}, []int{8}},
+ "v8.9": {[]int{8}, []int{9}},
+ "v9": {[]int{9, 8}, []int{0, 5}},
+ "v9.0": {[]int{9, 8}, []int{0, 5}},
+ "v9.1": {[]int{9, 8}, []int{1, 6}},
+ "v9.2": {[]int{9, 8}, []int{2, 7}},
+ "v9.3": {[]int{9, 8}, []int{3, 8}},
+ "v9.4": {[]int{9, 8}, []int{4, 9}},
+ "v9.5": {[]int{9, 8}, []int{5, 9}},
+ "v9.6": {[]int{9, 8}, []int{6, 9}},
+ "v9.7": {[]int{9, 8}, []int{7, 9}},
+}
+
+// platformVector returns an (ordered) vector of appropriate specs.Platform
+// objects to try matching for the given platform object (see platforms.Only).
+func platformVector(platform specs.Platform) []specs.Platform {
+ vector := []specs.Platform{platform}
+
+ switch platform.Architecture {
+ case "amd64":
+ if amd64Version, err := strconv.Atoi(strings.TrimPrefix(platform.Variant, "v")); err == nil && amd64Version > 1 {
+ for amd64Version--; amd64Version >= 1; amd64Version-- {
+ vector = append(vector, specs.Platform{
+ Architecture: platform.Architecture,
+ OS: platform.OS,
+ OSVersion: platform.OSVersion,
+ OSFeatures: platform.OSFeatures,
+ Variant: "v" + strconv.Itoa(amd64Version),
+ })
+ }
+ }
+ vector = append(vector, specs.Platform{
+ Architecture: "386",
+ OS: platform.OS,
+ OSVersion: platform.OSVersion,
+ OSFeatures: platform.OSFeatures,
+ })
+ case "arm":
+ if armVersion, err := strconv.Atoi(strings.TrimPrefix(platform.Variant, "v")); err == nil && armVersion > 5 {
+ for armVersion--; armVersion >= 5; armVersion-- {
+ vector = append(vector, specs.Platform{
+ Architecture: platform.Architecture,
+ OS: platform.OS,
+ OSVersion: platform.OSVersion,
+ OSFeatures: platform.OSFeatures,
+ Variant: "v" + strconv.Itoa(armVersion),
+ })
+ }
+ }
+ case "arm64":
+ variant := platform.Variant
+ if variant == "" {
+ variant = "v8"
+ }
+
+ vector = []specs.Platform{} // Reset vector, the first variant will be added in loop.
+ arm64Versions, ok := arm64variantToVersion[variant]
+ if !ok {
+ break
+ }
+ for i, major := range arm64Versions.major {
+ for minor := arm64Versions.minor[i]; minor >= 0; minor-- {
+ arm64Variant := "v" + strconv.Itoa(major) + "." + strconv.Itoa(minor)
+ if minor == 0 {
+ arm64Variant = "v" + strconv.Itoa(major)
+ }
+ vector = append(vector, specs.Platform{
+ Architecture: "arm64",
+ OS: platform.OS,
+ OSVersion: platform.OSVersion,
+ OSFeatures: platform.OSFeatures,
+ Variant: arm64Variant,
+ })
+ }
+ }
+
+ // All arm64/v8.x and arm64/v9.x are compatible with arm/v8 (32-bits) and below.
+ // There's no arm64 v9 variant, so it's normalized to v8.
+ if strings.HasPrefix(variant, "v8") || strings.HasPrefix(variant, "v9") {
+ variant = "v8"
+ }
+ vector = append(vector, platformVector(specs.Platform{
+ Architecture: "arm",
+ OS: platform.OS,
+ OSVersion: platform.OSVersion,
+ OSFeatures: platform.OSFeatures,
+ Variant: variant,
+ })...)
+ }
+
+ return vector
+}
+
+// Only returns a match comparer for a single platform
+// using default resolution logic for the platform.
+//
+// For arm64/v9.x, will also match arm64/v9.{0..x-1} and arm64/v8.{0..x+5}
+// For arm64/v8.x, will also match arm64/v8.{0..x-1}
+// For arm/v8, will also match arm/v7, arm/v6 and arm/v5
+// For arm/v7, will also match arm/v6 and arm/v5
+// For arm/v6, will also match arm/v5
+// For amd64, will also match 386
+func Only(platform specs.Platform) MatchComparer {
+ return Ordered(platformVector(Normalize(platform))...)
+}
+
+// OnlyStrict returns a match comparer for a single platform.
+//
+// Unlike Only, OnlyStrict does not match sub platforms.
+// So, "arm/vN" will not match "arm/vM" where M < N,
+// and "amd64" will not also match "386".
+//
+// OnlyStrict matches non-canonical forms.
+// So, "arm64" matches "arm/64/v8".
+func OnlyStrict(platform specs.Platform) MatchComparer {
+ return Ordered(Normalize(platform))
+}
+
+// Ordered returns a platform MatchComparer which matches any of the platforms
+// but orders them in order they are provided.
+func Ordered(platforms ...specs.Platform) MatchComparer {
+ matchers := make([]Matcher, len(platforms))
+ for i := range platforms {
+ matchers[i] = NewMatcher(platforms[i])
+ }
+ return orderedPlatformComparer{
+ matchers: matchers,
+ }
+}
+
+// Any returns a platform MatchComparer which matches any of the platforms
+// with no preference for ordering.
+func Any(platforms ...specs.Platform) MatchComparer {
+ matchers := make([]Matcher, len(platforms))
+ for i := range platforms {
+ matchers[i] = NewMatcher(platforms[i])
+ }
+ return anyPlatformComparer{
+ matchers: matchers,
+ }
+}
+
+// All is a platform MatchComparer which matches all platforms
+// with preference for ordering.
+var All MatchComparer = allPlatformComparer{}
+
+type orderedPlatformComparer struct {
+ matchers []Matcher
+}
+
+func (c orderedPlatformComparer) Match(platform specs.Platform) bool {
+ for _, m := range c.matchers {
+ if m.Match(platform) {
+ return true
+ }
+ }
+ return false
+}
+
+func (c orderedPlatformComparer) Less(p1 specs.Platform, p2 specs.Platform) bool {
+ for _, m := range c.matchers {
+ p1m := m.Match(p1)
+ p2m := m.Match(p2)
+ if p1m && !p2m {
+ return true
+ }
+ if p1m || p2m {
+ return false
+ }
+ }
+ return false
+}
+
+type anyPlatformComparer struct {
+ matchers []Matcher
+}
+
+func (c anyPlatformComparer) Match(platform specs.Platform) bool {
+ for _, m := range c.matchers {
+ if m.Match(platform) {
+ return true
+ }
+ }
+ return false
+}
+
+func (c anyPlatformComparer) Less(p1, p2 specs.Platform) bool {
+ var p1m, p2m bool
+ for _, m := range c.matchers {
+ if !p1m && m.Match(p1) {
+ p1m = true
+ }
+ if !p2m && m.Match(p2) {
+ p2m = true
+ }
+ if p1m && p2m {
+ return false
+ }
+ }
+ // If one matches, and the other does, sort match first
+ return p1m && !p2m
+}
+
+type allPlatformComparer struct{}
+
+func (allPlatformComparer) Match(specs.Platform) bool {
+ return true
+}
+
+func (allPlatformComparer) Less(specs.Platform, specs.Platform) bool {
+ return false
+}
diff --git a/vendor/github.com/containerd/platforms/cpuinfo.go b/vendor/github.com/containerd/platforms/cpuinfo.go
new file mode 100644
index 00000000..91f50e8c
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/cpuinfo.go
@@ -0,0 +1,43 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms
+
+import (
+ "runtime"
+ "sync"
+
+ "github.com/containerd/log"
+)
+
+// Present the ARM instruction set architecture, eg: v7, v8
+// Don't use this value directly; call cpuVariant() instead.
+var cpuVariantValue string
+
+var cpuVariantOnce sync.Once
+
+func cpuVariant() string {
+ cpuVariantOnce.Do(func() {
+ if isArmArch(runtime.GOARCH) {
+ var err error
+ cpuVariantValue, err = getCPUVariant()
+ if err != nil {
+ log.L.Errorf("Error getCPUVariant for OS %s: %v", runtime.GOOS, err)
+ }
+ }
+ })
+ return cpuVariantValue
+}
diff --git a/vendor/github.com/containerd/platforms/cpuinfo_linux.go b/vendor/github.com/containerd/platforms/cpuinfo_linux.go
new file mode 100644
index 00000000..98c7001f
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/cpuinfo_linux.go
@@ -0,0 +1,160 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "os"
+ "runtime"
+ "strings"
+
+ "golang.org/x/sys/unix"
+)
+
+// getMachineArch retrieves the machine architecture through system call
+func getMachineArch() (string, error) {
+ var uname unix.Utsname
+ err := unix.Uname(&uname)
+ if err != nil {
+ return "", err
+ }
+
+ arch := string(uname.Machine[:bytes.IndexByte(uname.Machine[:], 0)])
+
+ return arch, nil
+}
+
+// For Linux, the kernel has already detected the ABI, ISA and Features.
+// So we don't need to access the ARM registers to detect platform information
+// by ourselves. We can just parse these information from /proc/cpuinfo
+func getCPUInfo(pattern string) (info string, err error) {
+
+ cpuinfo, err := os.Open("/proc/cpuinfo")
+ if err != nil {
+ return "", err
+ }
+ defer cpuinfo.Close()
+
+ // Start to Parse the Cpuinfo line by line. For SMP SoC, we parse
+ // the first core is enough.
+ scanner := bufio.NewScanner(cpuinfo)
+ for scanner.Scan() {
+ newline := scanner.Text()
+ list := strings.Split(newline, ":")
+
+ if len(list) > 1 && strings.EqualFold(strings.TrimSpace(list[0]), pattern) {
+ return strings.TrimSpace(list[1]), nil
+ }
+ }
+
+ // Check whether the scanner encountered errors
+ err = scanner.Err()
+ if err != nil {
+ return "", err
+ }
+
+ return "", fmt.Errorf("getCPUInfo for pattern %s: %w", pattern, errNotFound)
+}
+
+// getCPUVariantFromArch get CPU variant from arch through a system call
+func getCPUVariantFromArch(arch string) (string, error) {
+
+ var variant string
+
+ arch = strings.ToLower(arch)
+
+ if arch == "aarch64" {
+ variant = "8"
+ } else if arch[0:4] == "armv" && len(arch) >= 5 {
+ // Valid arch format is in form of armvXx
+ switch arch[3:5] {
+ case "v8":
+ variant = "8"
+ case "v7":
+ variant = "7"
+ case "v6":
+ variant = "6"
+ case "v5":
+ variant = "5"
+ case "v4":
+ variant = "4"
+ case "v3":
+ variant = "3"
+ default:
+ variant = "unknown"
+ }
+ } else {
+ return "", fmt.Errorf("getCPUVariantFromArch invalid arch: %s, %w", arch, errInvalidArgument)
+ }
+ return variant, nil
+}
+
+// getCPUVariant returns cpu variant for ARM
+// We first try reading "Cpu architecture" field from /proc/cpuinfo
+// If we can't find it, then fall back using a system call
+// This is to cover running ARM in emulated environment on x86 host as this field in /proc/cpuinfo
+// was not present.
+func getCPUVariant() (string, error) {
+ variant, err := getCPUInfo("Cpu architecture")
+ if err != nil {
+ if errors.Is(err, errNotFound) {
+ // Let's try getting CPU variant from machine architecture
+ arch, err := getMachineArch()
+ if err != nil {
+ return "", fmt.Errorf("failure getting machine architecture: %v", err)
+ }
+
+ variant, err = getCPUVariantFromArch(arch)
+ if err != nil {
+ return "", fmt.Errorf("failure getting CPU variant from machine architecture: %v", err)
+ }
+ } else {
+ return "", fmt.Errorf("failure getting CPU variant: %v", err)
+ }
+ }
+
+ // handle edge case for Raspberry Pi ARMv6 devices (which due to a kernel quirk, report "CPU architecture: 7")
+ // https://www.raspberrypi.org/forums/viewtopic.php?t=12614
+ if runtime.GOARCH == "arm" && variant == "7" {
+ model, err := getCPUInfo("model name")
+ if err == nil && strings.HasPrefix(strings.ToLower(model), "armv6-compatible") {
+ variant = "6"
+ }
+ }
+
+ switch strings.ToLower(variant) {
+ case "8", "aarch64":
+ variant = "v8"
+ case "7", "7m", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)":
+ variant = "v7"
+ case "6", "6tej":
+ variant = "v6"
+ case "5", "5t", "5te", "5tej":
+ variant = "v5"
+ case "4", "4t":
+ variant = "v4"
+ case "3":
+ variant = "v3"
+ default:
+ variant = "unknown"
+ }
+
+ return variant, nil
+}
diff --git a/vendor/github.com/containerd/platforms/cpuinfo_other.go b/vendor/github.com/containerd/platforms/cpuinfo_other.go
new file mode 100644
index 00000000..97a1fe8a
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/cpuinfo_other.go
@@ -0,0 +1,55 @@
+//go:build !linux
+
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms
+
+import (
+ "fmt"
+ "runtime"
+)
+
+func getCPUVariant() (string, error) {
+
+ var variant string
+
+ if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
+ // Windows/Darwin only supports v7 for ARM32 and v8 for ARM64 and so we can use
+ // runtime.GOARCH to determine the variants
+ switch runtime.GOARCH {
+ case "arm64":
+ variant = "v8"
+ case "arm":
+ variant = "v7"
+ default:
+ variant = "unknown"
+ }
+ } else if runtime.GOOS == "freebsd" {
+ // FreeBSD supports ARMv6 and ARMv7 as well as ARMv4 and ARMv5 (though deprecated)
+ // detecting those variants is currently unimplemented
+ switch runtime.GOARCH {
+ case "arm64":
+ variant = "v8"
+ default:
+ variant = "unknown"
+ }
+ } else {
+ return "", fmt.Errorf("getCPUVariant for OS %s: %v", runtime.GOOS, errNotImplemented)
+ }
+
+ return variant, nil
+}
diff --git a/vendor/github.com/containerd/platforms/database.go b/vendor/github.com/containerd/platforms/database.go
new file mode 100644
index 00000000..7a6f0d98
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/database.go
@@ -0,0 +1,111 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms
+
+import (
+ "runtime"
+ "strings"
+)
+
+// These function are generated from https://golang.org/src/go/build/syslist.go.
+//
+// We use switch statements because they are slightly faster than map lookups
+// and use a little less memory.
+
+// isKnownOS returns true if we know about the operating system.
+//
+// The OS value should be normalized before calling this function.
+func isKnownOS(os string) bool {
+ switch os {
+ case "aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "ios", "js", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos":
+ return true
+ }
+ return false
+}
+
+// isArmArch returns true if the architecture is ARM.
+//
+// The arch value should be normalized before being passed to this function.
+func isArmArch(arch string) bool {
+ switch arch {
+ case "arm", "arm64":
+ return true
+ }
+ return false
+}
+
+// isKnownArch returns true if we know about the architecture.
+//
+// The arch value should be normalized before being passed to this function.
+func isKnownArch(arch string) bool {
+ switch arch {
+ case "386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64", "ppc64le", "loong64", "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le", "ppc", "riscv", "riscv64", "s390", "s390x", "sparc", "sparc64", "wasm":
+ return true
+ }
+ return false
+}
+
+func normalizeOS(os string) string {
+ if os == "" {
+ return runtime.GOOS
+ }
+ os = strings.ToLower(os)
+
+ switch os {
+ case "macos":
+ os = "darwin"
+ }
+ return os
+}
+
+// normalizeArch normalizes the architecture.
+func normalizeArch(arch, variant string) (string, string) {
+ arch, variant = strings.ToLower(arch), strings.ToLower(variant)
+ switch arch {
+ case "i386":
+ arch = "386"
+ variant = ""
+ case "x86_64", "x86-64", "amd64":
+ arch = "amd64"
+ if variant == "v1" {
+ variant = ""
+ }
+ case "aarch64", "arm64":
+ arch = "arm64"
+ switch variant {
+ case "8", "v8", "v8.0":
+ variant = ""
+ case "9", "9.0", "v9.0":
+ variant = "v9"
+ }
+ case "armhf":
+ arch = "arm"
+ variant = "v7"
+ case "armel":
+ arch = "arm"
+ variant = "v6"
+ case "arm":
+ switch variant {
+ case "", "7":
+ variant = "v7"
+ case "5", "6", "8":
+ variant = "v" + variant
+ }
+ }
+
+ return arch, variant
+}
diff --git a/vendor/github.com/containerd/platforms/defaults.go b/vendor/github.com/containerd/platforms/defaults.go
new file mode 100644
index 00000000..9d898d60
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/defaults.go
@@ -0,0 +1,29 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms
+
+// DefaultString returns the default string specifier for the platform,
+// with [PR#6](https://github.com/containerd/platforms/pull/6) the result
+// may now also include the OSVersion from the provided platform specification.
+func DefaultString() string {
+ return FormatAll(DefaultSpec())
+}
+
+// DefaultStrict returns strict form of Default.
+func DefaultStrict() MatchComparer {
+ return OnlyStrict(DefaultSpec())
+}
diff --git a/vendor/github.com/containerd/platforms/defaults_darwin.go b/vendor/github.com/containerd/platforms/defaults_darwin.go
new file mode 100644
index 00000000..72355ca8
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/defaults_darwin.go
@@ -0,0 +1,44 @@
+//go:build darwin
+
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms
+
+import (
+ "runtime"
+
+ specs "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+// DefaultSpec returns the current platform's default platform specification.
+func DefaultSpec() specs.Platform {
+ return specs.Platform{
+ OS: runtime.GOOS,
+ Architecture: runtime.GOARCH,
+ // The Variant field will be empty if arch != ARM.
+ Variant: cpuVariant(),
+ }
+}
+
+// Default returns the default matcher for the platform.
+func Default() MatchComparer {
+ return Ordered(DefaultSpec(), specs.Platform{
+ // darwin runtime also supports Linux binary via runu/LKL
+ OS: "linux",
+ Architecture: runtime.GOARCH,
+ })
+}
diff --git a/vendor/github.com/containerd/platforms/defaults_freebsd.go b/vendor/github.com/containerd/platforms/defaults_freebsd.go
new file mode 100644
index 00000000..d3fe89e0
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/defaults_freebsd.go
@@ -0,0 +1,43 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms
+
+import (
+ "runtime"
+
+ specs "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+// DefaultSpec returns the current platform's default platform specification.
+func DefaultSpec() specs.Platform {
+ return specs.Platform{
+ OS: runtime.GOOS,
+ Architecture: runtime.GOARCH,
+ // The Variant field will be empty if arch != ARM.
+ Variant: cpuVariant(),
+ }
+}
+
+// Default returns the default matcher for the platform.
+func Default() MatchComparer {
+ return Ordered(DefaultSpec(), specs.Platform{
+ OS: "linux",
+ Architecture: runtime.GOARCH,
+ // The Variant field will be empty if arch != ARM.
+ Variant: cpuVariant(),
+ })
+}
diff --git a/vendor/github.com/containerd/platforms/defaults_unix.go b/vendor/github.com/containerd/platforms/defaults_unix.go
new file mode 100644
index 00000000..44acc47e
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/defaults_unix.go
@@ -0,0 +1,40 @@
+//go:build !windows && !darwin && !freebsd
+
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms
+
+import (
+ "runtime"
+
+ specs "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+// DefaultSpec returns the current platform's default platform specification.
+func DefaultSpec() specs.Platform {
+ return specs.Platform{
+ OS: runtime.GOOS,
+ Architecture: runtime.GOARCH,
+ // The Variant field will be empty if arch != ARM.
+ Variant: cpuVariant(),
+ }
+}
+
+// Default returns the default matcher for the platform.
+func Default() MatchComparer {
+ return Only(DefaultSpec())
+}
diff --git a/vendor/github.com/containerd/platforms/defaults_windows.go b/vendor/github.com/containerd/platforms/defaults_windows.go
new file mode 100644
index 00000000..0165adea
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/defaults_windows.go
@@ -0,0 +1,42 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms
+
+import (
+ "fmt"
+ "runtime"
+
+ specs "github.com/opencontainers/image-spec/specs-go/v1"
+ "golang.org/x/sys/windows"
+)
+
+// DefaultSpec returns the current platform's default platform specification.
+func DefaultSpec() specs.Platform {
+ major, minor, build := windows.RtlGetNtVersionNumbers()
+ return specs.Platform{
+ OS: runtime.GOOS,
+ Architecture: runtime.GOARCH,
+ OSVersion: fmt.Sprintf("%d.%d.%d", major, minor, build),
+ // The Variant field will be empty if arch != ARM.
+ Variant: cpuVariant(),
+ }
+}
+
+// Default returns the current platform's default platform specification.
+func Default() MatchComparer {
+ return Only(DefaultSpec())
+}
diff --git a/vendor/github.com/containerd/platforms/errors.go b/vendor/github.com/containerd/platforms/errors.go
new file mode 100644
index 00000000..5ad721e7
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/errors.go
@@ -0,0 +1,30 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms
+
+import "errors"
+
+// These errors mirror the errors defined in [github.com/containerd/containerd/errdefs],
+// however, they are not exported as they are not expected to be used as sentinel
+// errors by consumers of this package.
+//
+//nolint:unused // not all errors are used on all platforms.
+var (
+ errNotFound = errors.New("not found")
+ errInvalidArgument = errors.New("invalid argument")
+ errNotImplemented = errors.New("not implemented")
+)
diff --git a/vendor/github.com/containerd/platforms/platform_windows_compat.go b/vendor/github.com/containerd/platforms/platform_windows_compat.go
new file mode 100644
index 00000000..7f3d9966
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/platform_windows_compat.go
@@ -0,0 +1,156 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms
+
+import (
+ "strconv"
+ "strings"
+
+ specs "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+// windowsOSVersion is a wrapper for Windows version information
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx
+type windowsOSVersion struct {
+ Version uint32
+ MajorVersion uint8
+ MinorVersion uint8
+ Build uint16
+}
+
+// Windows Client and Server build numbers.
+//
+// See:
+// https://learn.microsoft.com/en-us/windows/release-health/release-information
+// https://learn.microsoft.com/en-us/windows/release-health/windows-server-release-info
+// https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information
+const (
+ // rs5 (version 1809, codename "Redstone 5") corresponds to Windows Server
+ // 2019 (ltsc2019), and Windows 10 (October 2018 Update).
+ rs5 = 17763
+
+ // v21H2Server corresponds to Windows Server 2022 (ltsc2022).
+ v21H2Server = 20348
+
+ // v22H2Win11 corresponds to Windows 11 (2022 Update).
+ v22H2Win11 = 22621
+)
+
+// List of stable ABI compliant ltsc releases
+// Note: List must be sorted in ascending order
+var compatLTSCReleases = []uint16{
+ v21H2Server,
+}
+
+// CheckHostAndContainerCompat checks if given host and container
+// OS versions are compatible.
+// It includes support for stable ABI compliant versions as well.
+// Every release after WS 2022 will support the previous ltsc
+// container image. Stable ABI is in preview mode for windows 11 client.
+// Refer: https://learn.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/version-compatibility?tabs=windows-server-2022%2Cwindows-10#windows-server-host-os-compatibility
+func checkWindowsHostAndContainerCompat(host, ctr windowsOSVersion) bool {
+ // check major minor versions of host and guest
+ if host.MajorVersion != ctr.MajorVersion ||
+ host.MinorVersion != ctr.MinorVersion {
+ return false
+ }
+
+ // If host is < WS 2022, exact version match is required
+ if host.Build < v21H2Server {
+ return host.Build == ctr.Build
+ }
+
+ var supportedLtscRelease uint16
+ for i := len(compatLTSCReleases) - 1; i >= 0; i-- {
+ if host.Build >= compatLTSCReleases[i] {
+ supportedLtscRelease = compatLTSCReleases[i]
+ break
+ }
+ }
+ return ctr.Build >= supportedLtscRelease && ctr.Build <= host.Build
+}
+
+func getWindowsOSVersion(osVersionPrefix string) windowsOSVersion {
+ if strings.Count(osVersionPrefix, ".") < 2 {
+ return windowsOSVersion{}
+ }
+
+ major, extra, _ := strings.Cut(osVersionPrefix, ".")
+ minor, extra, _ := strings.Cut(extra, ".")
+ build, _, _ := strings.Cut(extra, ".")
+
+ majorVersion, err := strconv.ParseUint(major, 10, 8)
+ if err != nil {
+ return windowsOSVersion{}
+ }
+
+ minorVersion, err := strconv.ParseUint(minor, 10, 8)
+ if err != nil {
+ return windowsOSVersion{}
+ }
+ buildNumber, err := strconv.ParseUint(build, 10, 16)
+ if err != nil {
+ return windowsOSVersion{}
+ }
+
+ return windowsOSVersion{
+ MajorVersion: uint8(majorVersion),
+ MinorVersion: uint8(minorVersion),
+ Build: uint16(buildNumber),
+ }
+}
+
+func winRevision(v string) int {
+ parts := strings.Split(v, ".")
+ if len(parts) < 4 {
+ return 0
+ }
+ r, err := strconv.Atoi(parts[3])
+ if err != nil {
+ return 0
+ }
+ return r
+}
+
+type windowsVersionMatcher struct {
+ windowsOSVersion
+}
+
+func (m windowsVersionMatcher) Match(v string) bool {
+ if m.isEmpty() || v == "" {
+ return true
+ }
+ osv := getWindowsOSVersion(v)
+ return checkWindowsHostAndContainerCompat(m.windowsOSVersion, osv)
+}
+
+func (m windowsVersionMatcher) isEmpty() bool {
+ return m.MajorVersion == 0 && m.MinorVersion == 0 && m.Build == 0
+}
+
+type windowsMatchComparer struct {
+ Matcher
+}
+
+func (c *windowsMatchComparer) Less(p1, p2 specs.Platform) bool {
+ m1, m2 := c.Match(p1), c.Match(p2)
+ if m1 && m2 {
+ r1, r2 := winRevision(p1.OSVersion), winRevision(p2.OSVersion)
+ return r1 > r2
+ }
+ return m1 && !m2
+}
diff --git a/vendor/github.com/containerd/platforms/platforms.go b/vendor/github.com/containerd/platforms/platforms.go
new file mode 100644
index 00000000..14d65abd
--- /dev/null
+++ b/vendor/github.com/containerd/platforms/platforms.go
@@ -0,0 +1,341 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 platforms provides a toolkit for normalizing, matching and
+// specifying container platforms.
+//
+// Centered around OCI platform specifications, we define a string-based
+// specifier syntax that can be used for user input. With a specifier, users
+// only need to specify the parts of the platform that are relevant to their
+// context, providing an operating system or architecture or both.
+//
+// How do I use this package?
+//
+// The vast majority of use cases should simply use the match function with
+// user input. The first step is to parse a specifier into a matcher:
+//
+// m, err := Parse("linux")
+// if err != nil { ... }
+//
+// Once you have a matcher, use it to match against the platform declared by a
+// component, typically from an image or runtime. Since extracting an images
+// platform is a little more involved, we'll use an example against the
+// platform default:
+//
+// if ok := m.Match(Default()); !ok { /* doesn't match */ }
+//
+// This can be composed in loops for resolving runtimes or used as a filter for
+// fetch and select images.
+//
+// More details of the specifier syntax and platform spec follow.
+//
+// # Declaring Platform Support
+//
+// Components that have strict platform requirements should use the OCI
+// platform specification to declare their support. Typically, this will be
+// images and runtimes that should make these declaring which platform they
+// support specifically. This looks roughly as follows:
+//
+// type Platform struct {
+// Architecture string
+// OS string
+// Variant string
+// }
+//
+// Most images and runtimes should at least set Architecture and OS, according
+// to their GOARCH and GOOS values, respectively (follow the OCI image
+// specification when in doubt). ARM should set variant under certain
+// discussions, which are outlined below.
+//
+// # Platform Specifiers
+//
+// While the OCI platform specifications provide a tool for components to
+// specify structured information, user input typically doesn't need the full
+// context and much can be inferred. To solve this problem, we introduced
+// "specifiers". A specifier has the format
+// `||/[/]`. The user can provide either the
+// operating system or the architecture or both.
+//
+// An example of a common specifier is `linux/amd64`. If the host has a default
+// of runtime that matches this, the user can simply provide the component that
+// matters. For example, if a image provides amd64 and arm64 support, the
+// operating system, `linux` can be inferred, so they only have to provide
+// `arm64` or `amd64`. Similar behavior is implemented for operating systems,
+// where the architecture may be known but a runtime may support images from
+// different operating systems.
+//
+// # Normalization
+//
+// Because not all users are familiar with the way the Go runtime represents
+// platforms, several normalizations have been provided to make this package
+// easier to user.
+//
+// The following are performed for architectures:
+//
+// Value Normalized
+// aarch64 arm64
+// armhf arm
+// armel arm/v6
+// i386 386
+// x86_64 amd64
+// x86-64 amd64
+//
+// We also normalize the operating system `macos` to `darwin`.
+//
+// # ARM Support
+//
+// To qualify ARM architecture, the Variant field is used to qualify the arm
+// version. The most common arm version, v7, is represented without the variant
+// unless it is explicitly provided. This is treated as equivalent to armhf. A
+// previous architecture, armel, will be normalized to arm/v6.
+//
+// Similarly, the most common arm64 version v8, and most common amd64 version v1
+// are represented without the variant.
+//
+// While these normalizations are provided, their support on arm platforms has
+// not yet been fully implemented and tested.
+package platforms
+
+import (
+ "fmt"
+ "path"
+ "regexp"
+ "runtime"
+ "strconv"
+ "strings"
+
+ specs "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+var (
+ specifierRe = regexp.MustCompile(`^[A-Za-z0-9_.-]+$`)
+ osAndVersionRe = regexp.MustCompile(`^([A-Za-z0-9_-]+)(?:\(([A-Za-z0-9_.-]*)\))?$`)
+)
+
+const osAndVersionFormat = "%s(%s)"
+
+// Platform is a type alias for convenience, so there is no need to import image-spec package everywhere.
+type Platform = specs.Platform
+
+// Matcher matches platforms specifications, provided by an image or runtime.
+type Matcher interface {
+ Match(platform specs.Platform) bool
+}
+
+// NewMatcher returns a simple matcher based on the provided platform
+// specification. The returned matcher only looks for equality based on os,
+// architecture and variant.
+//
+// One may implement their own matcher if this doesn't provide the required
+// functionality.
+//
+// Applications should opt to use `Match` over directly parsing specifiers.
+func NewMatcher(platform specs.Platform) Matcher {
+ m := &matcher{
+ Platform: Normalize(platform),
+ }
+
+ if platform.OS == "windows" {
+ m.osvM = &windowsVersionMatcher{
+ windowsOSVersion: getWindowsOSVersion(platform.OSVersion),
+ }
+ // In prior versions, on windows, the returned matcher implements a
+ // MatchComprarer interface.
+ // This preserves that behavior for backwards compatibility.
+ //
+ // TODO: This isn't actually used in this package, except for a test case,
+ // which may have been an unintended side of some refactor.
+ // It was likely intended to be used in `Ordered` but it is not since
+ // `Less` that is implemented here ends up getting masked due to wrapping.
+ if runtime.GOOS == "windows" {
+ return &windowsMatchComparer{m}
+ }
+ }
+ return m
+}
+
+type osVerMatcher interface {
+ Match(string) bool
+}
+
+type matcher struct {
+ specs.Platform
+ osvM osVerMatcher
+}
+
+func (m *matcher) Match(platform specs.Platform) bool {
+ normalized := Normalize(platform)
+ return m.OS == normalized.OS &&
+ m.Architecture == normalized.Architecture &&
+ m.Variant == normalized.Variant &&
+ m.matchOSVersion(platform)
+}
+
+func (m *matcher) matchOSVersion(platform specs.Platform) bool {
+ if m.osvM != nil {
+ return m.osvM.Match(platform.OSVersion)
+ }
+ return true
+}
+
+func (m *matcher) String() string {
+ return FormatAll(m.Platform)
+}
+
+// ParseAll parses a list of platform specifiers into a list of platform.
+func ParseAll(specifiers []string) ([]specs.Platform, error) {
+ platforms := make([]specs.Platform, len(specifiers))
+ for i, s := range specifiers {
+ p, err := Parse(s)
+ if err != nil {
+ return nil, fmt.Errorf("invalid platform %s: %w", s, err)
+ }
+ platforms[i] = p
+ }
+ return platforms, nil
+}
+
+// Parse parses the platform specifier syntax into a platform declaration.
+//
+// Platform specifiers are in the format `[()]||[()]/[/]`.
+// The minimum required information for a platform specifier is the operating
+// system or architecture. The OSVersion can be part of the OS like `windows(10.0.17763)`
+// When an OSVersion is specified, then specs.Platform.OSVersion is populated with that value,
+// and an empty string otherwise.
+// If there is only a single string (no slashes), the
+// value will be matched against the known set of operating systems, then fall
+// back to the known set of architectures. The missing component will be
+// inferred based on the local environment.
+func Parse(specifier string) (specs.Platform, error) {
+ if strings.Contains(specifier, "*") {
+ // TODO(stevvooe): need to work out exact wildcard handling
+ return specs.Platform{}, fmt.Errorf("%q: wildcards not yet supported: %w", specifier, errInvalidArgument)
+ }
+
+ // Limit to 4 elements to prevent unbounded split
+ parts := strings.SplitN(specifier, "/", 4)
+
+ var p specs.Platform
+ for i, part := range parts {
+ if i == 0 {
+ // First element is [()]
+ osVer := osAndVersionRe.FindStringSubmatch(part)
+ if osVer == nil {
+ return specs.Platform{}, fmt.Errorf("%q is an invalid OS component of %q: OSAndVersion specifier component must match %q: %w", part, specifier, osAndVersionRe.String(), errInvalidArgument)
+ }
+
+ p.OS = normalizeOS(osVer[1])
+ p.OSVersion = osVer[2]
+ } else {
+ if !specifierRe.MatchString(part) {
+ return specs.Platform{}, fmt.Errorf("%q is an invalid component of %q: platform specifier component must match %q: %w", part, specifier, specifierRe.String(), errInvalidArgument)
+ }
+ }
+ }
+
+ switch len(parts) {
+ case 1:
+ // in this case, we will test that the value might be an OS (with or
+ // without the optional OSVersion specified) and look it up.
+ // If it is not known, we'll treat it as an architecture. Since
+ // we have very little information about the platform here, we are
+ // going to be a little more strict if we don't know about the argument
+ // value.
+ if isKnownOS(p.OS) {
+ // picks a default architecture
+ p.Architecture = runtime.GOARCH
+ if p.Architecture == "arm" && cpuVariant() != "v7" {
+ p.Variant = cpuVariant()
+ }
+
+ return p, nil
+ }
+
+ p.Architecture, p.Variant = normalizeArch(parts[0], "")
+ if p.Architecture == "arm" && p.Variant == "v7" {
+ p.Variant = ""
+ }
+ if isKnownArch(p.Architecture) {
+ p.OS = runtime.GOOS
+ return p, nil
+ }
+
+ return specs.Platform{}, fmt.Errorf("%q: unknown operating system or architecture: %w", specifier, errInvalidArgument)
+ case 2:
+ // In this case, we treat as a regular OS[(OSVersion)]/arch pair. We don't care
+ // about whether or not we know of the platform.
+ p.Architecture, p.Variant = normalizeArch(parts[1], "")
+ if p.Architecture == "arm" && p.Variant == "v7" {
+ p.Variant = ""
+ }
+
+ return p, nil
+ case 3:
+ // we have a fully specified variant, this is rare
+ p.Architecture, p.Variant = normalizeArch(parts[1], parts[2])
+ if p.Architecture == "arm64" && p.Variant == "" {
+ p.Variant = "v8"
+ }
+
+ return p, nil
+ }
+
+ return specs.Platform{}, fmt.Errorf("%q: cannot parse platform specifier: %w", specifier, errInvalidArgument)
+}
+
+// MustParse is like Parses but panics if the specifier cannot be parsed.
+// Simplifies initialization of global variables.
+func MustParse(specifier string) specs.Platform {
+ p, err := Parse(specifier)
+ if err != nil {
+ panic("platform: Parse(" + strconv.Quote(specifier) + "): " + err.Error())
+ }
+ return p
+}
+
+// Format returns a string specifier from the provided platform specification.
+func Format(platform specs.Platform) string {
+ if platform.OS == "" {
+ return "unknown"
+ }
+
+ return path.Join(platform.OS, platform.Architecture, platform.Variant)
+}
+
+// FormatAll returns a string specifier that also includes the OSVersion from the
+// provided platform specification.
+func FormatAll(platform specs.Platform) string {
+ if platform.OS == "" {
+ return "unknown"
+ }
+
+ if platform.OSVersion != "" {
+ OSAndVersion := fmt.Sprintf(osAndVersionFormat, platform.OS, platform.OSVersion)
+ return path.Join(OSAndVersion, platform.Architecture, platform.Variant)
+ }
+ return path.Join(platform.OS, platform.Architecture, platform.Variant)
+}
+
+// Normalize validates and translate the platform to the canonical value.
+//
+// For example, if "Aarch64" is encountered, we change it to "arm64" or if
+// "x86_64" is encountered, it becomes "amd64".
+func Normalize(platform specs.Platform) specs.Platform {
+ platform.OS = normalizeOS(platform.OS)
+ platform.Architecture, platform.Variant = normalizeArch(platform.Architecture, platform.Variant)
+
+ return platform
+}
diff --git a/vendor/github.com/moby/sys/atomicwriter/LICENSE b/vendor/github.com/containerd/stargz-snapshotter/estargz/LICENSE
similarity index 100%
rename from vendor/github.com/moby/sys/atomicwriter/LICENSE
rename to vendor/github.com/containerd/stargz-snapshotter/estargz/LICENSE
diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go
new file mode 100644
index 00000000..8b804b7d
--- /dev/null
+++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go
@@ -0,0 +1,689 @@
+/*
+ Copyright The containerd Authors.
+
+ 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.
+*/
+
+/*
+ Copyright 2019 The Go Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file.
+*/
+
+package estargz
+
+import (
+ "archive/tar"
+ "bytes"
+ "compress/gzip"
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "path"
+ "runtime"
+ "strings"
+ "sync"
+
+ "github.com/containerd/stargz-snapshotter/estargz/errorutil"
+ "github.com/klauspost/compress/zstd"
+ digest "github.com/opencontainers/go-digest"
+ "golang.org/x/sync/errgroup"
+)
+
+type options struct {
+ chunkSize int
+ compressionLevel int
+ prioritizedFiles []string
+ missedPrioritizedFiles *[]string
+ compression Compression
+ ctx context.Context
+ minChunkSize int
+}
+
+type Option func(o *options) error
+
+// WithChunkSize option specifies the chunk size of eStargz blob to build.
+func WithChunkSize(chunkSize int) Option {
+ return func(o *options) error {
+ o.chunkSize = chunkSize
+ return nil
+ }
+}
+
+// WithCompressionLevel option specifies the gzip compression level.
+// The default is gzip.BestCompression.
+// This option will be ignored if WithCompression option is used.
+// See also: https://godoc.org/compress/gzip#pkg-constants
+func WithCompressionLevel(level int) Option {
+ return func(o *options) error {
+ o.compressionLevel = level
+ return nil
+ }
+}
+
+// WithPrioritizedFiles option specifies the list of prioritized files.
+// These files must be complete paths that are absolute or relative to "/"
+// For example, all of "foo/bar", "/foo/bar", "./foo/bar" and "../foo/bar"
+// are treated as "/foo/bar".
+func WithPrioritizedFiles(files []string) Option {
+ return func(o *options) error {
+ o.prioritizedFiles = files
+ return nil
+ }
+}
+
+// WithAllowPrioritizeNotFound makes Build continue the execution even if some
+// of prioritized files specified by WithPrioritizedFiles option aren't found
+// in the input tar. Instead, this records all missed file names to the passed
+// slice.
+func WithAllowPrioritizeNotFound(missedFiles *[]string) Option {
+ return func(o *options) error {
+ if missedFiles == nil {
+ return fmt.Errorf("WithAllowPrioritizeNotFound: slice must be passed")
+ }
+ o.missedPrioritizedFiles = missedFiles
+ return nil
+ }
+}
+
+// WithCompression specifies compression algorithm to be used.
+// Default is gzip.
+func WithCompression(compression Compression) Option {
+ return func(o *options) error {
+ o.compression = compression
+ return nil
+ }
+}
+
+// WithContext specifies a context that can be used for clean canceleration.
+func WithContext(ctx context.Context) Option {
+ return func(o *options) error {
+ o.ctx = ctx
+ return nil
+ }
+}
+
+// WithMinChunkSize option specifies the minimal number of bytes of data
+// must be written in one gzip stream.
+// By increasing this number, one gzip stream can contain multiple files
+// and it hopefully leads to smaller result blob.
+// NOTE: This adds a TOC property that old reader doesn't understand.
+func WithMinChunkSize(minChunkSize int) Option {
+ return func(o *options) error {
+ o.minChunkSize = minChunkSize
+ return nil
+ }
+}
+
+// Blob is an eStargz blob.
+type Blob struct {
+ io.ReadCloser
+ diffID digest.Digester
+ tocDigest digest.Digest
+}
+
+// DiffID returns the digest of uncompressed blob.
+// It is only valid to call DiffID after Close.
+func (b *Blob) DiffID() digest.Digest {
+ return b.diffID.Digest()
+}
+
+// TOCDigest returns the digest of uncompressed TOC JSON.
+func (b *Blob) TOCDigest() digest.Digest {
+ return b.tocDigest
+}
+
+// Build builds an eStargz blob which is an extended version of stargz, from a blob (gzip, zstd
+// or plain tar) passed through the argument. If there are some prioritized files are listed in
+// the option, these files are grouped as "prioritized" and can be used for runtime optimization
+// (e.g. prefetch). This function builds a blob in parallel, with dividing that blob into several
+// (at least the number of runtime.GOMAXPROCS(0)) sub-blobs.
+func Build(tarBlob *io.SectionReader, opt ...Option) (_ *Blob, rErr error) {
+ var opts options
+ opts.compressionLevel = gzip.BestCompression // BestCompression by default
+ for _, o := range opt {
+ if err := o(&opts); err != nil {
+ return nil, err
+ }
+ }
+ if opts.compression == nil {
+ opts.compression = newGzipCompressionWithLevel(opts.compressionLevel)
+ }
+ layerFiles := newTempFiles()
+ ctx := opts.ctx
+ if ctx == nil {
+ ctx = context.Background()
+ }
+ done := make(chan struct{})
+ defer close(done)
+ go func() {
+ select {
+ case <-done:
+ // nop
+ case <-ctx.Done():
+ layerFiles.CleanupAll()
+ }
+ }()
+ defer func() {
+ if rErr != nil {
+ if err := layerFiles.CleanupAll(); err != nil {
+ rErr = fmt.Errorf("failed to cleanup tmp files: %v: %w", err, rErr)
+ }
+ }
+ if cErr := ctx.Err(); cErr != nil {
+ rErr = fmt.Errorf("error from context %q: %w", cErr, rErr)
+ }
+ }()
+ tarBlob, err := decompressBlob(tarBlob, layerFiles)
+ if err != nil {
+ return nil, err
+ }
+ entries, err := sortEntries(tarBlob, opts.prioritizedFiles, opts.missedPrioritizedFiles)
+ if err != nil {
+ return nil, err
+ }
+ var tarParts [][]*entry
+ if opts.minChunkSize > 0 {
+ // Each entry needs to know the size of the current gzip stream so they
+ // cannot be processed in parallel.
+ tarParts = [][]*entry{entries}
+ } else {
+ tarParts = divideEntries(entries, runtime.GOMAXPROCS(0))
+ }
+ writers := make([]*Writer, len(tarParts))
+ payloads := make([]*os.File, len(tarParts))
+ var mu sync.Mutex
+ var eg errgroup.Group
+ for i, parts := range tarParts {
+ i, parts := i, parts
+ // builds verifiable stargz sub-blobs
+ eg.Go(func() error {
+ esgzFile, err := layerFiles.TempFile("", "esgzdata")
+ if err != nil {
+ return err
+ }
+ sw := NewWriterWithCompressor(esgzFile, opts.compression)
+ sw.ChunkSize = opts.chunkSize
+ sw.MinChunkSize = opts.minChunkSize
+ if sw.needsOpenGzEntries == nil {
+ sw.needsOpenGzEntries = make(map[string]struct{})
+ }
+ for _, f := range []string{PrefetchLandmark, NoPrefetchLandmark} {
+ sw.needsOpenGzEntries[f] = struct{}{}
+ }
+ if err := sw.AppendTar(readerFromEntries(parts...)); err != nil {
+ return err
+ }
+ mu.Lock()
+ writers[i] = sw
+ payloads[i] = esgzFile
+ mu.Unlock()
+ return nil
+ })
+ }
+ if err := eg.Wait(); err != nil {
+ rErr = err
+ return nil, err
+ }
+ tocAndFooter, tocDgst, err := closeWithCombine(writers...)
+ if err != nil {
+ rErr = err
+ return nil, err
+ }
+ var rs []io.Reader
+ for _, p := range payloads {
+ fs, err := fileSectionReader(p)
+ if err != nil {
+ return nil, err
+ }
+ rs = append(rs, fs)
+ }
+ diffID := digest.Canonical.Digester()
+ pr, pw := io.Pipe()
+ go func() {
+ r, err := opts.compression.Reader(io.TeeReader(io.MultiReader(append(rs, tocAndFooter)...), pw))
+ if err != nil {
+ pw.CloseWithError(err)
+ return
+ }
+ defer r.Close()
+ if _, err := io.Copy(diffID.Hash(), r); err != nil {
+ pw.CloseWithError(err)
+ return
+ }
+ pw.Close()
+ }()
+ return &Blob{
+ ReadCloser: readCloser{
+ Reader: pr,
+ closeFunc: layerFiles.CleanupAll,
+ },
+ tocDigest: tocDgst,
+ diffID: diffID,
+ }, nil
+}
+
+// closeWithCombine takes unclosed Writers and close them. This also returns the
+// toc that combined all Writers into.
+// Writers doesn't write TOC and footer to the underlying writers so they can be
+// combined into a single eStargz and tocAndFooter returned by this function can
+// be appended at the tail of that combined blob.
+func closeWithCombine(ws ...*Writer) (tocAndFooterR io.Reader, tocDgst digest.Digest, err error) {
+ if len(ws) == 0 {
+ return nil, "", fmt.Errorf("at least one writer must be passed")
+ }
+ for _, w := range ws {
+ if w.closed {
+ return nil, "", fmt.Errorf("writer must be unclosed")
+ }
+ defer func(w *Writer) { w.closed = true }(w)
+ if err := w.closeGz(); err != nil {
+ return nil, "", err
+ }
+ if err := w.bw.Flush(); err != nil {
+ return nil, "", err
+ }
+ }
+ var (
+ mtoc = new(JTOC)
+ currentOffset int64
+ )
+ mtoc.Version = ws[0].toc.Version
+ for _, w := range ws {
+ for _, e := range w.toc.Entries {
+ // Recalculate Offset of non-empty files/chunks
+ if (e.Type == "reg" && e.Size > 0) || e.Type == "chunk" {
+ e.Offset += currentOffset
+ }
+ mtoc.Entries = append(mtoc.Entries, e)
+ }
+ if w.toc.Version > mtoc.Version {
+ mtoc.Version = w.toc.Version
+ }
+ currentOffset += w.cw.n
+ }
+
+ return tocAndFooter(ws[0].compressor, mtoc, currentOffset)
+}
+
+func tocAndFooter(compressor Compressor, toc *JTOC, offset int64) (io.Reader, digest.Digest, error) {
+ buf := new(bytes.Buffer)
+ tocDigest, err := compressor.WriteTOCAndFooter(buf, offset, toc, nil)
+ if err != nil {
+ return nil, "", err
+ }
+ return buf, tocDigest, nil
+}
+
+// divideEntries divides passed entries to the parts at least the number specified by the
+// argument.
+func divideEntries(entries []*entry, minPartsNum int) (set [][]*entry) {
+ var estimatedSize int64
+ for _, e := range entries {
+ estimatedSize += e.header.Size
+ }
+ unitSize := estimatedSize / int64(minPartsNum)
+ var (
+ nextEnd = unitSize
+ offset int64
+ )
+ set = append(set, []*entry{})
+ for _, e := range entries {
+ set[len(set)-1] = append(set[len(set)-1], e)
+ offset += e.header.Size
+ if offset > nextEnd {
+ set = append(set, []*entry{})
+ nextEnd += unitSize
+ }
+ }
+ return
+}
+
+var errNotFound = errors.New("not found")
+
+// sortEntries reads the specified tar blob and returns a list of tar entries.
+// If some of prioritized files are specified, the list starts from these
+// files with keeping the order specified by the argument.
+func sortEntries(in io.ReaderAt, prioritized []string, missedPrioritized *[]string) ([]*entry, error) {
+
+ // Import tar file.
+ intar, err := importTar(in)
+ if err != nil {
+ return nil, fmt.Errorf("failed to sort: %w", err)
+ }
+
+ // Sort the tar file respecting to the prioritized files list.
+ sorted := &tarFile{}
+ for _, l := range prioritized {
+ if err := moveRec(l, intar, sorted); err != nil {
+ if errors.Is(err, errNotFound) && missedPrioritized != nil {
+ *missedPrioritized = append(*missedPrioritized, l)
+ continue // allow not found
+ }
+ return nil, fmt.Errorf("failed to sort tar entries: %w", err)
+ }
+ }
+ if len(prioritized) == 0 {
+ sorted.add(&entry{
+ header: &tar.Header{
+ Name: NoPrefetchLandmark,
+ Typeflag: tar.TypeReg,
+ Size: int64(len([]byte{landmarkContents})),
+ },
+ payload: bytes.NewReader([]byte{landmarkContents}),
+ })
+ } else {
+ sorted.add(&entry{
+ header: &tar.Header{
+ Name: PrefetchLandmark,
+ Typeflag: tar.TypeReg,
+ Size: int64(len([]byte{landmarkContents})),
+ },
+ payload: bytes.NewReader([]byte{landmarkContents}),
+ })
+ }
+
+ // Dump all entry and concatinate them.
+ return append(sorted.dump(), intar.dump()...), nil
+}
+
+// readerFromEntries returns a reader of tar archive that contains entries passed
+// through the arguments.
+func readerFromEntries(entries ...*entry) io.Reader {
+ pr, pw := io.Pipe()
+ go func() {
+ tw := tar.NewWriter(pw)
+ defer tw.Close()
+ for _, entry := range entries {
+ if err := tw.WriteHeader(entry.header); err != nil {
+ pw.CloseWithError(fmt.Errorf("failed to write tar header: %v", err))
+ return
+ }
+ if _, err := io.Copy(tw, entry.payload); err != nil {
+ pw.CloseWithError(fmt.Errorf("failed to write tar payload: %v", err))
+ return
+ }
+ }
+ pw.Close()
+ }()
+ return pr
+}
+
+func importTar(in io.ReaderAt) (*tarFile, error) {
+ tf := &tarFile{}
+ pw, err := newCountReadSeeker(in)
+ if err != nil {
+ return nil, fmt.Errorf("failed to make position watcher: %w", err)
+ }
+ tr := tar.NewReader(pw)
+
+ // Walk through all nodes.
+ for {
+ // Fetch and parse next header.
+ h, err := tr.Next()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return nil, fmt.Errorf("failed to parse tar file, %w", err)
+ }
+ switch cleanEntryName(h.Name) {
+ case PrefetchLandmark, NoPrefetchLandmark:
+ // Ignore existing landmark
+ continue
+ }
+
+ // Add entry. If it already exists, replace it.
+ if _, ok := tf.get(h.Name); ok {
+ tf.remove(h.Name)
+ }
+ tf.add(&entry{
+ header: h,
+ payload: io.NewSectionReader(in, pw.currentPos(), h.Size),
+ })
+ }
+
+ return tf, nil
+}
+
+func moveRec(name string, in *tarFile, out *tarFile) error {
+ name = cleanEntryName(name)
+ if name == "" { // root directory. stop recursion.
+ if e, ok := in.get(name); ok {
+ // entry of the root directory exists. we should move it as well.
+ // this case will occur if tar entries are prefixed with "./", "/", etc.
+ out.add(e)
+ in.remove(name)
+ }
+ return nil
+ }
+
+ _, okIn := in.get(name)
+ _, okOut := out.get(name)
+ if !okIn && !okOut {
+ return fmt.Errorf("file: %q: %w", name, errNotFound)
+ }
+
+ parent, _ := path.Split(strings.TrimSuffix(name, "/"))
+ if err := moveRec(parent, in, out); err != nil {
+ return err
+ }
+ if e, ok := in.get(name); ok && e.header.Typeflag == tar.TypeLink {
+ if err := moveRec(e.header.Linkname, in, out); err != nil {
+ return err
+ }
+ }
+ if e, ok := in.get(name); ok {
+ out.add(e)
+ in.remove(name)
+ }
+ return nil
+}
+
+type entry struct {
+ header *tar.Header
+ payload io.ReadSeeker
+}
+
+type tarFile struct {
+ index map[string]*entry
+ stream []*entry
+}
+
+func (f *tarFile) add(e *entry) {
+ if f.index == nil {
+ f.index = make(map[string]*entry)
+ }
+ f.index[cleanEntryName(e.header.Name)] = e
+ f.stream = append(f.stream, e)
+}
+
+func (f *tarFile) remove(name string) {
+ name = cleanEntryName(name)
+ if f.index != nil {
+ delete(f.index, name)
+ }
+ var filtered []*entry
+ for _, e := range f.stream {
+ if cleanEntryName(e.header.Name) == name {
+ continue
+ }
+ filtered = append(filtered, e)
+ }
+ f.stream = filtered
+}
+
+func (f *tarFile) get(name string) (e *entry, ok bool) {
+ if f.index == nil {
+ return nil, false
+ }
+ e, ok = f.index[cleanEntryName(name)]
+ return
+}
+
+func (f *tarFile) dump() []*entry {
+ return f.stream
+}
+
+type readCloser struct {
+ io.Reader
+ closeFunc func() error
+}
+
+func (rc readCloser) Close() error {
+ return rc.closeFunc()
+}
+
+func fileSectionReader(file *os.File) (*io.SectionReader, error) {
+ info, err := file.Stat()
+ if err != nil {
+ return nil, err
+ }
+ return io.NewSectionReader(file, 0, info.Size()), nil
+}
+
+func newTempFiles() *tempFiles {
+ return &tempFiles{}
+}
+
+type tempFiles struct {
+ files []*os.File
+ filesMu sync.Mutex
+ cleanupOnce sync.Once
+}
+
+func (tf *tempFiles) TempFile(dir, pattern string) (*os.File, error) {
+ f, err := os.CreateTemp(dir, pattern)
+ if err != nil {
+ return nil, err
+ }
+ tf.filesMu.Lock()
+ tf.files = append(tf.files, f)
+ tf.filesMu.Unlock()
+ return f, nil
+}
+
+func (tf *tempFiles) CleanupAll() (err error) {
+ tf.cleanupOnce.Do(func() {
+ err = tf.cleanupAll()
+ })
+ return
+}
+
+func (tf *tempFiles) cleanupAll() error {
+ tf.filesMu.Lock()
+ defer tf.filesMu.Unlock()
+ var allErr []error
+ for _, f := range tf.files {
+ if err := f.Close(); err != nil {
+ allErr = append(allErr, err)
+ }
+ if err := os.Remove(f.Name()); err != nil {
+ allErr = append(allErr, err)
+ }
+ }
+ tf.files = nil
+ return errorutil.Aggregate(allErr)
+}
+
+func newCountReadSeeker(r io.ReaderAt) (*countReadSeeker, error) {
+ pos := int64(0)
+ return &countReadSeeker{r: r, cPos: &pos}, nil
+}
+
+type countReadSeeker struct {
+ r io.ReaderAt
+ cPos *int64
+
+ mu sync.Mutex
+}
+
+func (cr *countReadSeeker) Read(p []byte) (int, error) {
+ cr.mu.Lock()
+ defer cr.mu.Unlock()
+
+ n, err := cr.r.ReadAt(p, *cr.cPos)
+ if err == nil {
+ *cr.cPos += int64(n)
+ }
+ return n, err
+}
+
+func (cr *countReadSeeker) Seek(offset int64, whence int) (int64, error) {
+ cr.mu.Lock()
+ defer cr.mu.Unlock()
+
+ switch whence {
+ default:
+ return 0, fmt.Errorf("unknown whence: %v", whence)
+ case io.SeekStart:
+ case io.SeekCurrent:
+ offset += *cr.cPos
+ case io.SeekEnd:
+ return 0, fmt.Errorf("unsupported whence: %v", whence)
+ }
+
+ if offset < 0 {
+ return 0, fmt.Errorf("invalid offset")
+ }
+ *cr.cPos = offset
+ return offset, nil
+}
+
+func (cr *countReadSeeker) currentPos() int64 {
+ cr.mu.Lock()
+ defer cr.mu.Unlock()
+
+ return *cr.cPos
+}
+
+func decompressBlob(org *io.SectionReader, tmp *tempFiles) (*io.SectionReader, error) {
+ if org.Size() < 4 {
+ return org, nil
+ }
+ src := make([]byte, 4)
+ if _, err := org.Read(src); err != nil && err != io.EOF {
+ return nil, err
+ }
+ var dR io.Reader
+ if bytes.Equal([]byte{0x1F, 0x8B, 0x08}, src[:3]) {
+ // gzip
+ dgR, err := gzip.NewReader(io.NewSectionReader(org, 0, org.Size()))
+ if err != nil {
+ return nil, err
+ }
+ defer dgR.Close()
+ dR = io.Reader(dgR)
+ } else if bytes.Equal([]byte{0x28, 0xb5, 0x2f, 0xfd}, src[:4]) {
+ // zstd
+ dzR, err := zstd.NewReader(io.NewSectionReader(org, 0, org.Size()))
+ if err != nil {
+ return nil, err
+ }
+ defer dzR.Close()
+ dR = io.Reader(dzR)
+ } else {
+ // uncompressed
+ return io.NewSectionReader(org, 0, org.Size()), nil
+ }
+ b, err := tmp.TempFile("", "uncompresseddata")
+ if err != nil {
+ return nil, err
+ }
+ if _, err := io.Copy(b, dR); err != nil {
+ return nil, err
+ }
+ return fileSectionReader(b)
+}
diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/errorutil/errors.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/errorutil/errors.go
new file mode 100644
index 00000000..6de78b02
--- /dev/null
+++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/errorutil/errors.go
@@ -0,0 +1,40 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 errorutil
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+)
+
+// Aggregate combines a list of errors into a single new error.
+func Aggregate(errs []error) error {
+ switch len(errs) {
+ case 0:
+ return nil
+ case 1:
+ return errs[0]
+ default:
+ points := make([]string, len(errs)+1)
+ points[0] = fmt.Sprintf("%d error(s) occurred:", len(errs))
+ for i, err := range errs {
+ points[i+1] = fmt.Sprintf("* %s", err)
+ }
+ return errors.New(strings.Join(points, "\n\t"))
+ }
+}
diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go
new file mode 100644
index 00000000..f4d55465
--- /dev/null
+++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go
@@ -0,0 +1,1223 @@
+/*
+ Copyright The containerd Authors.
+
+ 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.
+*/
+
+/*
+ Copyright 2019 The Go Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file.
+*/
+
+package estargz
+
+import (
+ "bufio"
+ "bytes"
+ "compress/gzip"
+ "crypto/sha256"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "os"
+ "path"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/containerd/stargz-snapshotter/estargz/errorutil"
+ digest "github.com/opencontainers/go-digest"
+ "github.com/vbatts/tar-split/archive/tar"
+)
+
+// A Reader permits random access reads from a stargz file.
+type Reader struct {
+ sr *io.SectionReader
+ toc *JTOC
+ tocDigest digest.Digest
+
+ // m stores all non-chunk entries, keyed by name.
+ m map[string]*TOCEntry
+
+ // chunks stores all TOCEntry values for regular files that
+ // are split up. For a file with a single chunk, it's only
+ // stored in m.
+ chunks map[string][]*TOCEntry
+
+ decompressor Decompressor
+}
+
+type openOpts struct {
+ tocOffset int64
+ decompressors []Decompressor
+ telemetry *Telemetry
+}
+
+// OpenOption is an option used during opening the layer
+type OpenOption func(o *openOpts) error
+
+// WithTOCOffset option specifies the offset of TOC
+func WithTOCOffset(tocOffset int64) OpenOption {
+ return func(o *openOpts) error {
+ o.tocOffset = tocOffset
+ return nil
+ }
+}
+
+// WithDecompressors option specifies decompressors to use.
+// Default is gzip-based decompressor.
+func WithDecompressors(decompressors ...Decompressor) OpenOption {
+ return func(o *openOpts) error {
+ o.decompressors = decompressors
+ return nil
+ }
+}
+
+// WithTelemetry option specifies the telemetry hooks
+func WithTelemetry(telemetry *Telemetry) OpenOption {
+ return func(o *openOpts) error {
+ o.telemetry = telemetry
+ return nil
+ }
+}
+
+// MeasureLatencyHook is a func which takes start time and records the diff
+type MeasureLatencyHook func(time.Time)
+
+// Telemetry is a struct which defines telemetry hooks. By implementing these hooks you should be able to record
+// the latency metrics of the respective steps of estargz open operation. To be used with estargz.OpenWithTelemetry(...)
+type Telemetry struct {
+ GetFooterLatency MeasureLatencyHook // measure time to get stargz footer (in milliseconds)
+ GetTocLatency MeasureLatencyHook // measure time to GET TOC JSON (in milliseconds)
+ DeserializeTocLatency MeasureLatencyHook // measure time to deserialize TOC JSON (in milliseconds)
+}
+
+// Open opens a stargz file for reading.
+// The behavior is configurable using options.
+//
+// Note that each entry name is normalized as the path that is relative to root.
+func Open(sr *io.SectionReader, opt ...OpenOption) (*Reader, error) {
+ var opts openOpts
+ for _, o := range opt {
+ if err := o(&opts); err != nil {
+ return nil, err
+ }
+ }
+
+ gzipCompressors := []Decompressor{new(GzipDecompressor), new(LegacyGzipDecompressor)}
+ decompressors := append(gzipCompressors, opts.decompressors...)
+
+ // Determine the size to fetch. Try to fetch as many bytes as possible.
+ fetchSize := maxFooterSize(sr.Size(), decompressors...)
+ if maybeTocOffset := opts.tocOffset; maybeTocOffset > fetchSize {
+ if maybeTocOffset > sr.Size() {
+ return nil, fmt.Errorf("blob size %d is smaller than the toc offset", sr.Size())
+ }
+ fetchSize = sr.Size() - maybeTocOffset
+ }
+
+ start := time.Now() // before getting layer footer
+ footer := make([]byte, fetchSize)
+ if _, err := sr.ReadAt(footer, sr.Size()-fetchSize); err != nil {
+ return nil, fmt.Errorf("error reading footer: %v", err)
+ }
+ if opts.telemetry != nil && opts.telemetry.GetFooterLatency != nil {
+ opts.telemetry.GetFooterLatency(start)
+ }
+
+ var allErr []error
+ var found bool
+ var r *Reader
+ for _, d := range decompressors {
+ fSize := d.FooterSize()
+ fOffset := positive(int64(len(footer)) - fSize)
+ maybeTocBytes := footer[:fOffset]
+ _, tocOffset, tocSize, err := d.ParseFooter(footer[fOffset:])
+ if err != nil {
+ allErr = append(allErr, err)
+ continue
+ }
+ if tocOffset >= 0 && tocSize <= 0 {
+ tocSize = sr.Size() - tocOffset - fSize
+ }
+ if tocOffset >= 0 && tocSize < int64(len(maybeTocBytes)) {
+ maybeTocBytes = maybeTocBytes[:tocSize]
+ }
+ r, err = parseTOC(d, sr, tocOffset, tocSize, maybeTocBytes, opts)
+ if err == nil {
+ found = true
+ break
+ }
+ allErr = append(allErr, err)
+ }
+ if !found {
+ return nil, errorutil.Aggregate(allErr)
+ }
+ if err := r.initFields(); err != nil {
+ return nil, fmt.Errorf("failed to initialize fields of entries: %v", err)
+ }
+ return r, nil
+}
+
+// OpenFooter extracts and parses footer from the given blob.
+// only supports gzip-based eStargz.
+func OpenFooter(sr *io.SectionReader) (tocOffset int64, footerSize int64, rErr error) {
+ if sr.Size() < FooterSize && sr.Size() < legacyFooterSize {
+ return 0, 0, fmt.Errorf("blob size %d is smaller than the footer size", sr.Size())
+ }
+ var footer [FooterSize]byte
+ if _, err := sr.ReadAt(footer[:], sr.Size()-FooterSize); err != nil {
+ return 0, 0, fmt.Errorf("error reading footer: %v", err)
+ }
+ var allErr []error
+ for _, d := range []Decompressor{new(GzipDecompressor), new(LegacyGzipDecompressor)} {
+ fSize := d.FooterSize()
+ fOffset := positive(int64(len(footer)) - fSize)
+ _, tocOffset, _, err := d.ParseFooter(footer[fOffset:])
+ if err == nil {
+ return tocOffset, fSize, err
+ }
+ allErr = append(allErr, err)
+ }
+ return 0, 0, errorutil.Aggregate(allErr)
+}
+
+// initFields populates the Reader from r.toc after decoding it from
+// JSON.
+//
+// Unexported fields are populated and TOCEntry fields that were
+// implicit in the JSON are populated.
+func (r *Reader) initFields() error {
+ r.m = make(map[string]*TOCEntry, len(r.toc.Entries))
+ r.chunks = make(map[string][]*TOCEntry)
+ var lastPath string
+ uname := map[int]string{}
+ gname := map[int]string{}
+ var lastRegEnt *TOCEntry
+ var chunkTopIndex int
+ for i, ent := range r.toc.Entries {
+ ent.Name = cleanEntryName(ent.Name)
+ switch ent.Type {
+ case "reg", "chunk":
+ if ent.Offset != r.toc.Entries[chunkTopIndex].Offset {
+ chunkTopIndex = i
+ }
+ ent.chunkTopIndex = chunkTopIndex
+ }
+ if ent.Type == "reg" {
+ lastRegEnt = ent
+ }
+ if ent.Type == "chunk" {
+ ent.Name = lastPath
+ r.chunks[ent.Name] = append(r.chunks[ent.Name], ent)
+ if ent.ChunkSize == 0 && lastRegEnt != nil {
+ ent.ChunkSize = lastRegEnt.Size - ent.ChunkOffset
+ }
+ } else {
+ lastPath = ent.Name
+
+ if ent.Uname != "" {
+ uname[ent.UID] = ent.Uname
+ } else {
+ ent.Uname = uname[ent.UID]
+ }
+ if ent.Gname != "" {
+ gname[ent.GID] = ent.Gname
+ } else {
+ ent.Gname = uname[ent.GID]
+ }
+
+ ent.modTime, _ = time.Parse(time.RFC3339, ent.ModTime3339)
+
+ if ent.Type == "dir" {
+ ent.NumLink++ // Parent dir links to this directory
+ }
+ r.m[ent.Name] = ent
+ }
+ if ent.Type == "reg" && ent.ChunkSize > 0 && ent.ChunkSize < ent.Size {
+ r.chunks[ent.Name] = make([]*TOCEntry, 0, ent.Size/ent.ChunkSize+1)
+ r.chunks[ent.Name] = append(r.chunks[ent.Name], ent)
+ }
+ if ent.ChunkSize == 0 && ent.Size != 0 {
+ ent.ChunkSize = ent.Size
+ }
+ }
+
+ // Populate children, add implicit directories:
+ for _, ent := range r.toc.Entries {
+ if ent.Type == "chunk" {
+ continue
+ }
+ // add "foo/":
+ // add "foo" child to "" (creating "" if necessary)
+ //
+ // add "foo/bar/":
+ // add "bar" child to "foo" (creating "foo" if necessary)
+ //
+ // add "foo/bar.txt":
+ // add "bar.txt" child to "foo" (creating "foo" if necessary)
+ //
+ // add "a/b/c/d/e/f.txt":
+ // create "a/b/c/d/e" node
+ // add "f.txt" child to "e"
+
+ name := ent.Name
+ pdirName := parentDir(name)
+ if name == pdirName {
+ // This entry and its parent are the same.
+ // Ignore this for avoiding infinite loop of the reference.
+ // The example case where this can occur is when tar contains the root
+ // directory itself (e.g. "./", "/").
+ continue
+ }
+ pdir := r.getOrCreateDir(pdirName)
+ ent.NumLink++ // at least one name(ent.Name) references this entry.
+ if ent.Type == "hardlink" {
+ org, err := r.getSource(ent)
+ if err != nil {
+ return err
+ }
+ org.NumLink++ // original entry is referenced by this ent.Name.
+ ent = org
+ }
+ pdir.addChild(path.Base(name), ent)
+ }
+
+ lastOffset := r.sr.Size()
+ for i := len(r.toc.Entries) - 1; i >= 0; i-- {
+ e := r.toc.Entries[i]
+ if e.isDataType() {
+ e.nextOffset = lastOffset
+ }
+ if e.Offset != 0 && e.InnerOffset == 0 {
+ lastOffset = e.Offset
+ }
+ }
+
+ return nil
+}
+
+func (r *Reader) getSource(ent *TOCEntry) (_ *TOCEntry, err error) {
+ if ent.Type == "hardlink" {
+ org, ok := r.m[cleanEntryName(ent.LinkName)]
+ if !ok {
+ return nil, fmt.Errorf("%q is a hardlink but the linkname %q isn't found", ent.Name, ent.LinkName)
+ }
+ ent, err = r.getSource(org)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return ent, nil
+}
+
+func parentDir(p string) string {
+ dir, _ := path.Split(p)
+ return strings.TrimSuffix(dir, "/")
+}
+
+func (r *Reader) getOrCreateDir(d string) *TOCEntry {
+ e, ok := r.m[d]
+ if !ok {
+ e = &TOCEntry{
+ Name: d,
+ Type: "dir",
+ Mode: 0755,
+ NumLink: 2, // The directory itself(.) and the parent link to this directory.
+ }
+ r.m[d] = e
+ if d != "" {
+ pdir := r.getOrCreateDir(parentDir(d))
+ pdir.addChild(path.Base(d), e)
+ }
+ }
+ return e
+}
+
+func (r *Reader) TOCDigest() digest.Digest {
+ return r.tocDigest
+}
+
+// VerifyTOC checks that the TOC JSON in the passed blob matches the
+// passed digests and that the TOC JSON contains digests for all chunks
+// contained in the blob. If the verification succceeds, this function
+// returns TOCEntryVerifier which holds all chunk digests in the stargz blob.
+func (r *Reader) VerifyTOC(tocDigest digest.Digest) (TOCEntryVerifier, error) {
+ // Verify the digest of TOC JSON
+ if r.tocDigest != tocDigest {
+ return nil, fmt.Errorf("invalid TOC JSON %q; want %q", r.tocDigest, tocDigest)
+ }
+ return r.Verifiers()
+}
+
+// Verifiers returns TOCEntryVerifier of this chunk. Use VerifyTOC instead in most cases
+// because this doesn't verify TOC.
+func (r *Reader) Verifiers() (TOCEntryVerifier, error) {
+ chunkDigestMap := make(map[int64]digest.Digest) // map from chunk offset to the chunk digest
+ regDigestMap := make(map[int64]digest.Digest) // map from chunk offset to the reg file digest
+ var chunkDigestMapIncomplete bool
+ var regDigestMapIncomplete bool
+ var containsChunk bool
+ for _, e := range r.toc.Entries {
+ if e.Type != "reg" && e.Type != "chunk" {
+ continue
+ }
+
+ // offset must be unique in stargz blob
+ _, dOK := chunkDigestMap[e.Offset]
+ _, rOK := regDigestMap[e.Offset]
+ if dOK || rOK {
+ return nil, fmt.Errorf("offset %d found twice", e.Offset)
+ }
+
+ if e.Type == "reg" {
+ if e.Size == 0 {
+ continue // ignores empty file
+ }
+
+ // record the digest of regular file payload
+ if e.Digest != "" {
+ d, err := digest.Parse(e.Digest)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse regular file digest %q: %w", e.Digest, err)
+ }
+ regDigestMap[e.Offset] = d
+ } else {
+ regDigestMapIncomplete = true
+ }
+ } else {
+ containsChunk = true // this layer contains "chunk" entries.
+ }
+
+ // "reg" also can contain ChunkDigest (e.g. when "reg" is the first entry of
+ // chunked file)
+ if e.ChunkDigest != "" {
+ d, err := digest.Parse(e.ChunkDigest)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse chunk digest %q: %w", e.ChunkDigest, err)
+ }
+ chunkDigestMap[e.Offset] = d
+ } else {
+ chunkDigestMapIncomplete = true
+ }
+ }
+
+ if chunkDigestMapIncomplete {
+ // Though some chunk digests are not found, if this layer doesn't contain
+ // "chunk"s and all digest of "reg" files are recorded, we can use them instead.
+ if !containsChunk && !regDigestMapIncomplete {
+ return &verifier{digestMap: regDigestMap}, nil
+ }
+ return nil, fmt.Errorf("some ChunkDigest not found in TOC JSON")
+ }
+
+ return &verifier{digestMap: chunkDigestMap}, nil
+}
+
+// verifier is an implementation of TOCEntryVerifier which holds verifiers keyed by
+// offset of the chunk.
+type verifier struct {
+ digestMap map[int64]digest.Digest
+ digestMapMu sync.Mutex
+}
+
+// Verifier returns a content verifier specified by TOCEntry.
+func (v *verifier) Verifier(ce *TOCEntry) (digest.Verifier, error) {
+ v.digestMapMu.Lock()
+ defer v.digestMapMu.Unlock()
+ d, ok := v.digestMap[ce.Offset]
+ if !ok {
+ return nil, fmt.Errorf("verifier for offset=%d,size=%d hasn't been registered",
+ ce.Offset, ce.ChunkSize)
+ }
+ return d.Verifier(), nil
+}
+
+// ChunkEntryForOffset returns the TOCEntry containing the byte of the
+// named file at the given offset within the file.
+// Name must be absolute path or one that is relative to root.
+func (r *Reader) ChunkEntryForOffset(name string, offset int64) (e *TOCEntry, ok bool) {
+ name = cleanEntryName(name)
+ e, ok = r.Lookup(name)
+ if !ok || !e.isDataType() {
+ return nil, false
+ }
+ ents := r.chunks[name]
+ if len(ents) < 2 {
+ if offset >= e.ChunkSize {
+ return nil, false
+ }
+ return e, true
+ }
+ i := sort.Search(len(ents), func(i int) bool {
+ e := ents[i]
+ return e.ChunkOffset >= offset || (offset > e.ChunkOffset && offset < e.ChunkOffset+e.ChunkSize)
+ })
+ if i == len(ents) {
+ return nil, false
+ }
+ return ents[i], true
+}
+
+// Lookup returns the Table of Contents entry for the given path.
+//
+// To get the root directory, use the empty string.
+// Path must be absolute path or one that is relative to root.
+func (r *Reader) Lookup(path string) (e *TOCEntry, ok bool) {
+ path = cleanEntryName(path)
+ if r == nil {
+ return
+ }
+ e, ok = r.m[path]
+ if ok && e.Type == "hardlink" {
+ var err error
+ e, err = r.getSource(e)
+ if err != nil {
+ return nil, false
+ }
+ }
+ return
+}
+
+// OpenFile returns the reader of the specified file payload.
+//
+// Name must be absolute path or one that is relative to root.
+func (r *Reader) OpenFile(name string) (*io.SectionReader, error) {
+ fr, err := r.newFileReader(name)
+ if err != nil {
+ return nil, err
+ }
+ return io.NewSectionReader(fr, 0, fr.size), nil
+}
+
+func (r *Reader) newFileReader(name string) (*fileReader, error) {
+ name = cleanEntryName(name)
+ ent, ok := r.Lookup(name)
+ if !ok {
+ // TODO: come up with some error plan. This is lazy:
+ return nil, &os.PathError{
+ Path: name,
+ Op: "OpenFile",
+ Err: os.ErrNotExist,
+ }
+ }
+ if ent.Type != "reg" {
+ return nil, &os.PathError{
+ Path: name,
+ Op: "OpenFile",
+ Err: errors.New("not a regular file"),
+ }
+ }
+ return &fileReader{
+ r: r,
+ size: ent.Size,
+ ents: r.getChunks(ent),
+ }, nil
+}
+
+func (r *Reader) OpenFileWithPreReader(name string, preRead func(*TOCEntry, io.Reader) error) (*io.SectionReader, error) {
+ fr, err := r.newFileReader(name)
+ if err != nil {
+ return nil, err
+ }
+ fr.preRead = preRead
+ return io.NewSectionReader(fr, 0, fr.size), nil
+}
+
+func (r *Reader) getChunks(ent *TOCEntry) []*TOCEntry {
+ if ents, ok := r.chunks[ent.Name]; ok {
+ return ents
+ }
+ return []*TOCEntry{ent}
+}
+
+type fileReader struct {
+ r *Reader
+ size int64
+ ents []*TOCEntry // 1 or more reg/chunk entries
+ preRead func(*TOCEntry, io.Reader) error
+}
+
+func (fr *fileReader) ReadAt(p []byte, off int64) (n int, err error) {
+ if off >= fr.size {
+ return 0, io.EOF
+ }
+ if off < 0 {
+ return 0, errors.New("invalid offset")
+ }
+ var i int
+ if len(fr.ents) > 1 {
+ i = sort.Search(len(fr.ents), func(i int) bool {
+ return fr.ents[i].ChunkOffset >= off
+ })
+ if i == len(fr.ents) {
+ i = len(fr.ents) - 1
+ }
+ }
+ ent := fr.ents[i]
+ if ent.ChunkOffset > off {
+ if i == 0 {
+ return 0, errors.New("internal error; first chunk offset is non-zero")
+ }
+ ent = fr.ents[i-1]
+ }
+
+ // If ent is a chunk of a large file, adjust the ReadAt
+ // offset by the chunk's offset.
+ off -= ent.ChunkOffset
+
+ finalEnt := fr.ents[len(fr.ents)-1]
+ compressedOff := ent.Offset
+ // compressedBytesRemain is the number of compressed bytes in this
+ // file remaining, over 1+ chunks.
+ compressedBytesRemain := finalEnt.NextOffset() - compressedOff
+
+ sr := io.NewSectionReader(fr.r.sr, compressedOff, compressedBytesRemain)
+
+ const maxRead = 2 << 20
+ var bufSize = maxRead
+ if compressedBytesRemain < maxRead {
+ bufSize = int(compressedBytesRemain)
+ }
+
+ br := bufio.NewReaderSize(sr, bufSize)
+ if _, err := br.Peek(bufSize); err != nil {
+ return 0, fmt.Errorf("fileReader.ReadAt.peek: %v", err)
+ }
+
+ dr, err := fr.r.decompressor.Reader(br)
+ if err != nil {
+ return 0, fmt.Errorf("fileReader.ReadAt.decompressor.Reader: %v", err)
+ }
+ defer dr.Close()
+
+ if fr.preRead == nil {
+ if n, err := io.CopyN(io.Discard, dr, ent.InnerOffset+off); n != ent.InnerOffset+off || err != nil {
+ return 0, fmt.Errorf("discard of %d bytes != %v, %v", ent.InnerOffset+off, n, err)
+ }
+ return io.ReadFull(dr, p)
+ }
+
+ var retN int
+ var retErr error
+ var found bool
+ var nr int64
+ for _, e := range fr.r.toc.Entries[ent.chunkTopIndex:] {
+ if !e.isDataType() {
+ continue
+ }
+ if e.Offset != fr.r.toc.Entries[ent.chunkTopIndex].Offset {
+ break
+ }
+ if in, err := io.CopyN(io.Discard, dr, e.InnerOffset-nr); err != nil || in != e.InnerOffset-nr {
+ return 0, fmt.Errorf("discard of remaining %d bytes != %v, %v", e.InnerOffset-nr, in, err)
+ }
+ nr = e.InnerOffset
+ if e == ent {
+ found = true
+ if n, err := io.CopyN(io.Discard, dr, off); n != off || err != nil {
+ return 0, fmt.Errorf("discard of offset %d bytes != %v, %v", off, n, err)
+ }
+ retN, retErr = io.ReadFull(dr, p)
+ nr += off + int64(retN)
+ continue
+ }
+ cr := &countReader{r: io.LimitReader(dr, e.ChunkSize)}
+ if err := fr.preRead(e, cr); err != nil {
+ return 0, fmt.Errorf("failed to pre read: %w", err)
+ }
+ nr += cr.n
+ }
+ if !found {
+ return 0, fmt.Errorf("fileReader.ReadAt: target entry not found")
+ }
+ return retN, retErr
+}
+
+// A Writer writes stargz files.
+//
+// Use NewWriter to create a new Writer.
+type Writer struct {
+ bw *bufio.Writer
+ cw *countWriter
+ toc *JTOC
+ diffHash hash.Hash // SHA-256 of uncompressed tar
+
+ closed bool
+ gz io.WriteCloser
+ lastUsername map[int]string
+ lastGroupname map[int]string
+ compressor Compressor
+
+ uncompressedCounter *countWriteFlusher
+
+ // ChunkSize optionally controls the maximum number of bytes
+ // of data of a regular file that can be written in one gzip
+ // stream before a new gzip stream is started.
+ // Zero means to use a default, currently 4 MiB.
+ ChunkSize int
+
+ // MinChunkSize optionally controls the minimum number of bytes
+ // of data must be written in one gzip stream before a new gzip
+ // NOTE: This adds a TOC property that stargz snapshotter < v0.13.0 doesn't understand.
+ MinChunkSize int
+
+ needsOpenGzEntries map[string]struct{}
+}
+
+// currentCompressionWriter writes to the current w.gz field, which can
+// change throughout writing a tar entry.
+//
+// Additionally, it updates w's SHA-256 of the uncompressed bytes
+// of the tar file.
+type currentCompressionWriter struct{ w *Writer }
+
+func (ccw currentCompressionWriter) Write(p []byte) (int, error) {
+ ccw.w.diffHash.Write(p)
+ if ccw.w.gz == nil {
+ if err := ccw.w.condOpenGz(); err != nil {
+ return 0, err
+ }
+ }
+ return ccw.w.gz.Write(p)
+}
+
+func (w *Writer) chunkSize() int {
+ if w.ChunkSize <= 0 {
+ return 4 << 20
+ }
+ return w.ChunkSize
+}
+
+// Unpack decompresses the given estargz blob and returns a ReadCloser of the tar blob.
+// TOC JSON and footer are removed.
+func Unpack(sr *io.SectionReader, c Decompressor) (io.ReadCloser, error) {
+ footerSize := c.FooterSize()
+ if sr.Size() < footerSize {
+ return nil, fmt.Errorf("blob is too small; %d < %d", sr.Size(), footerSize)
+ }
+ footerOffset := sr.Size() - footerSize
+ footer := make([]byte, footerSize)
+ if _, err := sr.ReadAt(footer, footerOffset); err != nil {
+ return nil, err
+ }
+ blobPayloadSize, _, _, err := c.ParseFooter(footer)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse footer: %w", err)
+ }
+ if blobPayloadSize < 0 {
+ blobPayloadSize = sr.Size()
+ }
+ return c.Reader(io.LimitReader(sr, blobPayloadSize))
+}
+
+// NewWriter returns a new stargz writer (gzip-based) writing to w.
+//
+// The writer must be closed to write its trailing table of contents.
+func NewWriter(w io.Writer) *Writer {
+ return NewWriterLevel(w, gzip.BestCompression)
+}
+
+// NewWriterLevel returns a new stargz writer (gzip-based) writing to w.
+// The compression level is configurable.
+//
+// The writer must be closed to write its trailing table of contents.
+func NewWriterLevel(w io.Writer, compressionLevel int) *Writer {
+ return NewWriterWithCompressor(w, NewGzipCompressorWithLevel(compressionLevel))
+}
+
+// NewWriterWithCompressor returns a new stargz writer writing to w.
+// The compression method is configurable.
+//
+// The writer must be closed to write its trailing table of contents.
+func NewWriterWithCompressor(w io.Writer, c Compressor) *Writer {
+ bw := bufio.NewWriter(w)
+ cw := &countWriter{w: bw}
+ return &Writer{
+ bw: bw,
+ cw: cw,
+ toc: &JTOC{Version: 1},
+ diffHash: sha256.New(),
+ compressor: c,
+ uncompressedCounter: &countWriteFlusher{},
+ }
+}
+
+// Close writes the stargz's table of contents and flushes all the
+// buffers, returning any error.
+func (w *Writer) Close() (digest.Digest, error) {
+ if w.closed {
+ return "", nil
+ }
+ defer func() { w.closed = true }()
+
+ if err := w.closeGz(); err != nil {
+ return "", err
+ }
+
+ // Write the TOC index and footer.
+ tocDigest, err := w.compressor.WriteTOCAndFooter(w.cw, w.cw.n, w.toc, w.diffHash)
+ if err != nil {
+ return "", err
+ }
+ if err := w.bw.Flush(); err != nil {
+ return "", err
+ }
+
+ return tocDigest, nil
+}
+
+func (w *Writer) closeGz() error {
+ if w.closed {
+ return errors.New("write on closed Writer")
+ }
+ if w.gz != nil {
+ if err := w.gz.Close(); err != nil {
+ return err
+ }
+ w.gz = nil
+ }
+ return nil
+}
+
+func (w *Writer) flushGz() error {
+ if w.closed {
+ return errors.New("flush on closed Writer")
+ }
+ if w.gz != nil {
+ if f, ok := w.gz.(interface {
+ Flush() error
+ }); ok {
+ return f.Flush()
+ }
+ }
+ return nil
+}
+
+// nameIfChanged returns name, unless it was the already the value of (*mp)[id],
+// in which case it returns the empty string.
+func (w *Writer) nameIfChanged(mp *map[int]string, id int, name string) string {
+ if name == "" {
+ return ""
+ }
+ if *mp == nil {
+ *mp = make(map[int]string)
+ }
+ if (*mp)[id] == name {
+ return ""
+ }
+ (*mp)[id] = name
+ return name
+}
+
+func (w *Writer) condOpenGz() (err error) {
+ if w.gz == nil {
+ w.gz, err = w.compressor.Writer(w.cw)
+ if w.gz != nil {
+ w.gz = w.uncompressedCounter.register(w.gz)
+ }
+ }
+ return
+}
+
+// AppendTar reads the tar or tar.gz file from r and appends
+// each of its contents to w.
+//
+// The input r can optionally be gzip compressed but the output will
+// always be compressed by the specified compressor.
+func (w *Writer) AppendTar(r io.Reader) error {
+ return w.appendTar(r, false)
+}
+
+// AppendTarLossLess reads the tar or tar.gz file from r and appends
+// each of its contents to w.
+//
+// The input r can optionally be gzip compressed but the output will
+// always be compressed by the specified compressor.
+//
+// The difference of this func with AppendTar is that this writes
+// the input tar stream into w without any modification (e.g. to header bytes).
+//
+// Note that if the input tar stream already contains TOC JSON, this returns
+// error because w cannot overwrite the TOC JSON to the one generated by w without
+// lossy modification. To avoid this error, if the input stream is known to be stargz/estargz,
+// you shoud decompress it and remove TOC JSON in advance.
+func (w *Writer) AppendTarLossLess(r io.Reader) error {
+ return w.appendTar(r, true)
+}
+
+func (w *Writer) appendTar(r io.Reader, lossless bool) error {
+ var src io.Reader
+ br := bufio.NewReader(r)
+ if isGzip(br) {
+ zr, _ := gzip.NewReader(br)
+ src = zr
+ } else {
+ src = io.Reader(br)
+ }
+ dst := currentCompressionWriter{w}
+ var tw *tar.Writer
+ if !lossless {
+ tw = tar.NewWriter(dst) // use tar writer only when this isn't lossless mode.
+ }
+ tr := tar.NewReader(src)
+ if lossless {
+ tr.RawAccounting = true
+ }
+ prevOffset := w.cw.n
+ var prevOffsetUncompressed int64
+ for {
+ h, err := tr.Next()
+ if err == io.EOF {
+ if lossless {
+ if remain := tr.RawBytes(); len(remain) > 0 {
+ // Collect the remaining null bytes.
+ // https://github.com/vbatts/tar-split/blob/80a436fd6164c557b131f7c59ed69bd81af69761/concept/main.go#L49-L53
+ if _, err := dst.Write(remain); err != nil {
+ return err
+ }
+ }
+ }
+ break
+ }
+ if err != nil {
+ return fmt.Errorf("error reading from source tar: tar.Reader.Next: %v", err)
+ }
+ if cleanEntryName(h.Name) == TOCTarName {
+ // It is possible for a layer to be "stargzified" twice during the
+ // distribution lifecycle. So we reserve "TOCTarName" here to avoid
+ // duplicated entries in the resulting layer.
+ if lossless {
+ // We cannot handle this in lossless way.
+ return fmt.Errorf("existing TOC JSON is not allowed; decompress layer before append")
+ }
+ continue
+ }
+
+ xattrs := make(map[string][]byte)
+ const xattrPAXRecordsPrefix = "SCHILY.xattr."
+ if h.PAXRecords != nil {
+ for k, v := range h.PAXRecords {
+ if strings.HasPrefix(k, xattrPAXRecordsPrefix) {
+ xattrs[k[len(xattrPAXRecordsPrefix):]] = []byte(v)
+ }
+ }
+ }
+ ent := &TOCEntry{
+ Name: h.Name,
+ Mode: h.Mode,
+ UID: h.Uid,
+ GID: h.Gid,
+ Uname: w.nameIfChanged(&w.lastUsername, h.Uid, h.Uname),
+ Gname: w.nameIfChanged(&w.lastGroupname, h.Gid, h.Gname),
+ ModTime3339: formatModtime(h.ModTime),
+ Xattrs: xattrs,
+ }
+ if err := w.condOpenGz(); err != nil {
+ return err
+ }
+ if tw != nil {
+ if err := tw.WriteHeader(h); err != nil {
+ return err
+ }
+ } else {
+ if _, err := dst.Write(tr.RawBytes()); err != nil {
+ return err
+ }
+ }
+ switch h.Typeflag {
+ case tar.TypeLink:
+ ent.Type = "hardlink"
+ ent.LinkName = h.Linkname
+ case tar.TypeSymlink:
+ ent.Type = "symlink"
+ ent.LinkName = h.Linkname
+ case tar.TypeDir:
+ ent.Type = "dir"
+ case tar.TypeReg:
+ ent.Type = "reg"
+ ent.Size = h.Size
+ case tar.TypeChar:
+ ent.Type = "char"
+ ent.DevMajor = int(h.Devmajor)
+ ent.DevMinor = int(h.Devminor)
+ case tar.TypeBlock:
+ ent.Type = "block"
+ ent.DevMajor = int(h.Devmajor)
+ ent.DevMinor = int(h.Devminor)
+ case tar.TypeFifo:
+ ent.Type = "fifo"
+ default:
+ return fmt.Errorf("unsupported input tar entry %q", h.Typeflag)
+ }
+
+ // We need to keep a reference to the TOC entry for regular files, so that we
+ // can fill the digest later.
+ var regFileEntry *TOCEntry
+ var payloadDigest digest.Digester
+ if h.Typeflag == tar.TypeReg {
+ regFileEntry = ent
+ payloadDigest = digest.Canonical.Digester()
+ }
+
+ if h.Typeflag == tar.TypeReg && ent.Size > 0 {
+ var written int64
+ totalSize := ent.Size // save it before we destroy ent
+ tee := io.TeeReader(tr, payloadDigest.Hash())
+ for written < totalSize {
+ chunkSize := int64(w.chunkSize())
+ remain := totalSize - written
+ if remain < chunkSize {
+ chunkSize = remain
+ } else {
+ ent.ChunkSize = chunkSize
+ }
+
+ // We flush the underlying compression writer here to correctly calculate "w.cw.n".
+ if err := w.flushGz(); err != nil {
+ return err
+ }
+ if w.needsOpenGz(ent) || w.cw.n-prevOffset >= int64(w.MinChunkSize) {
+ if err := w.closeGz(); err != nil {
+ return err
+ }
+ ent.Offset = w.cw.n
+ prevOffset = ent.Offset
+ prevOffsetUncompressed = w.uncompressedCounter.n
+ } else {
+ ent.Offset = prevOffset
+ ent.InnerOffset = w.uncompressedCounter.n - prevOffsetUncompressed
+ }
+
+ ent.ChunkOffset = written
+ chunkDigest := digest.Canonical.Digester()
+
+ if err := w.condOpenGz(); err != nil {
+ return err
+ }
+
+ teeChunk := io.TeeReader(tee, chunkDigest.Hash())
+ var out io.Writer
+ if tw != nil {
+ out = tw
+ } else {
+ out = dst
+ }
+ if _, err := io.CopyN(out, teeChunk, chunkSize); err != nil {
+ return fmt.Errorf("error copying %q: %v", h.Name, err)
+ }
+ ent.ChunkDigest = chunkDigest.Digest().String()
+ w.toc.Entries = append(w.toc.Entries, ent)
+ written += chunkSize
+ ent = &TOCEntry{
+ Name: h.Name,
+ Type: "chunk",
+ }
+ }
+ } else {
+ w.toc.Entries = append(w.toc.Entries, ent)
+ }
+ if payloadDigest != nil {
+ regFileEntry.Digest = payloadDigest.Digest().String()
+ }
+ if tw != nil {
+ if err := tw.Flush(); err != nil {
+ return err
+ }
+ }
+ }
+ remainDest := io.Discard
+ if lossless {
+ remainDest = dst // Preserve the remaining bytes in lossless mode
+ }
+ _, err := io.Copy(remainDest, src)
+ return err
+}
+
+func (w *Writer) needsOpenGz(ent *TOCEntry) bool {
+ if ent.Type != "reg" {
+ return false
+ }
+ if w.needsOpenGzEntries == nil {
+ return false
+ }
+ _, ok := w.needsOpenGzEntries[ent.Name]
+ return ok
+}
+
+// DiffID returns the SHA-256 of the uncompressed tar bytes.
+// It is only valid to call DiffID after Close.
+func (w *Writer) DiffID() string {
+ return fmt.Sprintf("sha256:%x", w.diffHash.Sum(nil))
+}
+
+func maxFooterSize(blobSize int64, decompressors ...Decompressor) (res int64) {
+ for _, d := range decompressors {
+ if s := d.FooterSize(); res < s && s <= blobSize {
+ res = s
+ }
+ }
+ return
+}
+
+func parseTOC(d Decompressor, sr *io.SectionReader, tocOff, tocSize int64, tocBytes []byte, opts openOpts) (*Reader, error) {
+ if tocOff < 0 {
+ // This means that TOC isn't contained in the blob.
+ // We pass nil reader to ParseTOC and expect that ParseTOC acquire TOC from
+ // the external location.
+ start := time.Now()
+ toc, tocDgst, err := d.ParseTOC(nil)
+ if err != nil {
+ return nil, err
+ }
+ if opts.telemetry != nil && opts.telemetry.GetTocLatency != nil {
+ opts.telemetry.GetTocLatency(start)
+ }
+ if opts.telemetry != nil && opts.telemetry.DeserializeTocLatency != nil {
+ opts.telemetry.DeserializeTocLatency(start)
+ }
+ return &Reader{
+ sr: sr,
+ toc: toc,
+ tocDigest: tocDgst,
+ decompressor: d,
+ }, nil
+ }
+ if len(tocBytes) > 0 {
+ start := time.Now()
+ toc, tocDgst, err := d.ParseTOC(bytes.NewReader(tocBytes))
+ if err == nil {
+ if opts.telemetry != nil && opts.telemetry.DeserializeTocLatency != nil {
+ opts.telemetry.DeserializeTocLatency(start)
+ }
+ return &Reader{
+ sr: sr,
+ toc: toc,
+ tocDigest: tocDgst,
+ decompressor: d,
+ }, nil
+ }
+ }
+
+ start := time.Now()
+ tocBytes = make([]byte, tocSize)
+ if _, err := sr.ReadAt(tocBytes, tocOff); err != nil {
+ return nil, fmt.Errorf("error reading %d byte TOC targz: %v", len(tocBytes), err)
+ }
+ if opts.telemetry != nil && opts.telemetry.GetTocLatency != nil {
+ opts.telemetry.GetTocLatency(start)
+ }
+ start = time.Now()
+ toc, tocDgst, err := d.ParseTOC(bytes.NewReader(tocBytes))
+ if err != nil {
+ return nil, err
+ }
+ if opts.telemetry != nil && opts.telemetry.DeserializeTocLatency != nil {
+ opts.telemetry.DeserializeTocLatency(start)
+ }
+ return &Reader{
+ sr: sr,
+ toc: toc,
+ tocDigest: tocDgst,
+ decompressor: d,
+ }, nil
+}
+
+func formatModtime(t time.Time) string {
+ if t.IsZero() || t.Unix() == 0 {
+ return ""
+ }
+ return t.UTC().Round(time.Second).Format(time.RFC3339)
+}
+
+func cleanEntryName(name string) string {
+ // Use path.Clean to consistently deal with path separators across platforms.
+ return strings.TrimPrefix(path.Clean("/"+name), "/")
+}
+
+// countWriter counts how many bytes have been written to its wrapped
+// io.Writer.
+type countWriter struct {
+ w io.Writer
+ n int64
+}
+
+func (cw *countWriter) Write(p []byte) (n int, err error) {
+ n, err = cw.w.Write(p)
+ cw.n += int64(n)
+ return
+}
+
+type countWriteFlusher struct {
+ io.WriteCloser
+ n int64
+}
+
+func (wc *countWriteFlusher) register(w io.WriteCloser) io.WriteCloser {
+ wc.WriteCloser = w
+ return wc
+}
+
+func (wc *countWriteFlusher) Write(p []byte) (n int, err error) {
+ n, err = wc.WriteCloser.Write(p)
+ wc.n += int64(n)
+ return
+}
+
+func (wc *countWriteFlusher) Flush() error {
+ if f, ok := wc.WriteCloser.(interface {
+ Flush() error
+ }); ok {
+ return f.Flush()
+ }
+ return nil
+}
+
+func (wc *countWriteFlusher) Close() error {
+ err := wc.WriteCloser.Close()
+ wc.WriteCloser = nil
+ return err
+}
+
+// isGzip reports whether br is positioned right before an upcoming gzip stream.
+// It does not consume any bytes from br.
+func isGzip(br *bufio.Reader) bool {
+ const (
+ gzipID1 = 0x1f
+ gzipID2 = 0x8b
+ gzipDeflate = 8
+ )
+ peek, _ := br.Peek(3)
+ return len(peek) >= 3 && peek[0] == gzipID1 && peek[1] == gzipID2 && peek[2] == gzipDeflate
+}
+
+func positive(n int64) int64 {
+ if n < 0 {
+ return 0
+ }
+ return n
+}
+
+type countReader struct {
+ r io.Reader
+ n int64
+}
+
+func (cr *countReader) Read(p []byte) (n int, err error) {
+ n, err = cr.r.Read(p)
+ cr.n += int64(n)
+ return
+}
diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go
new file mode 100644
index 00000000..88fa13b1
--- /dev/null
+++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go
@@ -0,0 +1,237 @@
+/*
+ Copyright The containerd Authors.
+
+ 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.
+*/
+
+/*
+ Copyright 2019 The Go Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file.
+*/
+
+package estargz
+
+import (
+ "archive/tar"
+ "bytes"
+ "compress/gzip"
+ "encoding/binary"
+ "encoding/json"
+ "fmt"
+ "hash"
+ "io"
+ "strconv"
+
+ digest "github.com/opencontainers/go-digest"
+)
+
+type gzipCompression struct {
+ *GzipCompressor
+ *GzipDecompressor
+}
+
+func newGzipCompressionWithLevel(level int) Compression {
+ return &gzipCompression{
+ &GzipCompressor{level},
+ &GzipDecompressor{},
+ }
+}
+
+func NewGzipCompressor() *GzipCompressor {
+ return &GzipCompressor{gzip.BestCompression}
+}
+
+func NewGzipCompressorWithLevel(level int) *GzipCompressor {
+ return &GzipCompressor{level}
+}
+
+type GzipCompressor struct {
+ compressionLevel int
+}
+
+func (gc *GzipCompressor) Writer(w io.Writer) (WriteFlushCloser, error) {
+ return gzip.NewWriterLevel(w, gc.compressionLevel)
+}
+
+func (gc *GzipCompressor) WriteTOCAndFooter(w io.Writer, off int64, toc *JTOC, diffHash hash.Hash) (digest.Digest, error) {
+ tocJSON, err := json.MarshalIndent(toc, "", "\t")
+ if err != nil {
+ return "", err
+ }
+ gz, _ := gzip.NewWriterLevel(w, gc.compressionLevel)
+ gw := io.Writer(gz)
+ if diffHash != nil {
+ gw = io.MultiWriter(gz, diffHash)
+ }
+ tw := tar.NewWriter(gw)
+ if err := tw.WriteHeader(&tar.Header{
+ Typeflag: tar.TypeReg,
+ Name: TOCTarName,
+ Size: int64(len(tocJSON)),
+ }); err != nil {
+ return "", err
+ }
+ if _, err := tw.Write(tocJSON); err != nil {
+ return "", err
+ }
+
+ if err := tw.Close(); err != nil {
+ return "", err
+ }
+ if err := gz.Close(); err != nil {
+ return "", err
+ }
+ if _, err := w.Write(gzipFooterBytes(off)); err != nil {
+ return "", err
+ }
+ return digest.FromBytes(tocJSON), nil
+}
+
+// gzipFooterBytes returns the 51 bytes footer.
+func gzipFooterBytes(tocOff int64) []byte {
+ buf := bytes.NewBuffer(make([]byte, 0, FooterSize))
+ gz, _ := gzip.NewWriterLevel(buf, gzip.NoCompression) // MUST be NoCompression to keep 51 bytes
+
+ // Extra header indicating the offset of TOCJSON
+ // https://tools.ietf.org/html/rfc1952#section-2.3.1.1
+ header := make([]byte, 4)
+ header[0], header[1] = 'S', 'G'
+ subfield := fmt.Sprintf("%016xSTARGZ", tocOff)
+ binary.LittleEndian.PutUint16(header[2:4], uint16(len(subfield))) // little-endian per RFC1952
+ gz.Extra = append(header, []byte(subfield)...)
+ gz.Close()
+ if buf.Len() != FooterSize {
+ panic(fmt.Sprintf("footer buffer = %d, not %d", buf.Len(), FooterSize))
+ }
+ return buf.Bytes()
+}
+
+type GzipDecompressor struct{}
+
+func (gz *GzipDecompressor) Reader(r io.Reader) (io.ReadCloser, error) {
+ return gzip.NewReader(r)
+}
+
+func (gz *GzipDecompressor) ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) {
+ return parseTOCEStargz(r)
+}
+
+func (gz *GzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) {
+ if len(p) != FooterSize {
+ return 0, 0, 0, fmt.Errorf("invalid length %d cannot be parsed", len(p))
+ }
+ zr, err := gzip.NewReader(bytes.NewReader(p))
+ if err != nil {
+ return 0, 0, 0, err
+ }
+ defer zr.Close()
+ extra := zr.Extra
+ si1, si2, subfieldlen, subfield := extra[0], extra[1], extra[2:4], extra[4:]
+ if si1 != 'S' || si2 != 'G' {
+ return 0, 0, 0, fmt.Errorf("invalid subfield IDs: %q, %q; want E, S", si1, si2)
+ }
+ if slen := binary.LittleEndian.Uint16(subfieldlen); slen != uint16(16+len("STARGZ")) {
+ return 0, 0, 0, fmt.Errorf("invalid length of subfield %d; want %d", slen, 16+len("STARGZ"))
+ }
+ if string(subfield[16:]) != "STARGZ" {
+ return 0, 0, 0, fmt.Errorf("STARGZ magic string must be included in the footer subfield")
+ }
+ tocOffset, err = strconv.ParseInt(string(subfield[:16]), 16, 64)
+ if err != nil {
+ return 0, 0, 0, fmt.Errorf("legacy: failed to parse toc offset: %w", err)
+ }
+ return tocOffset, tocOffset, 0, nil
+}
+
+func (gz *GzipDecompressor) FooterSize() int64 {
+ return FooterSize
+}
+
+func (gz *GzipDecompressor) DecompressTOC(r io.Reader) (tocJSON io.ReadCloser, err error) {
+ return decompressTOCEStargz(r)
+}
+
+type LegacyGzipDecompressor struct{}
+
+func (gz *LegacyGzipDecompressor) Reader(r io.Reader) (io.ReadCloser, error) {
+ return gzip.NewReader(r)
+}
+
+func (gz *LegacyGzipDecompressor) ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) {
+ return parseTOCEStargz(r)
+}
+
+func (gz *LegacyGzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) {
+ if len(p) != legacyFooterSize {
+ return 0, 0, 0, fmt.Errorf("legacy: invalid length %d cannot be parsed", len(p))
+ }
+ zr, err := gzip.NewReader(bytes.NewReader(p))
+ if err != nil {
+ return 0, 0, 0, fmt.Errorf("legacy: failed to get footer gzip reader: %w", err)
+ }
+ defer zr.Close()
+ extra := zr.Extra
+ if len(extra) != 16+len("STARGZ") {
+ return 0, 0, 0, fmt.Errorf("legacy: invalid stargz's extra field size")
+ }
+ if string(extra[16:]) != "STARGZ" {
+ return 0, 0, 0, fmt.Errorf("legacy: magic string STARGZ not found")
+ }
+ tocOffset, err = strconv.ParseInt(string(extra[:16]), 16, 64)
+ if err != nil {
+ return 0, 0, 0, fmt.Errorf("legacy: failed to parse toc offset: %w", err)
+ }
+ return tocOffset, tocOffset, 0, nil
+}
+
+func (gz *LegacyGzipDecompressor) FooterSize() int64 {
+ return legacyFooterSize
+}
+
+func (gz *LegacyGzipDecompressor) DecompressTOC(r io.Reader) (tocJSON io.ReadCloser, err error) {
+ return decompressTOCEStargz(r)
+}
+
+func parseTOCEStargz(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) {
+ tr, err := decompressTOCEStargz(r)
+ if err != nil {
+ return nil, "", err
+ }
+ dgstr := digest.Canonical.Digester()
+ toc = new(JTOC)
+ if err := json.NewDecoder(io.TeeReader(tr, dgstr.Hash())).Decode(&toc); err != nil {
+ return nil, "", fmt.Errorf("error decoding TOC JSON: %v", err)
+ }
+ if err := tr.Close(); err != nil {
+ return nil, "", err
+ }
+ return toc, dgstr.Digest(), nil
+}
+
+func decompressTOCEStargz(r io.Reader) (tocJSON io.ReadCloser, err error) {
+ zr, err := gzip.NewReader(r)
+ if err != nil {
+ return nil, fmt.Errorf("malformed TOC gzip header: %v", err)
+ }
+ zr.Multistream(false)
+ tr := tar.NewReader(zr)
+ h, err := tr.Next()
+ if err != nil {
+ return nil, fmt.Errorf("failed to find tar header in TOC gzip stream: %v", err)
+ }
+ if h.Name != TOCTarName {
+ return nil, fmt.Errorf("TOC tar entry had name %q; expected %q", h.Name, TOCTarName)
+ }
+ return readCloser{tr, zr.Close}, nil
+}
diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go
new file mode 100644
index 00000000..a8dcdb86
--- /dev/null
+++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go
@@ -0,0 +1,2370 @@
+/*
+ Copyright The containerd Authors.
+
+ 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.
+*/
+
+/*
+ Copyright 2019 The Go Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file.
+*/
+
+package estargz
+
+import (
+ "archive/tar"
+ "bytes"
+ "compress/gzip"
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+ "os"
+ "path/filepath"
+ "reflect"
+ "sort"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/containerd/stargz-snapshotter/estargz/errorutil"
+ "github.com/klauspost/compress/zstd"
+ digest "github.com/opencontainers/go-digest"
+)
+
+// TestingController is Compression with some helper methods necessary for testing.
+type TestingController interface {
+ Compression
+ TestStreams(t *testing.T, b []byte, streams []int64)
+ DiffIDOf(*testing.T, []byte) string
+ String() string
+}
+
+// CompressionTestSuite tests this pkg with controllers can build valid eStargz blobs and parse them.
+func CompressionTestSuite(t *testing.T, controllers ...TestingControllerFactory) {
+ t.Run("testBuild", func(t *testing.T) { t.Parallel(); testBuild(t, controllers...) })
+ t.Run("testDigestAndVerify", func(t *testing.T) { t.Parallel(); testDigestAndVerify(t, controllers...) })
+ t.Run("testWriteAndOpen", func(t *testing.T) { t.Parallel(); testWriteAndOpen(t, controllers...) })
+}
+
+type TestingControllerFactory func() TestingController
+
+const (
+ uncompressedType int = iota
+ gzipType
+ zstdType
+)
+
+var srcCompressions = []int{
+ uncompressedType,
+ gzipType,
+ zstdType,
+}
+
+var allowedPrefix = [4]string{"", "./", "/", "../"}
+
+// testBuild tests the resulting stargz blob built by this pkg has the same
+// contents as the normal stargz blob.
+func testBuild(t *testing.T, controllers ...TestingControllerFactory) {
+ tests := []struct {
+ name string
+ chunkSize int
+ minChunkSize []int
+ in []tarEntry
+ }{
+ {
+ name: "regfiles and directories",
+ chunkSize: 4,
+ in: tarOf(
+ file("foo", "test1"),
+ dir("foo2/"),
+ file("foo2/bar", "test2", xAttr(map[string]string{"test": "sample"})),
+ ),
+ },
+ {
+ name: "empty files",
+ chunkSize: 4,
+ in: tarOf(
+ file("foo", "tttttt"),
+ file("foo_empty", ""),
+ file("foo2", "tttttt"),
+ file("foo_empty2", ""),
+ file("foo3", "tttttt"),
+ file("foo_empty3", ""),
+ file("foo4", "tttttt"),
+ file("foo_empty4", ""),
+ file("foo5", "tttttt"),
+ file("foo_empty5", ""),
+ file("foo6", "tttttt"),
+ ),
+ },
+ {
+ name: "various files",
+ chunkSize: 4,
+ minChunkSize: []int{0, 64000},
+ in: tarOf(
+ file("baz.txt", "bazbazbazbazbazbazbaz"),
+ file("foo1.txt", "a"),
+ file("bar/foo2.txt", "b"),
+ file("foo3.txt", "c"),
+ symlink("barlink", "test/bar.txt"),
+ dir("test/"),
+ dir("dev/"),
+ blockdev("dev/testblock", 3, 4),
+ fifo("dev/testfifo"),
+ chardev("dev/testchar1", 5, 6),
+ file("test/bar.txt", "testbartestbar", xAttr(map[string]string{"test2": "sample2"})),
+ dir("test2/"),
+ link("test2/bazlink", "baz.txt"),
+ chardev("dev/testchar2", 1, 2),
+ ),
+ },
+ {
+ name: "no contents",
+ chunkSize: 4,
+ in: tarOf(
+ file("baz.txt", ""),
+ symlink("barlink", "test/bar.txt"),
+ dir("test/"),
+ dir("dev/"),
+ blockdev("dev/testblock", 3, 4),
+ fifo("dev/testfifo"),
+ chardev("dev/testchar1", 5, 6),
+ file("test/bar.txt", "", xAttr(map[string]string{"test2": "sample2"})),
+ dir("test2/"),
+ link("test2/bazlink", "baz.txt"),
+ chardev("dev/testchar2", 1, 2),
+ ),
+ },
+ }
+ for _, tt := range tests {
+ if len(tt.minChunkSize) == 0 {
+ tt.minChunkSize = []int{0}
+ }
+ for _, srcCompression := range srcCompressions {
+ srcCompression := srcCompression
+ for _, newCL := range controllers {
+ newCL := newCL
+ for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} {
+ srcTarFormat := srcTarFormat
+ for _, prefix := range allowedPrefix {
+ prefix := prefix
+ for _, minChunkSize := range tt.minChunkSize {
+ minChunkSize := minChunkSize
+ t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,src=%d,format=%s,minChunkSize=%d", newCL(), prefix, srcCompression, srcTarFormat, minChunkSize), func(t *testing.T) {
+ tarBlob := buildTar(t, tt.in, prefix, srcTarFormat)
+ // Test divideEntries()
+ entries, err := sortEntries(tarBlob, nil, nil) // identical order
+ if err != nil {
+ t.Fatalf("failed to parse tar: %v", err)
+ }
+ var merged []*entry
+ for _, part := range divideEntries(entries, 4) {
+ merged = append(merged, part...)
+ }
+ if !reflect.DeepEqual(entries, merged) {
+ for _, e := range entries {
+ t.Logf("Original: %v", e.header)
+ }
+ for _, e := range merged {
+ t.Logf("Merged: %v", e.header)
+ }
+ t.Errorf("divided entries couldn't be merged")
+ return
+ }
+
+ // Prepare sample data
+ cl1 := newCL()
+ wantBuf := new(bytes.Buffer)
+ sw := NewWriterWithCompressor(wantBuf, cl1)
+ sw.MinChunkSize = minChunkSize
+ sw.ChunkSize = tt.chunkSize
+ if err := sw.AppendTar(tarBlob); err != nil {
+ t.Fatalf("failed to append tar to want stargz: %v", err)
+ }
+ if _, err := sw.Close(); err != nil {
+ t.Fatalf("failed to prepare want stargz: %v", err)
+ }
+ wantData := wantBuf.Bytes()
+ want, err := Open(io.NewSectionReader(
+ bytes.NewReader(wantData), 0, int64(len(wantData))),
+ WithDecompressors(cl1),
+ )
+ if err != nil {
+ t.Fatalf("failed to parse the want stargz: %v", err)
+ }
+
+ // Prepare testing data
+ var opts []Option
+ if minChunkSize > 0 {
+ opts = append(opts, WithMinChunkSize(minChunkSize))
+ }
+ cl2 := newCL()
+ rc, err := Build(compressBlob(t, tarBlob, srcCompression),
+ append(opts, WithChunkSize(tt.chunkSize), WithCompression(cl2))...)
+ if err != nil {
+ t.Fatalf("failed to build stargz: %v", err)
+ }
+ defer rc.Close()
+ gotBuf := new(bytes.Buffer)
+ if _, err := io.Copy(gotBuf, rc); err != nil {
+ t.Fatalf("failed to copy built stargz blob: %v", err)
+ }
+ gotData := gotBuf.Bytes()
+ got, err := Open(io.NewSectionReader(
+ bytes.NewReader(gotBuf.Bytes()), 0, int64(len(gotData))),
+ WithDecompressors(cl2),
+ )
+ if err != nil {
+ t.Fatalf("failed to parse the got stargz: %v", err)
+ }
+
+ // Check DiffID is properly calculated
+ rc.Close()
+ diffID := rc.DiffID()
+ wantDiffID := cl2.DiffIDOf(t, gotData)
+ if diffID.String() != wantDiffID {
+ t.Errorf("DiffID = %q; want %q", diffID, wantDiffID)
+ }
+
+ // Compare as stargz
+ if !isSameVersion(t, cl1, wantData, cl2, gotData) {
+ t.Errorf("built stargz hasn't same json")
+ return
+ }
+ if !isSameEntries(t, want, got) {
+ t.Errorf("built stargz isn't same as the original")
+ return
+ }
+
+ // Compare as tar.gz
+ if !isSameTarGz(t, cl1, wantData, cl2, gotData) {
+ t.Errorf("built stargz isn't same tar.gz")
+ return
+ }
+ })
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+func isSameTarGz(t *testing.T, cla TestingController, a []byte, clb TestingController, b []byte) bool {
+ aGz, err := cla.Reader(bytes.NewReader(a))
+ if err != nil {
+ t.Fatalf("failed to read A")
+ }
+ defer aGz.Close()
+ bGz, err := clb.Reader(bytes.NewReader(b))
+ if err != nil {
+ t.Fatalf("failed to read B")
+ }
+ defer bGz.Close()
+
+ // Same as tar's Next() method but ignores landmarks and TOCJSON file
+ next := func(r *tar.Reader) (h *tar.Header, err error) {
+ for {
+ if h, err = r.Next(); err != nil {
+ return
+ }
+ if h.Name != PrefetchLandmark &&
+ h.Name != NoPrefetchLandmark &&
+ h.Name != TOCTarName {
+ return
+ }
+ }
+ }
+
+ aTar := tar.NewReader(aGz)
+ bTar := tar.NewReader(bGz)
+ for {
+ // Fetch and parse next header.
+ aH, aErr := next(aTar)
+ bH, bErr := next(bTar)
+ if aErr != nil || bErr != nil {
+ if aErr == io.EOF && bErr == io.EOF {
+ break
+ }
+ t.Fatalf("Failed to parse tar file: A: %v, B: %v", aErr, bErr)
+ }
+ if !reflect.DeepEqual(aH, bH) {
+ t.Logf("different header (A = %v; B = %v)", aH, bH)
+ return false
+
+ }
+ aFile, err := io.ReadAll(aTar)
+ if err != nil {
+ t.Fatal("failed to read tar payload of A")
+ }
+ bFile, err := io.ReadAll(bTar)
+ if err != nil {
+ t.Fatal("failed to read tar payload of B")
+ }
+ if !bytes.Equal(aFile, bFile) {
+ t.Logf("different tar payload (A = %q; B = %q)", string(a), string(b))
+ return false
+ }
+ }
+
+ return true
+}
+
+func isSameVersion(t *testing.T, cla TestingController, a []byte, clb TestingController, b []byte) bool {
+ aJTOC, _, err := parseStargz(io.NewSectionReader(bytes.NewReader(a), 0, int64(len(a))), cla)
+ if err != nil {
+ t.Fatalf("failed to parse A: %v", err)
+ }
+ bJTOC, _, err := parseStargz(io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), clb)
+ if err != nil {
+ t.Fatalf("failed to parse B: %v", err)
+ }
+ t.Logf("A: TOCJSON: %v", dumpTOCJSON(t, aJTOC))
+ t.Logf("B: TOCJSON: %v", dumpTOCJSON(t, bJTOC))
+ return aJTOC.Version == bJTOC.Version
+}
+
+func isSameEntries(t *testing.T, a, b *Reader) bool {
+ aroot, ok := a.Lookup("")
+ if !ok {
+ t.Fatalf("failed to get root of A")
+ }
+ broot, ok := b.Lookup("")
+ if !ok {
+ t.Fatalf("failed to get root of B")
+ }
+ aEntry := stargzEntry{aroot, a}
+ bEntry := stargzEntry{broot, b}
+ return contains(t, aEntry, bEntry) && contains(t, bEntry, aEntry)
+}
+
+func compressBlob(t *testing.T, src *io.SectionReader, srcCompression int) *io.SectionReader {
+ buf := new(bytes.Buffer)
+ var w io.WriteCloser
+ var err error
+ switch srcCompression {
+ case gzipType:
+ w = gzip.NewWriter(buf)
+ case zstdType:
+ w, err = zstd.NewWriter(buf)
+ if err != nil {
+ t.Fatalf("failed to init zstd writer: %v", err)
+ }
+ default:
+ return src
+ }
+ src.Seek(0, io.SeekStart)
+ if _, err := io.Copy(w, src); err != nil {
+ t.Fatalf("failed to compress source")
+ }
+ if err := w.Close(); err != nil {
+ t.Fatalf("failed to finalize compress source")
+ }
+ data := buf.Bytes()
+ return io.NewSectionReader(bytes.NewReader(data), 0, int64(len(data)))
+
+}
+
+type stargzEntry struct {
+ e *TOCEntry
+ r *Reader
+}
+
+// contains checks if all child entries in "b" are also contained in "a".
+// This function also checks if the files/chunks contain the same contents among "a" and "b".
+func contains(t *testing.T, a, b stargzEntry) bool {
+ ae, ar := a.e, a.r
+ be, br := b.e, b.r
+ t.Logf("Comparing: %q vs %q", ae.Name, be.Name)
+ if !equalEntry(ae, be) {
+ t.Logf("%q != %q: entry: a: %v, b: %v", ae.Name, be.Name, ae, be)
+ return false
+ }
+ if ae.Type == "dir" {
+ t.Logf("Directory: %q vs %q: %v vs %v", ae.Name, be.Name,
+ allChildrenName(ae), allChildrenName(be))
+ iscontain := true
+ ae.ForeachChild(func(aBaseName string, aChild *TOCEntry) bool {
+ // Walk through all files on this stargz file.
+
+ if aChild.Name == PrefetchLandmark ||
+ aChild.Name == NoPrefetchLandmark {
+ return true // Ignore landmarks
+ }
+
+ // Ignore a TOCEntry of "./" (formated as "" by stargz lib) on root directory
+ // because this points to the root directory itself.
+ if aChild.Name == "" && ae.Name == "" {
+ return true
+ }
+
+ bChild, ok := be.LookupChild(aBaseName)
+ if !ok {
+ t.Logf("%q (base: %q): not found in b: %v",
+ ae.Name, aBaseName, allChildrenName(be))
+ iscontain = false
+ return false
+ }
+
+ childcontain := contains(t, stargzEntry{aChild, a.r}, stargzEntry{bChild, b.r})
+ if !childcontain {
+ t.Logf("%q != %q: non-equal dir", ae.Name, be.Name)
+ iscontain = false
+ return false
+ }
+ return true
+ })
+ return iscontain
+ } else if ae.Type == "reg" {
+ af, err := ar.OpenFile(ae.Name)
+ if err != nil {
+ t.Fatalf("failed to open file %q on A: %v", ae.Name, err)
+ }
+ bf, err := br.OpenFile(be.Name)
+ if err != nil {
+ t.Fatalf("failed to open file %q on B: %v", be.Name, err)
+ }
+
+ var nr int64
+ for nr < ae.Size {
+ abytes, anext, aok := readOffset(t, af, nr, a)
+ bbytes, bnext, bok := readOffset(t, bf, nr, b)
+ if !aok && !bok {
+ break
+ } else if !aok || !bok || anext != bnext {
+ t.Logf("%q != %q (offset=%d): chunk existence a=%v vs b=%v, anext=%v vs bnext=%v",
+ ae.Name, be.Name, nr, aok, bok, anext, bnext)
+ return false
+ }
+ nr = anext
+ if !bytes.Equal(abytes, bbytes) {
+ t.Logf("%q != %q: different contents %v vs %v",
+ ae.Name, be.Name, string(abytes), string(bbytes))
+ return false
+ }
+ }
+ return true
+ }
+
+ return true
+}
+
+func allChildrenName(e *TOCEntry) (children []string) {
+ e.ForeachChild(func(baseName string, _ *TOCEntry) bool {
+ children = append(children, baseName)
+ return true
+ })
+ return
+}
+
+func equalEntry(a, b *TOCEntry) bool {
+ // Here, we selectively compare fileds that we are interested in.
+ return a.Name == b.Name &&
+ a.Type == b.Type &&
+ a.Size == b.Size &&
+ a.ModTime3339 == b.ModTime3339 &&
+ a.Stat().ModTime().Equal(b.Stat().ModTime()) && // modTime time.Time
+ a.LinkName == b.LinkName &&
+ a.Mode == b.Mode &&
+ a.UID == b.UID &&
+ a.GID == b.GID &&
+ a.Uname == b.Uname &&
+ a.Gname == b.Gname &&
+ (a.Offset >= 0) == (b.Offset >= 0) &&
+ (a.NextOffset() > 0) == (b.NextOffset() > 0) &&
+ a.DevMajor == b.DevMajor &&
+ a.DevMinor == b.DevMinor &&
+ a.NumLink == b.NumLink &&
+ reflect.DeepEqual(a.Xattrs, b.Xattrs) &&
+ // chunk-related infomations aren't compared in this function.
+ // ChunkOffset int64 `json:"chunkOffset,omitempty"`
+ // ChunkSize int64 `json:"chunkSize,omitempty"`
+ // children map[string]*TOCEntry
+ a.Digest == b.Digest
+}
+
+func readOffset(t *testing.T, r *io.SectionReader, offset int64, e stargzEntry) ([]byte, int64, bool) {
+ ce, ok := e.r.ChunkEntryForOffset(e.e.Name, offset)
+ if !ok {
+ return nil, 0, false
+ }
+ data := make([]byte, ce.ChunkSize)
+ t.Logf("Offset: %v, NextOffset: %v", ce.Offset, ce.NextOffset())
+ n, err := r.ReadAt(data, ce.ChunkOffset)
+ if err != nil {
+ t.Fatalf("failed to read file payload of %q (offset:%d,size:%d): %v",
+ e.e.Name, ce.ChunkOffset, ce.ChunkSize, err)
+ }
+ if int64(n) != ce.ChunkSize {
+ t.Fatalf("unexpected copied data size %d; want %d",
+ n, ce.ChunkSize)
+ }
+ return data[:n], offset + ce.ChunkSize, true
+}
+
+func dumpTOCJSON(t *testing.T, tocJSON *JTOC) string {
+ jtocData, err := json.Marshal(*tocJSON)
+ if err != nil {
+ t.Fatalf("failed to marshal TOC JSON: %v", err)
+ }
+ buf := new(bytes.Buffer)
+ if _, err := io.Copy(buf, bytes.NewReader(jtocData)); err != nil {
+ t.Fatalf("failed to read toc json blob: %v", err)
+ }
+ return buf.String()
+}
+
+const chunkSize = 3
+
+// type check func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, compressionLevel int)
+type check func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory)
+
+// testDigestAndVerify runs specified checks against sample stargz blobs.
+func testDigestAndVerify(t *testing.T, controllers ...TestingControllerFactory) {
+ tests := []struct {
+ name string
+ tarInit func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry)
+ checks []check
+ minChunkSize []int
+ }{
+ {
+ name: "no-regfile",
+ tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) {
+ return tarOf(
+ dir("test/"),
+ )
+ },
+ checks: []check{
+ checkStargzTOC,
+ checkVerifyTOC,
+ checkVerifyInvalidStargzFail(buildTar(t, tarOf(
+ dir("test2/"), // modified
+ ), allowedPrefix[0])),
+ },
+ },
+ {
+ name: "small-files",
+ tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) {
+ return tarOf(
+ regDigest(t, "baz.txt", "", dgstMap),
+ regDigest(t, "foo.txt", "a", dgstMap),
+ dir("test/"),
+ regDigest(t, "test/bar.txt", "bbb", dgstMap),
+ )
+ },
+ minChunkSize: []int{0, 64000},
+ checks: []check{
+ checkStargzTOC,
+ checkVerifyTOC,
+ checkVerifyInvalidStargzFail(buildTar(t, tarOf(
+ file("baz.txt", ""),
+ file("foo.txt", "M"), // modified
+ dir("test/"),
+ file("test/bar.txt", "bbb"),
+ ), allowedPrefix[0])),
+ // checkVerifyInvalidTOCEntryFail("foo.txt"), // TODO
+ checkVerifyBrokenContentFail("foo.txt"),
+ },
+ },
+ {
+ name: "big-files",
+ tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) {
+ return tarOf(
+ regDigest(t, "baz.txt", "bazbazbazbazbazbazbaz", dgstMap),
+ regDigest(t, "foo.txt", "a", dgstMap),
+ dir("test/"),
+ regDigest(t, "test/bar.txt", "testbartestbar", dgstMap),
+ )
+ },
+ checks: []check{
+ checkStargzTOC,
+ checkVerifyTOC,
+ checkVerifyInvalidStargzFail(buildTar(t, tarOf(
+ file("baz.txt", "bazbazbazMMMbazbazbaz"), // modified
+ file("foo.txt", "a"),
+ dir("test/"),
+ file("test/bar.txt", "testbartestbar"),
+ ), allowedPrefix[0])),
+ checkVerifyInvalidTOCEntryFail("test/bar.txt"),
+ checkVerifyBrokenContentFail("test/bar.txt"),
+ },
+ },
+ {
+ name: "with-non-regfiles",
+ minChunkSize: []int{0, 64000},
+ tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) {
+ return tarOf(
+ regDigest(t, "baz.txt", "bazbazbazbazbazbazbaz", dgstMap),
+ regDigest(t, "foo.txt", "a", dgstMap),
+ regDigest(t, "bar/foo2.txt", "b", dgstMap),
+ regDigest(t, "foo3.txt", "c", dgstMap),
+ symlink("barlink", "test/bar.txt"),
+ dir("test/"),
+ regDigest(t, "test/bar.txt", "testbartestbar", dgstMap),
+ dir("test2/"),
+ link("test2/bazlink", "baz.txt"),
+ )
+ },
+ checks: []check{
+ checkStargzTOC,
+ checkVerifyTOC,
+ checkVerifyInvalidStargzFail(buildTar(t, tarOf(
+ file("baz.txt", "bazbazbazbazbazbazbaz"),
+ file("foo.txt", "a"),
+ file("bar/foo2.txt", "b"),
+ file("foo3.txt", "c"),
+ symlink("barlink", "test/bar.txt"),
+ dir("test/"),
+ file("test/bar.txt", "testbartestbar"),
+ dir("test2/"),
+ link("test2/bazlink", "foo.txt"), // modified
+ ), allowedPrefix[0])),
+ checkVerifyInvalidTOCEntryFail("test/bar.txt"),
+ checkVerifyBrokenContentFail("test/bar.txt"),
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ if len(tt.minChunkSize) == 0 {
+ tt.minChunkSize = []int{0}
+ }
+ for _, srcCompression := range srcCompressions {
+ srcCompression := srcCompression
+ for _, newCL := range controllers {
+ newCL := newCL
+ for _, prefix := range allowedPrefix {
+ prefix := prefix
+ for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} {
+ srcTarFormat := srcTarFormat
+ for _, minChunkSize := range tt.minChunkSize {
+ minChunkSize := minChunkSize
+ t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,format=%s,minChunkSize=%d", newCL(), prefix, srcTarFormat, minChunkSize), func(t *testing.T) {
+ // Get original tar file and chunk digests
+ dgstMap := make(map[string]digest.Digest)
+ tarBlob := buildTar(t, tt.tarInit(t, dgstMap), prefix, srcTarFormat)
+
+ cl := newCL()
+ rc, err := Build(compressBlob(t, tarBlob, srcCompression),
+ WithChunkSize(chunkSize), WithCompression(cl))
+ if err != nil {
+ t.Fatalf("failed to convert stargz: %v", err)
+ }
+ tocDigest := rc.TOCDigest()
+ defer rc.Close()
+ buf := new(bytes.Buffer)
+ if _, err := io.Copy(buf, rc); err != nil {
+ t.Fatalf("failed to copy built stargz blob: %v", err)
+ }
+ newStargz := buf.Bytes()
+ // NoPrefetchLandmark is added during `Bulid`, which is expected behaviour.
+ dgstMap[chunkID(NoPrefetchLandmark, 0, int64(len([]byte{landmarkContents})))] = digest.FromBytes([]byte{landmarkContents})
+
+ for _, check := range tt.checks {
+ check(t, newStargz, tocDigest, dgstMap, cl, newCL)
+ }
+ })
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// checkStargzTOC checks the TOC JSON of the passed stargz has the expected
+// digest and contains valid chunks. It walks all entries in the stargz and
+// checks all chunk digests stored to the TOC JSON match the actual contents.
+func checkStargzTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
+ sgz, err := Open(
+ io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))),
+ WithDecompressors(controller),
+ )
+ if err != nil {
+ t.Errorf("failed to parse converted stargz: %v", err)
+ return
+ }
+ digestMapTOC, err := listDigests(io.NewSectionReader(
+ bytes.NewReader(sgzData), 0, int64(len(sgzData))),
+ controller,
+ )
+ if err != nil {
+ t.Fatalf("failed to list digest: %v", err)
+ }
+ found := make(map[string]bool)
+ for id := range dgstMap {
+ found[id] = false
+ }
+ zr, err := controller.Reader(bytes.NewReader(sgzData))
+ if err != nil {
+ t.Fatalf("failed to decompress converted stargz: %v", err)
+ }
+ defer zr.Close()
+ tr := tar.NewReader(zr)
+ for {
+ h, err := tr.Next()
+ if err != nil {
+ if err != io.EOF {
+ t.Errorf("failed to read tar entry: %v", err)
+ return
+ }
+ break
+ }
+ if h.Name == TOCTarName {
+ // Check the digest of TOC JSON based on the actual contents
+ // It's sure that TOC JSON exists in this archive because
+ // Open succeeded.
+ dgstr := digest.Canonical.Digester()
+ if _, err := io.Copy(dgstr.Hash(), tr); err != nil {
+ t.Fatalf("failed to calculate digest of TOC JSON: %v",
+ err)
+ }
+ if dgstr.Digest() != tocDigest {
+ t.Errorf("invalid TOC JSON %q; want %q", tocDigest, dgstr.Digest())
+ }
+ continue
+ }
+ if _, ok := sgz.Lookup(h.Name); !ok {
+ t.Errorf("lost stargz entry %q in the converted TOC", h.Name)
+ return
+ }
+ var n int64
+ for n < h.Size {
+ ce, ok := sgz.ChunkEntryForOffset(h.Name, n)
+ if !ok {
+ t.Errorf("lost chunk %q(offset=%d) in the converted TOC",
+ h.Name, n)
+ return
+ }
+
+ // Get the original digest to make sure the file contents are kept unchanged
+ // from the original tar, during the whole conversion steps.
+ id := chunkID(h.Name, n, ce.ChunkSize)
+ want, ok := dgstMap[id]
+ if !ok {
+ t.Errorf("Unexpected chunk %q(offset=%d,size=%d): %v",
+ h.Name, n, ce.ChunkSize, dgstMap)
+ return
+ }
+ found[id] = true
+
+ // Check the file contents
+ dgstr := digest.Canonical.Digester()
+ if _, err := io.CopyN(dgstr.Hash(), tr, ce.ChunkSize); err != nil {
+ t.Fatalf("failed to calculate digest of %q (offset=%d,size=%d)",
+ h.Name, n, ce.ChunkSize)
+ }
+ if want != dgstr.Digest() {
+ t.Errorf("Invalid contents in converted stargz %q: %q; want %q",
+ h.Name, dgstr.Digest(), want)
+ return
+ }
+
+ // Check the digest stored in TOC JSON
+ dgstTOC, ok := digestMapTOC[ce.Offset]
+ if !ok {
+ t.Errorf("digest of %q(offset=%d,size=%d,chunkOffset=%d) isn't registered",
+ h.Name, ce.Offset, ce.ChunkSize, ce.ChunkOffset)
+ }
+ if want != dgstTOC {
+ t.Errorf("Invalid digest in TOCEntry %q: %q; want %q",
+ h.Name, dgstTOC, want)
+ return
+ }
+
+ n += ce.ChunkSize
+ }
+ }
+
+ for id, ok := range found {
+ if !ok {
+ t.Errorf("required chunk %q not found in the converted stargz: %v", id, found)
+ }
+ }
+}
+
+// checkVerifyTOC checks the verification works for the TOC JSON of the passed
+// stargz. It walks all entries in the stargz and checks the verifications for
+// all chunks work.
+func checkVerifyTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
+ sgz, err := Open(
+ io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))),
+ WithDecompressors(controller),
+ )
+ if err != nil {
+ t.Errorf("failed to parse converted stargz: %v", err)
+ return
+ }
+ ev, err := sgz.VerifyTOC(tocDigest)
+ if err != nil {
+ t.Errorf("failed to verify stargz: %v", err)
+ return
+ }
+
+ found := make(map[string]bool)
+ for id := range dgstMap {
+ found[id] = false
+ }
+ zr, err := controller.Reader(bytes.NewReader(sgzData))
+ if err != nil {
+ t.Fatalf("failed to decompress converted stargz: %v", err)
+ }
+ defer zr.Close()
+ tr := tar.NewReader(zr)
+ for {
+ h, err := tr.Next()
+ if err != nil {
+ if err != io.EOF {
+ t.Errorf("failed to read tar entry: %v", err)
+ return
+ }
+ break
+ }
+ if h.Name == TOCTarName {
+ continue
+ }
+ if _, ok := sgz.Lookup(h.Name); !ok {
+ t.Errorf("lost stargz entry %q in the converted TOC", h.Name)
+ return
+ }
+ var n int64
+ for n < h.Size {
+ ce, ok := sgz.ChunkEntryForOffset(h.Name, n)
+ if !ok {
+ t.Errorf("lost chunk %q(offset=%d) in the converted TOC",
+ h.Name, n)
+ return
+ }
+
+ v, err := ev.Verifier(ce)
+ if err != nil {
+ t.Errorf("failed to get verifier for %q(offset=%d)", h.Name, n)
+ }
+
+ found[chunkID(h.Name, n, ce.ChunkSize)] = true
+
+ // Check the file contents
+ if _, err := io.CopyN(v, tr, ce.ChunkSize); err != nil {
+ t.Fatalf("failed to get chunk of %q (offset=%d,size=%d)",
+ h.Name, n, ce.ChunkSize)
+ }
+ if !v.Verified() {
+ t.Errorf("Invalid contents in converted stargz %q (should be succeeded)",
+ h.Name)
+ return
+ }
+ n += ce.ChunkSize
+ }
+ }
+
+ for id, ok := range found {
+ if !ok {
+ t.Errorf("required chunk %q not found in the converted stargz: %v", id, found)
+ }
+ }
+}
+
+// checkVerifyInvalidTOCEntryFail checks if misconfigured TOC JSON can be
+// detected during the verification and the verification returns an error.
+func checkVerifyInvalidTOCEntryFail(filename string) check {
+ return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
+ funcs := map[string]rewriteFunc{
+ "lost digest in a entry": func(t *testing.T, toc *JTOC, sgz *io.SectionReader) {
+ var found bool
+ for _, e := range toc.Entries {
+ if cleanEntryName(e.Name) == filename {
+ if e.Type != "reg" && e.Type != "chunk" {
+ t.Fatalf("entry %q to break must be regfile or chunk", filename)
+ }
+ if e.ChunkDigest == "" {
+ t.Fatalf("entry %q is already invalid", filename)
+ }
+ e.ChunkDigest = ""
+ found = true
+ }
+ }
+ if !found {
+ t.Fatalf("rewrite target not found")
+ }
+ },
+ "duplicated entry offset": func(t *testing.T, toc *JTOC, sgz *io.SectionReader) {
+ var (
+ sampleEntry *TOCEntry
+ targetEntry *TOCEntry
+ )
+ for _, e := range toc.Entries {
+ if e.Type == "reg" || e.Type == "chunk" {
+ if cleanEntryName(e.Name) == filename {
+ targetEntry = e
+ } else {
+ sampleEntry = e
+ }
+ }
+ }
+ if sampleEntry == nil {
+ t.Fatalf("TOC must contain at least one regfile or chunk entry other than the rewrite target")
+ return
+ }
+ if targetEntry == nil {
+ t.Fatalf("rewrite target not found")
+ return
+ }
+ targetEntry.Offset = sampleEntry.Offset
+ },
+ }
+
+ for name, rFunc := range funcs {
+ t.Run(name, func(t *testing.T) {
+ newSgz, newTocDigest := rewriteTOCJSON(t, io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), rFunc, controller)
+ buf := new(bytes.Buffer)
+ if _, err := io.Copy(buf, newSgz); err != nil {
+ t.Fatalf("failed to get converted stargz")
+ }
+ isgz := buf.Bytes()
+
+ sgz, err := Open(
+ io.NewSectionReader(bytes.NewReader(isgz), 0, int64(len(isgz))),
+ WithDecompressors(controller),
+ )
+ if err != nil {
+ t.Fatalf("failed to parse converted stargz: %v", err)
+ return
+ }
+ _, err = sgz.VerifyTOC(newTocDigest)
+ if err == nil {
+ t.Errorf("must fail for invalid TOC")
+ return
+ }
+ })
+ }
+ }
+}
+
+// checkVerifyInvalidStargzFail checks if the verification detects that the
+// given stargz file doesn't match to the expected digest and returns error.
+func checkVerifyInvalidStargzFail(invalid *io.SectionReader) check {
+ return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
+ cl := newController()
+ rc, err := Build(invalid, WithChunkSize(chunkSize), WithCompression(cl))
+ if err != nil {
+ t.Fatalf("failed to convert stargz: %v", err)
+ }
+ defer rc.Close()
+ buf := new(bytes.Buffer)
+ if _, err := io.Copy(buf, rc); err != nil {
+ t.Fatalf("failed to copy built stargz blob: %v", err)
+ }
+ mStargz := buf.Bytes()
+
+ sgz, err := Open(
+ io.NewSectionReader(bytes.NewReader(mStargz), 0, int64(len(mStargz))),
+ WithDecompressors(cl),
+ )
+ if err != nil {
+ t.Fatalf("failed to parse converted stargz: %v", err)
+ return
+ }
+ _, err = sgz.VerifyTOC(tocDigest)
+ if err == nil {
+ t.Errorf("must fail for invalid TOC")
+ return
+ }
+ }
+}
+
+// checkVerifyBrokenContentFail checks if the verifier detects broken contents
+// that doesn't match to the expected digest and returns error.
+func checkVerifyBrokenContentFail(filename string) check {
+ return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) {
+ // Parse stargz file
+ sgz, err := Open(
+ io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))),
+ WithDecompressors(controller),
+ )
+ if err != nil {
+ t.Fatalf("failed to parse converted stargz: %v", err)
+ return
+ }
+ ev, err := sgz.VerifyTOC(tocDigest)
+ if err != nil {
+ t.Fatalf("failed to verify stargz: %v", err)
+ return
+ }
+
+ // Open the target file
+ sr, err := sgz.OpenFile(filename)
+ if err != nil {
+ t.Fatalf("failed to open file %q", filename)
+ }
+ ce, ok := sgz.ChunkEntryForOffset(filename, 0)
+ if !ok {
+ t.Fatalf("lost chunk %q(offset=%d) in the converted TOC", filename, 0)
+ return
+ }
+ if ce.ChunkSize == 0 {
+ t.Fatalf("file mustn't be empty")
+ return
+ }
+ data := make([]byte, ce.ChunkSize)
+ if _, err := sr.ReadAt(data, ce.ChunkOffset); err != nil {
+ t.Errorf("failed to get data of a chunk of %q(offset=%q)",
+ filename, ce.ChunkOffset)
+ }
+
+ // Check the broken chunk (must fail)
+ v, err := ev.Verifier(ce)
+ if err != nil {
+ t.Fatalf("failed to get verifier for %q", filename)
+ }
+ broken := append([]byte{^data[0]}, data[1:]...)
+ if _, err := io.CopyN(v, bytes.NewReader(broken), ce.ChunkSize); err != nil {
+ t.Fatalf("failed to get chunk of %q (offset=%d,size=%d)",
+ filename, ce.ChunkOffset, ce.ChunkSize)
+ }
+ if v.Verified() {
+ t.Errorf("verification must fail for broken file chunk %q(org:%q,broken:%q)",
+ filename, data, broken)
+ }
+ }
+}
+
+func chunkID(name string, offset, size int64) string {
+ return fmt.Sprintf("%s-%d-%d", cleanEntryName(name), offset, size)
+}
+
+type rewriteFunc func(t *testing.T, toc *JTOC, sgz *io.SectionReader)
+
+func rewriteTOCJSON(t *testing.T, sgz *io.SectionReader, rewrite rewriteFunc, controller TestingController) (newSgz io.Reader, tocDigest digest.Digest) {
+ decodedJTOC, jtocOffset, err := parseStargz(sgz, controller)
+ if err != nil {
+ t.Fatalf("failed to extract TOC JSON: %v", err)
+ }
+
+ rewrite(t, decodedJTOC, sgz)
+
+ tocFooter, tocDigest, err := tocAndFooter(controller, decodedJTOC, jtocOffset)
+ if err != nil {
+ t.Fatalf("failed to create toc and footer: %v", err)
+ }
+
+ // Reconstruct stargz file with the modified TOC JSON
+ if _, err := sgz.Seek(0, io.SeekStart); err != nil {
+ t.Fatalf("failed to reset the seek position of stargz: %v", err)
+ }
+ return io.MultiReader(
+ io.LimitReader(sgz, jtocOffset), // Original stargz (before TOC JSON)
+ tocFooter, // Rewritten TOC and footer
+ ), tocDigest
+}
+
+func listDigests(sgz *io.SectionReader, controller TestingController) (map[int64]digest.Digest, error) {
+ decodedJTOC, _, err := parseStargz(sgz, controller)
+ if err != nil {
+ return nil, err
+ }
+ digestMap := make(map[int64]digest.Digest)
+ for _, e := range decodedJTOC.Entries {
+ if e.Type == "reg" || e.Type == "chunk" {
+ if e.Type == "reg" && e.Size == 0 {
+ continue // ignores empty file
+ }
+ if e.ChunkDigest == "" {
+ return nil, fmt.Errorf("ChunkDigest of %q(off=%d) not found in TOC JSON",
+ e.Name, e.Offset)
+ }
+ d, err := digest.Parse(e.ChunkDigest)
+ if err != nil {
+ return nil, err
+ }
+ digestMap[e.Offset] = d
+ }
+ }
+ return digestMap, nil
+}
+
+func parseStargz(sgz *io.SectionReader, controller TestingController) (decodedJTOC *JTOC, jtocOffset int64, err error) {
+ fSize := controller.FooterSize()
+ footer := make([]byte, fSize)
+ if _, err := sgz.ReadAt(footer, sgz.Size()-fSize); err != nil {
+ return nil, 0, fmt.Errorf("error reading footer: %w", err)
+ }
+ _, tocOffset, _, err := controller.ParseFooter(footer[positive(int64(len(footer))-fSize):])
+ if err != nil {
+ return nil, 0, fmt.Errorf("failed to parse footer: %w", err)
+ }
+
+ // Decode the TOC JSON
+ var tocReader io.Reader
+ if tocOffset >= 0 {
+ tocReader = io.NewSectionReader(sgz, tocOffset, sgz.Size()-tocOffset-fSize)
+ }
+ decodedJTOC, _, err = controller.ParseTOC(tocReader)
+ if err != nil {
+ return nil, 0, fmt.Errorf("failed to parse TOC: %w", err)
+ }
+ return decodedJTOC, tocOffset, nil
+}
+
+func testWriteAndOpen(t *testing.T, controllers ...TestingControllerFactory) {
+ const content = "Some contents"
+ invalidUtf8 := "\xff\xfe\xfd"
+
+ xAttrFile := xAttr{"foo": "bar", "invalid-utf8": invalidUtf8}
+ sampleOwner := owner{uid: 50, gid: 100}
+
+ data64KB := randomContents(64000)
+
+ tests := []struct {
+ name string
+ chunkSize int
+ minChunkSize int
+ in []tarEntry
+ want []stargzCheck
+ wantNumGz int // expected number of streams
+
+ wantNumGzLossLess int // expected number of streams (> 0) in lossless mode if it's different from wantNumGz
+ wantFailOnLossLess bool
+ wantTOCVersion int // default = 1
+ }{
+ {
+ name: "empty",
+ in: tarOf(),
+ wantNumGz: 2, // (empty tar) + TOC + footer
+ want: checks(
+ numTOCEntries(0),
+ ),
+ },
+ {
+ name: "1dir_1empty_file",
+ in: tarOf(
+ dir("foo/"),
+ file("foo/bar.txt", ""),
+ ),
+ wantNumGz: 3, // dir, TOC, footer
+ want: checks(
+ numTOCEntries(2),
+ hasDir("foo/"),
+ hasFileLen("foo/bar.txt", 0),
+ entryHasChildren("foo", "bar.txt"),
+ hasFileDigest("foo/bar.txt", digestFor("")),
+ ),
+ },
+ {
+ name: "1dir_1file",
+ in: tarOf(
+ dir("foo/"),
+ file("foo/bar.txt", content, xAttrFile),
+ ),
+ wantNumGz: 4, // var dir, foo.txt alone, TOC, footer
+ want: checks(
+ numTOCEntries(2),
+ hasDir("foo/"),
+ hasFileLen("foo/bar.txt", len(content)),
+ hasFileDigest("foo/bar.txt", digestFor(content)),
+ hasFileContentsRange("foo/bar.txt", 0, content),
+ hasFileContentsRange("foo/bar.txt", 1, content[1:]),
+ entryHasChildren("", "foo"),
+ entryHasChildren("foo", "bar.txt"),
+ hasFileXattrs("foo/bar.txt", "foo", "bar"),
+ hasFileXattrs("foo/bar.txt", "invalid-utf8", invalidUtf8),
+ ),
+ },
+ {
+ name: "2meta_2file",
+ in: tarOf(
+ dir("bar/", sampleOwner),
+ dir("foo/", sampleOwner),
+ file("foo/bar.txt", content, sampleOwner),
+ ),
+ wantNumGz: 4, // both dirs, foo.txt alone, TOC, footer
+ want: checks(
+ numTOCEntries(3),
+ hasDir("bar/"),
+ hasDir("foo/"),
+ hasFileLen("foo/bar.txt", len(content)),
+ entryHasChildren("", "bar", "foo"),
+ entryHasChildren("foo", "bar.txt"),
+ hasChunkEntries("foo/bar.txt", 1),
+ hasEntryOwner("bar/", sampleOwner),
+ hasEntryOwner("foo/", sampleOwner),
+ hasEntryOwner("foo/bar.txt", sampleOwner),
+ ),
+ },
+ {
+ name: "3dir",
+ in: tarOf(
+ dir("bar/"),
+ dir("foo/"),
+ dir("foo/bar/"),
+ ),
+ wantNumGz: 3, // 3 dirs, TOC, footer
+ want: checks(
+ hasDirLinkCount("bar/", 2),
+ hasDirLinkCount("foo/", 3),
+ hasDirLinkCount("foo/bar/", 2),
+ ),
+ },
+ {
+ name: "symlink",
+ in: tarOf(
+ dir("foo/"),
+ symlink("foo/bar", "../../x"),
+ ),
+ wantNumGz: 3, // metas + TOC + footer
+ want: checks(
+ numTOCEntries(2),
+ hasSymlink("foo/bar", "../../x"),
+ entryHasChildren("", "foo"),
+ entryHasChildren("foo", "bar"),
+ ),
+ },
+ {
+ name: "chunked_file",
+ chunkSize: 4,
+ in: tarOf(
+ dir("foo/"),
+ file("foo/big.txt", "This "+"is s"+"uch "+"a bi"+"g fi"+"le"),
+ ),
+ wantNumGz: 9, // dir + big.txt(6 chunks) + TOC + footer
+ want: checks(
+ numTOCEntries(7), // 1 for foo dir, 6 for the foo/big.txt file
+ hasDir("foo/"),
+ hasFileLen("foo/big.txt", len("This is such a big file")),
+ hasFileDigest("foo/big.txt", digestFor("This is such a big file")),
+ hasFileContentsRange("foo/big.txt", 0, "This is such a big file"),
+ hasFileContentsRange("foo/big.txt", 1, "his is such a big file"),
+ hasFileContentsRange("foo/big.txt", 2, "is is such a big file"),
+ hasFileContentsRange("foo/big.txt", 3, "s is such a big file"),
+ hasFileContentsRange("foo/big.txt", 4, " is such a big file"),
+ hasFileContentsRange("foo/big.txt", 5, "is such a big file"),
+ hasFileContentsRange("foo/big.txt", 6, "s such a big file"),
+ hasFileContentsRange("foo/big.txt", 7, " such a big file"),
+ hasFileContentsRange("foo/big.txt", 8, "such a big file"),
+ hasFileContentsRange("foo/big.txt", 9, "uch a big file"),
+ hasFileContentsRange("foo/big.txt", 10, "ch a big file"),
+ hasFileContentsRange("foo/big.txt", 11, "h a big file"),
+ hasFileContentsRange("foo/big.txt", 12, " a big file"),
+ hasFileContentsRange("foo/big.txt", len("This is such a big file")-1, ""),
+ hasChunkEntries("foo/big.txt", 6),
+ ),
+ },
+ {
+ name: "recursive",
+ in: tarOf(
+ dir("/", sampleOwner),
+ dir("bar/", sampleOwner),
+ dir("foo/", sampleOwner),
+ file("foo/bar.txt", content, sampleOwner),
+ ),
+ wantNumGz: 4, // dirs, bar.txt alone, TOC, footer
+ want: checks(
+ maxDepth(2), // 0: root directory, 1: "foo/", 2: "bar.txt"
+ ),
+ },
+ {
+ name: "block_char_fifo",
+ in: tarOf(
+ tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error {
+ return w.WriteHeader(&tar.Header{
+ Name: prefix + "b",
+ Typeflag: tar.TypeBlock,
+ Devmajor: 123,
+ Devminor: 456,
+ Format: format,
+ })
+ }),
+ tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error {
+ return w.WriteHeader(&tar.Header{
+ Name: prefix + "c",
+ Typeflag: tar.TypeChar,
+ Devmajor: 111,
+ Devminor: 222,
+ Format: format,
+ })
+ }),
+ tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error {
+ return w.WriteHeader(&tar.Header{
+ Name: prefix + "f",
+ Typeflag: tar.TypeFifo,
+ Format: format,
+ })
+ }),
+ ),
+ wantNumGz: 3,
+ want: checks(
+ lookupMatch("b", &TOCEntry{Name: "b", Type: "block", DevMajor: 123, DevMinor: 456, NumLink: 1}),
+ lookupMatch("c", &TOCEntry{Name: "c", Type: "char", DevMajor: 111, DevMinor: 222, NumLink: 1}),
+ lookupMatch("f", &TOCEntry{Name: "f", Type: "fifo", NumLink: 1}),
+ ),
+ },
+ {
+ name: "modes",
+ in: tarOf(
+ dir("foo1/", 0755|os.ModeDir|os.ModeSetgid),
+ file("foo1/bar1", content, 0700|os.ModeSetuid),
+ file("foo1/bar2", content, 0755|os.ModeSetgid),
+ dir("foo2/", 0755|os.ModeDir|os.ModeSticky),
+ file("foo2/bar3", content, 0755|os.ModeSticky),
+ dir("foo3/", 0755|os.ModeDir),
+ file("foo3/bar4", content, os.FileMode(0700)),
+ file("foo3/bar5", content, os.FileMode(0755)),
+ ),
+ wantNumGz: 8, // dir, bar1 alone, bar2 alone + dir, bar3 alone + dir, bar4 alone, bar5 alone, TOC, footer
+ want: checks(
+ hasMode("foo1/", 0755|os.ModeDir|os.ModeSetgid),
+ hasMode("foo1/bar1", 0700|os.ModeSetuid),
+ hasMode("foo1/bar2", 0755|os.ModeSetgid),
+ hasMode("foo2/", 0755|os.ModeDir|os.ModeSticky),
+ hasMode("foo2/bar3", 0755|os.ModeSticky),
+ hasMode("foo3/", 0755|os.ModeDir),
+ hasMode("foo3/bar4", os.FileMode(0700)),
+ hasMode("foo3/bar5", os.FileMode(0755)),
+ ),
+ },
+ {
+ name: "lossy",
+ in: tarOf(
+ dir("bar/", sampleOwner),
+ dir("foo/", sampleOwner),
+ file("foo/bar.txt", content, sampleOwner),
+ file(TOCTarName, "dummy"), // ignored by the writer. (lossless write returns error)
+ ),
+ wantNumGz: 4, // both dirs, foo.txt alone, TOC, footer
+ want: checks(
+ numTOCEntries(3),
+ hasDir("bar/"),
+ hasDir("foo/"),
+ hasFileLen("foo/bar.txt", len(content)),
+ entryHasChildren("", "bar", "foo"),
+ entryHasChildren("foo", "bar.txt"),
+ hasChunkEntries("foo/bar.txt", 1),
+ hasEntryOwner("bar/", sampleOwner),
+ hasEntryOwner("foo/", sampleOwner),
+ hasEntryOwner("foo/bar.txt", sampleOwner),
+ ),
+ wantFailOnLossLess: true,
+ },
+ {
+ name: "hardlink should be replaced to the destination entry",
+ in: tarOf(
+ dir("foo/"),
+ file("foo/foo1", "test"),
+ link("foolink", "foo/foo1"),
+ ),
+ wantNumGz: 4, // dir, foo1 + link, TOC, footer
+ want: checks(
+ mustSameEntry("foo/foo1", "foolink"),
+ ),
+ },
+ {
+ name: "several_files_in_chunk",
+ minChunkSize: 8000,
+ in: tarOf(
+ dir("foo/"),
+ file("foo/foo1", data64KB),
+ file("foo2", "bb"),
+ file("foo22", "ccc"),
+ dir("bar/"),
+ file("bar/bar.txt", "aaa"),
+ file("foo3", data64KB),
+ ),
+ // NOTE: we assume that the compressed "data64KB" is still larger than 8KB
+ wantNumGz: 4, // dir+foo1, foo2+foo22+dir+bar.txt+foo3, TOC, footer
+ want: checks(
+ numTOCEntries(7), // dir, foo1, foo2, foo22, dir, bar.txt, foo3
+ hasDir("foo/"),
+ hasDir("bar/"),
+ hasFileLen("foo/foo1", len(data64KB)),
+ hasFileLen("foo2", len("bb")),
+ hasFileLen("foo22", len("ccc")),
+ hasFileLen("bar/bar.txt", len("aaa")),
+ hasFileLen("foo3", len(data64KB)),
+ hasFileDigest("foo/foo1", digestFor(data64KB)),
+ hasFileDigest("foo2", digestFor("bb")),
+ hasFileDigest("foo22", digestFor("ccc")),
+ hasFileDigest("bar/bar.txt", digestFor("aaa")),
+ hasFileDigest("foo3", digestFor(data64KB)),
+ hasFileContentsWithPreRead("foo22", 0, "ccc", chunkInfo{"foo2", "bb"}, chunkInfo{"bar/bar.txt", "aaa"}, chunkInfo{"foo3", data64KB}),
+ hasFileContentsRange("foo/foo1", 0, data64KB),
+ hasFileContentsRange("foo2", 0, "bb"),
+ hasFileContentsRange("foo2", 1, "b"),
+ hasFileContentsRange("foo22", 0, "ccc"),
+ hasFileContentsRange("foo22", 1, "cc"),
+ hasFileContentsRange("foo22", 2, "c"),
+ hasFileContentsRange("bar/bar.txt", 0, "aaa"),
+ hasFileContentsRange("bar/bar.txt", 1, "aa"),
+ hasFileContentsRange("bar/bar.txt", 2, "a"),
+ hasFileContentsRange("foo3", 0, data64KB),
+ hasFileContentsRange("foo3", 1, data64KB[1:]),
+ hasFileContentsRange("foo3", 2, data64KB[2:]),
+ hasFileContentsRange("foo3", len(data64KB)/2, data64KB[len(data64KB)/2:]),
+ hasFileContentsRange("foo3", len(data64KB)-1, data64KB[len(data64KB)-1:]),
+ ),
+ },
+ {
+ name: "several_files_in_chunk_chunked",
+ minChunkSize: 8000,
+ chunkSize: 32000,
+ in: tarOf(
+ dir("foo/"),
+ file("foo/foo1", data64KB),
+ file("foo2", "bb"),
+ dir("bar/"),
+ file("foo3", data64KB),
+ ),
+ // NOTE: we assume that the compressed chunk of "data64KB" is still larger than 8KB
+ wantNumGz: 6, // dir+foo1(1), foo1(2), foo2+dir+foo3(1), foo3(2), TOC, footer
+ want: checks(
+ numTOCEntries(7), // dir, foo1(2 chunks), foo2, dir, foo3(2 chunks)
+ hasDir("foo/"),
+ hasDir("bar/"),
+ hasFileLen("foo/foo1", len(data64KB)),
+ hasFileLen("foo2", len("bb")),
+ hasFileLen("foo3", len(data64KB)),
+ hasFileDigest("foo/foo1", digestFor(data64KB)),
+ hasFileDigest("foo2", digestFor("bb")),
+ hasFileDigest("foo3", digestFor(data64KB)),
+ hasFileContentsWithPreRead("foo2", 0, "bb", chunkInfo{"foo3", data64KB[:32000]}),
+ hasFileContentsRange("foo/foo1", 0, data64KB),
+ hasFileContentsRange("foo/foo1", 1, data64KB[1:]),
+ hasFileContentsRange("foo/foo1", 2, data64KB[2:]),
+ hasFileContentsRange("foo/foo1", len(data64KB)/2, data64KB[len(data64KB)/2:]),
+ hasFileContentsRange("foo/foo1", len(data64KB)-1, data64KB[len(data64KB)-1:]),
+ hasFileContentsRange("foo2", 0, "bb"),
+ hasFileContentsRange("foo2", 1, "b"),
+ hasFileContentsRange("foo3", 0, data64KB),
+ hasFileContentsRange("foo3", 1, data64KB[1:]),
+ hasFileContentsRange("foo3", 2, data64KB[2:]),
+ hasFileContentsRange("foo3", len(data64KB)/2, data64KB[len(data64KB)/2:]),
+ hasFileContentsRange("foo3", len(data64KB)-1, data64KB[len(data64KB)-1:]),
+ ),
+ },
+ }
+
+ for _, tt := range tests {
+ for _, newCL := range controllers {
+ newCL := newCL
+ for _, prefix := range allowedPrefix {
+ prefix := prefix
+ for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} {
+ srcTarFormat := srcTarFormat
+ for _, lossless := range []bool{true, false} {
+ t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,lossless=%v,format=%s", newCL(), prefix, lossless, srcTarFormat), func(t *testing.T) {
+ var tr io.Reader = buildTar(t, tt.in, prefix, srcTarFormat)
+ origTarDgstr := digest.Canonical.Digester()
+ tr = io.TeeReader(tr, origTarDgstr.Hash())
+ var stargzBuf bytes.Buffer
+ cl1 := newCL()
+ w := NewWriterWithCompressor(&stargzBuf, cl1)
+ w.ChunkSize = tt.chunkSize
+ w.MinChunkSize = tt.minChunkSize
+ if lossless {
+ err := w.AppendTarLossLess(tr)
+ if tt.wantFailOnLossLess {
+ if err != nil {
+ return // expected to fail
+ }
+ t.Fatalf("Append wanted to fail on lossless")
+ }
+ if err != nil {
+ t.Fatalf("Append(lossless): %v", err)
+ }
+ } else {
+ if err := w.AppendTar(tr); err != nil {
+ t.Fatalf("Append: %v", err)
+ }
+ }
+ if _, err := w.Close(); err != nil {
+ t.Fatalf("Writer.Close: %v", err)
+ }
+ b := stargzBuf.Bytes()
+
+ if lossless {
+ // Check if the result blob reserves original tar metadata
+ rc, err := Unpack(io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), cl1)
+ if err != nil {
+ t.Errorf("failed to decompress blob: %v", err)
+ return
+ }
+ defer rc.Close()
+ resultDgstr := digest.Canonical.Digester()
+ if _, err := io.Copy(resultDgstr.Hash(), rc); err != nil {
+ t.Errorf("failed to read result decompressed blob: %v", err)
+ return
+ }
+ if resultDgstr.Digest() != origTarDgstr.Digest() {
+ t.Errorf("lossy compression occurred: digest=%v; want %v",
+ resultDgstr.Digest(), origTarDgstr.Digest())
+ return
+ }
+ }
+
+ diffID := w.DiffID()
+ wantDiffID := cl1.DiffIDOf(t, b)
+ if diffID != wantDiffID {
+ t.Errorf("DiffID = %q; want %q", diffID, wantDiffID)
+ }
+
+ telemetry, checkCalled := newCalledTelemetry()
+ sr := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b)))
+ r, err := Open(
+ sr,
+ WithDecompressors(cl1),
+ WithTelemetry(telemetry),
+ )
+ if err != nil {
+ t.Fatalf("stargz.Open: %v", err)
+ }
+ wantTOCVersion := 1
+ if tt.wantTOCVersion > 0 {
+ wantTOCVersion = tt.wantTOCVersion
+ }
+ if r.toc.Version != wantTOCVersion {
+ t.Fatalf("invalid TOC Version %d; wanted %d", r.toc.Version, wantTOCVersion)
+ }
+
+ footerSize := cl1.FooterSize()
+ footerOffset := sr.Size() - footerSize
+ footer := make([]byte, footerSize)
+ if _, err := sr.ReadAt(footer, footerOffset); err != nil {
+ t.Errorf("failed to read footer: %v", err)
+ }
+ _, tocOffset, _, err := cl1.ParseFooter(footer)
+ if err != nil {
+ t.Errorf("failed to parse footer: %v", err)
+ }
+ if err := checkCalled(tocOffset >= 0); err != nil {
+ t.Errorf("telemetry failure: %v", err)
+ }
+
+ wantNumGz := tt.wantNumGz
+ if lossless && tt.wantNumGzLossLess > 0 {
+ wantNumGz = tt.wantNumGzLossLess
+ }
+ streamOffsets := []int64{0}
+ prevOffset := int64(-1)
+ streams := 0
+ for _, e := range r.toc.Entries {
+ if e.Offset > prevOffset {
+ streamOffsets = append(streamOffsets, e.Offset)
+ prevOffset = e.Offset
+ streams++
+ }
+ }
+ streams++ // TOC
+ if tocOffset >= 0 {
+ // toc is in the blob
+ streamOffsets = append(streamOffsets, tocOffset)
+ }
+ streams++ // footer
+ streamOffsets = append(streamOffsets, footerOffset)
+ if streams != wantNumGz {
+ t.Errorf("number of streams in TOC = %d; want %d", streams, wantNumGz)
+ }
+
+ t.Logf("testing streams: %+v", streamOffsets)
+ cl1.TestStreams(t, b, streamOffsets)
+
+ for _, want := range tt.want {
+ want.check(t, r)
+ }
+ })
+ }
+ }
+ }
+ }
+ }
+}
+
+type chunkInfo struct {
+ name string
+ data string
+}
+
+func newCalledTelemetry() (telemetry *Telemetry, check func(needsGetTOC bool) error) {
+ var getFooterLatencyCalled bool
+ var getTocLatencyCalled bool
+ var deserializeTocLatencyCalled bool
+ return &Telemetry{
+ func(time.Time) { getFooterLatencyCalled = true },
+ func(time.Time) { getTocLatencyCalled = true },
+ func(time.Time) { deserializeTocLatencyCalled = true },
+ }, func(needsGetTOC bool) error {
+ var allErr []error
+ if !getFooterLatencyCalled {
+ allErr = append(allErr, fmt.Errorf("metrics GetFooterLatency isn't called"))
+ }
+ if needsGetTOC {
+ if !getTocLatencyCalled {
+ allErr = append(allErr, fmt.Errorf("metrics GetTocLatency isn't called"))
+ }
+ }
+ if !deserializeTocLatencyCalled {
+ allErr = append(allErr, fmt.Errorf("metrics DeserializeTocLatency isn't called"))
+ }
+ return errorutil.Aggregate(allErr)
+ }
+}
+
+func digestFor(content string) string {
+ sum := sha256.Sum256([]byte(content))
+ return fmt.Sprintf("sha256:%x", sum)
+}
+
+type numTOCEntries int
+
+func (n numTOCEntries) check(t *testing.T, r *Reader) {
+ if r.toc == nil {
+ t.Fatal("nil TOC")
+ }
+ if got, want := len(r.toc.Entries), int(n); got != want {
+ t.Errorf("got %d TOC entries; want %d", got, want)
+ }
+ t.Logf("got TOC entries:")
+ for i, ent := range r.toc.Entries {
+ entj, _ := json.Marshal(ent)
+ t.Logf(" [%d]: %s\n", i, entj)
+ }
+ if t.Failed() {
+ t.FailNow()
+ }
+}
+
+func checks(s ...stargzCheck) []stargzCheck { return s }
+
+type stargzCheck interface {
+ check(t *testing.T, r *Reader)
+}
+
+type stargzCheckFn func(*testing.T, *Reader)
+
+func (f stargzCheckFn) check(t *testing.T, r *Reader) { f(t, r) }
+
+func maxDepth(max int) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ e, ok := r.Lookup("")
+ if !ok {
+ t.Fatal("root directory not found")
+ }
+ d, err := getMaxDepth(t, e, 0, 10*max)
+ if err != nil {
+ t.Errorf("failed to get max depth (wanted %d): %v", max, err)
+ return
+ }
+ if d != max {
+ t.Errorf("invalid depth %d; want %d", d, max)
+ return
+ }
+ })
+}
+
+func getMaxDepth(t *testing.T, e *TOCEntry, current, limit int) (max int, rErr error) {
+ if current > limit {
+ return -1, fmt.Errorf("walkMaxDepth: exceeds limit: current:%d > limit:%d",
+ current, limit)
+ }
+ max = current
+ e.ForeachChild(func(baseName string, ent *TOCEntry) bool {
+ t.Logf("%q(basename:%q) is child of %q\n", ent.Name, baseName, e.Name)
+ d, err := getMaxDepth(t, ent, current+1, limit)
+ if err != nil {
+ rErr = err
+ return false
+ }
+ if d > max {
+ max = d
+ }
+ return true
+ })
+ return
+}
+
+func hasFileLen(file string, wantLen int) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ for _, ent := range r.toc.Entries {
+ if ent.Name == file {
+ if ent.Type != "reg" {
+ t.Errorf("file type of %q is %q; want \"reg\"", file, ent.Type)
+ } else if ent.Size != int64(wantLen) {
+ t.Errorf("file size of %q = %d; want %d", file, ent.Size, wantLen)
+ }
+ return
+ }
+ }
+ t.Errorf("file %q not found", file)
+ })
+}
+
+func hasFileXattrs(file, name, value string) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ for _, ent := range r.toc.Entries {
+ if ent.Name == file {
+ if ent.Type != "reg" {
+ t.Errorf("file type of %q is %q; want \"reg\"", file, ent.Type)
+ }
+ if ent.Xattrs == nil {
+ t.Errorf("file %q has no xattrs", file)
+ return
+ }
+ valueFound, found := ent.Xattrs[name]
+ if !found {
+ t.Errorf("file %q has no xattr %q", file, name)
+ return
+ }
+ if string(valueFound) != value {
+ t.Errorf("file %q has xattr %q with value %q instead of %q", file, name, valueFound, value)
+ }
+
+ return
+ }
+ }
+ t.Errorf("file %q not found", file)
+ })
+}
+
+func hasFileDigest(file string, digest string) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ ent, ok := r.Lookup(file)
+ if !ok {
+ t.Fatalf("didn't find TOCEntry for file %q", file)
+ }
+ if ent.Digest != digest {
+ t.Fatalf("Digest(%q) = %q, want %q", file, ent.Digest, digest)
+ }
+ })
+}
+
+func hasFileContentsWithPreRead(file string, offset int, want string, extra ...chunkInfo) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ extraMap := make(map[string]chunkInfo)
+ for _, e := range extra {
+ extraMap[e.name] = e
+ }
+ var extraNames []string
+ for n := range extraMap {
+ extraNames = append(extraNames, n)
+ }
+ f, err := r.OpenFileWithPreReader(file, func(e *TOCEntry, cr io.Reader) error {
+ t.Logf("On %q: got preread of %q", file, e.Name)
+ ex, ok := extraMap[e.Name]
+ if !ok {
+ t.Fatalf("fail on %q: unexpected entry %q: %+v, %+v", file, e.Name, e, extraNames)
+ }
+ got, err := io.ReadAll(cr)
+ if err != nil {
+ t.Fatalf("fail on %q: failed to read %q: %v", file, e.Name, err)
+ }
+ if ex.data != string(got) {
+ t.Fatalf("fail on %q: unexpected contents of %q: len=%d; want=%d", file, e.Name, len(got), len(ex.data))
+ }
+ delete(extraMap, e.Name)
+ return nil
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ got := make([]byte, len(want))
+ n, err := f.ReadAt(got, int64(offset))
+ if err != nil {
+ t.Fatalf("ReadAt(len %d, offset %d, size %d) = %v, %v", len(got), offset, f.Size(), n, err)
+ }
+ if string(got) != want {
+ t.Fatalf("ReadAt(len %d, offset %d) = %q, want %q", len(got), offset, viewContent(got), viewContent([]byte(want)))
+ }
+ if len(extraMap) != 0 {
+ var exNames []string
+ for _, ex := range extraMap {
+ exNames = append(exNames, ex.name)
+ }
+ t.Fatalf("fail on %q: some entries aren't read: %+v", file, exNames)
+ }
+ })
+}
+
+func hasFileContentsRange(file string, offset int, want string) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ f, err := r.OpenFile(file)
+ if err != nil {
+ t.Fatal(err)
+ }
+ got := make([]byte, len(want))
+ n, err := f.ReadAt(got, int64(offset))
+ if err != nil {
+ t.Fatalf("ReadAt(len %d, offset %d) = %v, %v", len(got), offset, n, err)
+ }
+ if string(got) != want {
+ t.Fatalf("ReadAt(len %d, offset %d) = %q, want %q", len(got), offset, viewContent(got), viewContent([]byte(want)))
+ }
+ })
+}
+
+func hasChunkEntries(file string, wantChunks int) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ ent, ok := r.Lookup(file)
+ if !ok {
+ t.Fatalf("no file for %q", file)
+ }
+ if ent.Type != "reg" {
+ t.Fatalf("file %q has unexpected type %q; want reg", file, ent.Type)
+ }
+ chunks := r.getChunks(ent)
+ if len(chunks) != wantChunks {
+ t.Errorf("len(r.getChunks(%q)) = %d; want %d", file, len(chunks), wantChunks)
+ return
+ }
+ f := chunks[0]
+
+ var gotChunks []*TOCEntry
+ var last *TOCEntry
+ for off := int64(0); off < f.Size; off++ {
+ e, ok := r.ChunkEntryForOffset(file, off)
+ if !ok {
+ t.Errorf("no ChunkEntryForOffset at %d", off)
+ return
+ }
+ if last != e {
+ gotChunks = append(gotChunks, e)
+ last = e
+ }
+ }
+ if !reflect.DeepEqual(chunks, gotChunks) {
+ t.Errorf("gotChunks=%d, want=%d; contents mismatch", len(gotChunks), wantChunks)
+ }
+
+ // And verify the NextOffset
+ for i := 0; i < len(gotChunks)-1; i++ {
+ ci := gotChunks[i]
+ cnext := gotChunks[i+1]
+ if ci.NextOffset() != cnext.Offset {
+ t.Errorf("chunk %d NextOffset %d != next chunk's Offset of %d", i, ci.NextOffset(), cnext.Offset)
+ }
+ }
+ })
+}
+
+func entryHasChildren(dir string, want ...string) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ want := append([]string(nil), want...)
+ var got []string
+ ent, ok := r.Lookup(dir)
+ if !ok {
+ t.Fatalf("didn't find TOCEntry for dir node %q", dir)
+ }
+ for baseName := range ent.children {
+ got = append(got, baseName)
+ }
+ sort.Strings(got)
+ sort.Strings(want)
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("children of %q = %q; want %q", dir, got, want)
+ }
+ })
+}
+
+func hasDir(file string) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ for _, ent := range r.toc.Entries {
+ if ent.Name == cleanEntryName(file) {
+ if ent.Type != "dir" {
+ t.Errorf("file type of %q is %q; want \"dir\"", file, ent.Type)
+ }
+ return
+ }
+ }
+ t.Errorf("directory %q not found", file)
+ })
+}
+
+func hasDirLinkCount(file string, count int) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ for _, ent := range r.toc.Entries {
+ if ent.Name == cleanEntryName(file) {
+ if ent.Type != "dir" {
+ t.Errorf("file type of %q is %q; want \"dir\"", file, ent.Type)
+ return
+ }
+ if ent.NumLink != count {
+ t.Errorf("link count of %q = %d; want %d", file, ent.NumLink, count)
+ }
+ return
+ }
+ }
+ t.Errorf("directory %q not found", file)
+ })
+}
+
+func hasMode(file string, mode os.FileMode) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ for _, ent := range r.toc.Entries {
+ if ent.Name == cleanEntryName(file) {
+ if ent.Stat().Mode() != mode {
+ t.Errorf("invalid mode: got %v; want %v", ent.Stat().Mode(), mode)
+ return
+ }
+ return
+ }
+ }
+ t.Errorf("file %q not found", file)
+ })
+}
+
+func hasSymlink(file, target string) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ for _, ent := range r.toc.Entries {
+ if ent.Name == file {
+ if ent.Type != "symlink" {
+ t.Errorf("file type of %q is %q; want \"symlink\"", file, ent.Type)
+ } else if ent.LinkName != target {
+ t.Errorf("link target of symlink %q is %q; want %q", file, ent.LinkName, target)
+ }
+ return
+ }
+ }
+ t.Errorf("symlink %q not found", file)
+ })
+}
+
+func lookupMatch(name string, want *TOCEntry) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ e, ok := r.Lookup(name)
+ if !ok {
+ t.Fatalf("failed to Lookup entry %q", name)
+ }
+ if !reflect.DeepEqual(e, want) {
+ t.Errorf("entry %q mismatch.\n got: %+v\nwant: %+v\n", name, e, want)
+ }
+
+ })
+}
+
+func hasEntryOwner(entry string, owner owner) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ ent, ok := r.Lookup(strings.TrimSuffix(entry, "/"))
+ if !ok {
+ t.Errorf("entry %q not found", entry)
+ return
+ }
+ if ent.UID != owner.uid || ent.GID != owner.gid {
+ t.Errorf("entry %q has invalid owner (uid:%d, gid:%d) instead of (uid:%d, gid:%d)", entry, ent.UID, ent.GID, owner.uid, owner.gid)
+ return
+ }
+ })
+}
+
+func mustSameEntry(files ...string) stargzCheck {
+ return stargzCheckFn(func(t *testing.T, r *Reader) {
+ var first *TOCEntry
+ for _, f := range files {
+ if first == nil {
+ var ok bool
+ first, ok = r.Lookup(f)
+ if !ok {
+ t.Errorf("unknown first file on Lookup: %q", f)
+ return
+ }
+ }
+
+ // Test Lookup
+ e, ok := r.Lookup(f)
+ if !ok {
+ t.Errorf("unknown file on Lookup: %q", f)
+ return
+ }
+ if e != first {
+ t.Errorf("Lookup: %+v(%p) != %+v(%p)", e, e, first, first)
+ return
+ }
+
+ // Test LookupChild
+ pe, ok := r.Lookup(filepath.Dir(filepath.Clean(f)))
+ if !ok {
+ t.Errorf("failed to get parent of %q", f)
+ return
+ }
+ e, ok = pe.LookupChild(filepath.Base(filepath.Clean(f)))
+ if !ok {
+ t.Errorf("failed to get %q as the child of %+v", f, pe)
+ return
+ }
+ if e != first {
+ t.Errorf("LookupChild: %+v(%p) != %+v(%p)", e, e, first, first)
+ return
+ }
+
+ // Test ForeachChild
+ pe.ForeachChild(func(baseName string, e *TOCEntry) bool {
+ if baseName == filepath.Base(filepath.Clean(f)) {
+ if e != first {
+ t.Errorf("ForeachChild: %+v(%p) != %+v(%p)", e, e, first, first)
+ return false
+ }
+ }
+ return true
+ })
+ }
+ })
+}
+
+func viewContent(c []byte) string {
+ if len(c) < 100 {
+ return string(c)
+ }
+ return string(c[:50]) + "...(omit)..." + string(c[50:100])
+}
+
+func tarOf(s ...tarEntry) []tarEntry { return s }
+
+type tarEntry interface {
+ appendTar(tw *tar.Writer, prefix string, format tar.Format) error
+}
+
+type tarEntryFunc func(*tar.Writer, string, tar.Format) error
+
+func (f tarEntryFunc) appendTar(tw *tar.Writer, prefix string, format tar.Format) error {
+ return f(tw, prefix, format)
+}
+
+func buildTar(t *testing.T, ents []tarEntry, prefix string, opts ...interface{}) *io.SectionReader {
+ format := tar.FormatUnknown
+ for _, opt := range opts {
+ switch v := opt.(type) {
+ case tar.Format:
+ format = v
+ default:
+ panic(fmt.Errorf("unsupported opt for buildTar: %v", opt))
+ }
+ }
+ buf := new(bytes.Buffer)
+ tw := tar.NewWriter(buf)
+ for _, ent := range ents {
+ if err := ent.appendTar(tw, prefix, format); err != nil {
+ t.Fatalf("building input tar: %v", err)
+ }
+ }
+ if err := tw.Close(); err != nil {
+ t.Errorf("closing write of input tar: %v", err)
+ }
+ data := append(buf.Bytes(), make([]byte, 100)...) // append empty bytes at the tail to see lossless works
+ return io.NewSectionReader(bytes.NewReader(data), 0, int64(len(data)))
+}
+
+func dir(name string, opts ...interface{}) tarEntry {
+ return tarEntryFunc(func(tw *tar.Writer, prefix string, format tar.Format) error {
+ var o owner
+ mode := os.FileMode(0755)
+ for _, opt := range opts {
+ switch v := opt.(type) {
+ case owner:
+ o = v
+ case os.FileMode:
+ mode = v
+ default:
+ return errors.New("unsupported opt")
+ }
+ }
+ if !strings.HasSuffix(name, "/") {
+ panic(fmt.Sprintf("missing trailing slash in dir %q ", name))
+ }
+ tm, err := fileModeToTarMode(mode)
+ if err != nil {
+ return err
+ }
+ return tw.WriteHeader(&tar.Header{
+ Typeflag: tar.TypeDir,
+ Name: prefix + name,
+ Mode: tm,
+ Uid: o.uid,
+ Gid: o.gid,
+ Format: format,
+ })
+ })
+}
+
+// xAttr are extended attributes to set on test files created with the file func.
+type xAttr map[string]string
+
+// owner is owner ot set on test files and directories with the file and dir functions.
+type owner struct {
+ uid int
+ gid int
+}
+
+func file(name, contents string, opts ...interface{}) tarEntry {
+ return tarEntryFunc(func(tw *tar.Writer, prefix string, format tar.Format) error {
+ var xattrs xAttr
+ var o owner
+ mode := os.FileMode(0644)
+ for _, opt := range opts {
+ switch v := opt.(type) {
+ case xAttr:
+ xattrs = v
+ case owner:
+ o = v
+ case os.FileMode:
+ mode = v
+ default:
+ return errors.New("unsupported opt")
+ }
+ }
+ if strings.HasSuffix(name, "/") {
+ return fmt.Errorf("bogus trailing slash in file %q", name)
+ }
+ tm, err := fileModeToTarMode(mode)
+ if err != nil {
+ return err
+ }
+ if len(xattrs) > 0 {
+ format = tar.FormatPAX // only PAX supports xattrs
+ }
+ if err := tw.WriteHeader(&tar.Header{
+ Typeflag: tar.TypeReg,
+ Name: prefix + name,
+ Mode: tm,
+ Xattrs: xattrs,
+ Size: int64(len(contents)),
+ Uid: o.uid,
+ Gid: o.gid,
+ Format: format,
+ }); err != nil {
+ return err
+ }
+ _, err = io.WriteString(tw, contents)
+ return err
+ })
+}
+
+func symlink(name, target string) tarEntry {
+ return tarEntryFunc(func(tw *tar.Writer, prefix string, format tar.Format) error {
+ return tw.WriteHeader(&tar.Header{
+ Typeflag: tar.TypeSymlink,
+ Name: prefix + name,
+ Linkname: target,
+ Mode: 0644,
+ Format: format,
+ })
+ })
+}
+
+func link(name string, linkname string) tarEntry {
+ now := time.Now()
+ return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error {
+ return w.WriteHeader(&tar.Header{
+ Typeflag: tar.TypeLink,
+ Name: prefix + name,
+ Linkname: linkname,
+ ModTime: now,
+ Format: format,
+ })
+ })
+}
+
+func chardev(name string, major, minor int64) tarEntry {
+ now := time.Now()
+ return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error {
+ return w.WriteHeader(&tar.Header{
+ Typeflag: tar.TypeChar,
+ Name: prefix + name,
+ Devmajor: major,
+ Devminor: minor,
+ ModTime: now,
+ Format: format,
+ })
+ })
+}
+
+func blockdev(name string, major, minor int64) tarEntry {
+ now := time.Now()
+ return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error {
+ return w.WriteHeader(&tar.Header{
+ Typeflag: tar.TypeBlock,
+ Name: prefix + name,
+ Devmajor: major,
+ Devminor: minor,
+ ModTime: now,
+ Format: format,
+ })
+ })
+}
+func fifo(name string) tarEntry {
+ now := time.Now()
+ return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error {
+ return w.WriteHeader(&tar.Header{
+ Typeflag: tar.TypeFifo,
+ Name: prefix + name,
+ ModTime: now,
+ Format: format,
+ })
+ })
+}
+
+func prefetchLandmark() tarEntry {
+ return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error {
+ if err := w.WriteHeader(&tar.Header{
+ Name: PrefetchLandmark,
+ Typeflag: tar.TypeReg,
+ Size: int64(len([]byte{landmarkContents})),
+ Format: format,
+ }); err != nil {
+ return err
+ }
+ contents := []byte{landmarkContents}
+ if _, err := io.CopyN(w, bytes.NewReader(contents), int64(len(contents))); err != nil {
+ return err
+ }
+ return nil
+ })
+}
+
+func noPrefetchLandmark() tarEntry {
+ return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error {
+ if err := w.WriteHeader(&tar.Header{
+ Name: NoPrefetchLandmark,
+ Typeflag: tar.TypeReg,
+ Size: int64(len([]byte{landmarkContents})),
+ Format: format,
+ }); err != nil {
+ return err
+ }
+ contents := []byte{landmarkContents}
+ if _, err := io.CopyN(w, bytes.NewReader(contents), int64(len(contents))); err != nil {
+ return err
+ }
+ return nil
+ })
+}
+
+func regDigest(t *testing.T, name string, contentStr string, digestMap map[string]digest.Digest) tarEntry {
+ if digestMap == nil {
+ t.Fatalf("digest map mustn't be nil")
+ }
+ content := []byte(contentStr)
+
+ var n int64
+ for n < int64(len(content)) {
+ size := int64(chunkSize)
+ remain := int64(len(content)) - n
+ if remain < size {
+ size = remain
+ }
+ dgstr := digest.Canonical.Digester()
+ if _, err := io.CopyN(dgstr.Hash(), bytes.NewReader(content[n:n+size]), size); err != nil {
+ t.Fatalf("failed to calculate digest of %q (name=%q,offset=%d,size=%d)",
+ string(content[n:n+size]), name, n, size)
+ }
+ digestMap[chunkID(name, n, size)] = dgstr.Digest()
+ n += size
+ }
+
+ return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error {
+ if err := w.WriteHeader(&tar.Header{
+ Typeflag: tar.TypeReg,
+ Name: prefix + name,
+ Size: int64(len(content)),
+ Format: format,
+ }); err != nil {
+ return err
+ }
+ if _, err := io.CopyN(w, bytes.NewReader(content), int64(len(content))); err != nil {
+ return err
+ }
+ return nil
+ })
+}
+
+var runes = []rune("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+
+func randomContents(n int) string {
+ b := make([]rune, n)
+ for i := range b {
+ bi, err := rand.Int(rand.Reader, big.NewInt(int64(len(runes))))
+ if err != nil {
+ panic(err)
+ }
+ b[i] = runes[int(bi.Int64())]
+ }
+ return string(b)
+}
+
+func fileModeToTarMode(mode os.FileMode) (int64, error) {
+ h, err := tar.FileInfoHeader(fileInfoOnlyMode(mode), "")
+ if err != nil {
+ return 0, err
+ }
+ return h.Mode, nil
+}
+
+// fileInfoOnlyMode is os.FileMode that populates only file mode.
+type fileInfoOnlyMode os.FileMode
+
+func (f fileInfoOnlyMode) Name() string { return "" }
+func (f fileInfoOnlyMode) Size() int64 { return 0 }
+func (f fileInfoOnlyMode) Mode() os.FileMode { return os.FileMode(f) }
+func (f fileInfoOnlyMode) ModTime() time.Time { return time.Now() }
+func (f fileInfoOnlyMode) IsDir() bool { return os.FileMode(f).IsDir() }
+func (f fileInfoOnlyMode) Sys() interface{} { return nil }
+
+func CheckGzipHasStreams(t *testing.T, b []byte, streams []int64) {
+ if len(streams) == 0 {
+ return // nop
+ }
+
+ wants := map[int64]struct{}{}
+ for _, s := range streams {
+ wants[s] = struct{}{}
+ }
+
+ len0 := len(b)
+ br := bytes.NewReader(b)
+ zr := new(gzip.Reader)
+ t.Logf("got gzip streams:")
+ numStreams := 0
+ for {
+ zoff := len0 - br.Len()
+ if err := zr.Reset(br); err != nil {
+ if err == io.EOF {
+ return
+ }
+ t.Fatalf("countStreams(gzip), Reset: %v", err)
+ }
+ zr.Multistream(false)
+ n, err := io.Copy(io.Discard, zr)
+ if err != nil {
+ t.Fatalf("countStreams(gzip), Copy: %v", err)
+ }
+ var extra string
+ if len(zr.Extra) > 0 {
+ extra = fmt.Sprintf("; extra=%q", zr.Extra)
+ }
+ t.Logf(" [%d] at %d in stargz, uncompressed length %d%s", numStreams, zoff, n, extra)
+ delete(wants, int64(zoff))
+ numStreams++
+ }
+}
+
+func GzipDiffIDOf(t *testing.T, b []byte) string {
+ h := sha256.New()
+ zr, err := gzip.NewReader(bytes.NewReader(b))
+ if err != nil {
+ t.Fatalf("diffIDOf(gzip): %v", err)
+ }
+ defer zr.Close()
+ if _, err := io.Copy(h, zr); err != nil {
+ t.Fatalf("diffIDOf(gzip).Copy: %v", err)
+ }
+ return fmt.Sprintf("sha256:%x", h.Sum(nil))
+}
diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go
new file mode 100644
index 00000000..57e0aa61
--- /dev/null
+++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go
@@ -0,0 +1,342 @@
+/*
+ Copyright The containerd Authors.
+
+ 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.
+*/
+
+/*
+ Copyright 2019 The Go Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file.
+*/
+
+package estargz
+
+import (
+ "archive/tar"
+ "hash"
+ "io"
+ "os"
+ "path"
+ "time"
+
+ digest "github.com/opencontainers/go-digest"
+)
+
+const (
+ // TOCTarName is the name of the JSON file in the tar archive in the
+ // table of contents gzip stream.
+ TOCTarName = "stargz.index.json"
+
+ // FooterSize is the number of bytes in the footer
+ //
+ // The footer is an empty gzip stream with no compression and an Extra
+ // header of the form "%016xSTARGZ", where the 64 bit hex-encoded
+ // number is the offset to the gzip stream of JSON TOC.
+ //
+ // 51 comes from:
+ //
+ // 10 bytes gzip header
+ // 2 bytes XLEN (length of Extra field) = 26 (4 bytes header + 16 hex digits + len("STARGZ"))
+ // 2 bytes Extra: SI1 = 'S', SI2 = 'G'
+ // 2 bytes Extra: LEN = 22 (16 hex digits + len("STARGZ"))
+ // 22 bytes Extra: subfield = fmt.Sprintf("%016xSTARGZ", offsetOfTOC)
+ // 5 bytes flate header
+ // 8 bytes gzip footer
+ // (End of the eStargz blob)
+ //
+ // NOTE: For Extra fields, subfield IDs SI1='S' SI2='G' is used for eStargz.
+ FooterSize = 51
+
+ // legacyFooterSize is the number of bytes in the legacy stargz footer.
+ //
+ // 47 comes from:
+ //
+ // 10 byte gzip header +
+ // 2 byte (LE16) length of extra, encoding 22 (16 hex digits + len("STARGZ")) == "\x16\x00" +
+ // 22 bytes of extra (fmt.Sprintf("%016xSTARGZ", tocGzipOffset))
+ // 5 byte flate header
+ // 8 byte gzip footer (two little endian uint32s: digest, size)
+ legacyFooterSize = 47
+
+ // TOCJSONDigestAnnotation is an annotation for an image layer. This stores the
+ // digest of the TOC JSON.
+ // This annotation is valid only when it is specified in `.[]layers.annotations`
+ // of an image manifest.
+ TOCJSONDigestAnnotation = "containerd.io/snapshot/stargz/toc.digest"
+
+ // StoreUncompressedSizeAnnotation is an additional annotation key for eStargz to enable lazy
+ // pulling on containers/storage. Stargz Store is required to expose the layer's uncompressed size
+ // to the runtime but current OCI image doesn't ship this information by default. So we store this
+ // to the special annotation.
+ StoreUncompressedSizeAnnotation = "io.containers.estargz.uncompressed-size"
+
+ // PrefetchLandmark is a file entry which indicates the end position of
+ // prefetch in the stargz file.
+ PrefetchLandmark = ".prefetch.landmark"
+
+ // NoPrefetchLandmark is a file entry which indicates that no prefetch should
+ // occur in the stargz file.
+ NoPrefetchLandmark = ".no.prefetch.landmark"
+
+ landmarkContents = 0xf
+)
+
+// JTOC is the JSON-serialized table of contents index of the files in the stargz file.
+type JTOC struct {
+ Version int `json:"version"`
+ Entries []*TOCEntry `json:"entries"`
+}
+
+// TOCEntry is an entry in the stargz file's TOC (Table of Contents).
+type TOCEntry struct {
+ // Name is the tar entry's name. It is the complete path
+ // stored in the tar file, not just the base name.
+ Name string `json:"name"`
+
+ // Type is one of "dir", "reg", "symlink", "hardlink", "char",
+ // "block", "fifo", or "chunk".
+ // The "chunk" type is used for regular file data chunks past the first
+ // TOCEntry; the 2nd chunk and on have only Type ("chunk"), Offset,
+ // ChunkOffset, and ChunkSize populated.
+ Type string `json:"type"`
+
+ // Size, for regular files, is the logical size of the file.
+ Size int64 `json:"size,omitempty"`
+
+ // ModTime3339 is the modification time of the tar entry. Empty
+ // means zero or unknown. Otherwise it's in UTC RFC3339
+ // format. Use the ModTime method to access the time.Time value.
+ ModTime3339 string `json:"modtime,omitempty"`
+ modTime time.Time
+
+ // LinkName, for symlinks and hardlinks, is the link target.
+ LinkName string `json:"linkName,omitempty"`
+
+ // Mode is the permission and mode bits.
+ Mode int64 `json:"mode,omitempty"`
+
+ // UID is the user ID of the owner.
+ UID int `json:"uid,omitempty"`
+
+ // GID is the group ID of the owner.
+ GID int `json:"gid,omitempty"`
+
+ // Uname is the username of the owner.
+ //
+ // In the serialized JSON, this field may only be present for
+ // the first entry with the same UID.
+ Uname string `json:"userName,omitempty"`
+
+ // Gname is the group name of the owner.
+ //
+ // In the serialized JSON, this field may only be present for
+ // the first entry with the same GID.
+ Gname string `json:"groupName,omitempty"`
+
+ // Offset, for regular files, provides the offset in the
+ // stargz file to the file's data bytes. See ChunkOffset and
+ // ChunkSize.
+ Offset int64 `json:"offset,omitempty"`
+
+ // InnerOffset is an optional field indicates uncompressed offset
+ // of this "reg" or "chunk" payload in a stream starts from Offset.
+ // This field enables to put multiple "reg" or "chunk" payloads
+ // in one chunk with having the same Offset but different InnerOffset.
+ InnerOffset int64 `json:"innerOffset,omitempty"`
+
+ nextOffset int64 // the Offset of the next entry with a non-zero Offset
+
+ // DevMajor is the major device number for "char" and "block" types.
+ DevMajor int `json:"devMajor,omitempty"`
+
+ // DevMinor is the major device number for "char" and "block" types.
+ DevMinor int `json:"devMinor,omitempty"`
+
+ // NumLink is the number of entry names pointing to this entry.
+ // Zero means one name references this entry.
+ // This field is calculated during runtime and not recorded in TOC JSON.
+ NumLink int `json:"-"`
+
+ // Xattrs are the extended attribute for the entry.
+ Xattrs map[string][]byte `json:"xattrs,omitempty"`
+
+ // Digest stores the OCI checksum for regular files payload.
+ // It has the form "sha256:abcdef01234....".
+ Digest string `json:"digest,omitempty"`
+
+ // ChunkOffset is non-zero if this is a chunk of a large,
+ // regular file. If so, the Offset is where the gzip header of
+ // ChunkSize bytes at ChunkOffset in Name begin.
+ //
+ // In serialized form, a "chunkSize" JSON field of zero means
+ // that the chunk goes to the end of the file. After reading
+ // from the stargz TOC, though, the ChunkSize is initialized
+ // to a non-zero file for when Type is either "reg" or
+ // "chunk".
+ ChunkOffset int64 `json:"chunkOffset,omitempty"`
+ ChunkSize int64 `json:"chunkSize,omitempty"`
+
+ // ChunkDigest stores an OCI digest of the chunk. This must be formed
+ // as "sha256:0123abcd...".
+ ChunkDigest string `json:"chunkDigest,omitempty"`
+
+ children map[string]*TOCEntry
+
+ // chunkTopIndex is index of the entry where Offset starts in the blob.
+ chunkTopIndex int
+}
+
+// ModTime returns the entry's modification time.
+func (e *TOCEntry) ModTime() time.Time { return e.modTime }
+
+// NextOffset returns the position (relative to the start of the
+// stargz file) of the next gzip boundary after e.Offset.
+func (e *TOCEntry) NextOffset() int64 { return e.nextOffset }
+
+func (e *TOCEntry) addChild(baseName string, child *TOCEntry) {
+ if e.children == nil {
+ e.children = make(map[string]*TOCEntry)
+ }
+ if child.Type == "dir" {
+ e.NumLink++ // Entry ".." in the subdirectory links to this directory
+ }
+ e.children[baseName] = child
+}
+
+// isDataType reports whether TOCEntry is a regular file or chunk (something that
+// contains regular file data).
+func (e *TOCEntry) isDataType() bool { return e.Type == "reg" || e.Type == "chunk" }
+
+// Stat returns a FileInfo value representing e.
+func (e *TOCEntry) Stat() os.FileInfo { return fileInfo{e} }
+
+// ForeachChild calls f for each child item. If f returns false, iteration ends.
+// If e is not a directory, f is not called.
+func (e *TOCEntry) ForeachChild(f func(baseName string, ent *TOCEntry) bool) {
+ for name, ent := range e.children {
+ if !f(name, ent) {
+ return
+ }
+ }
+}
+
+// LookupChild returns the directory e's child by its base name.
+func (e *TOCEntry) LookupChild(baseName string) (child *TOCEntry, ok bool) {
+ child, ok = e.children[baseName]
+ return
+}
+
+// fileInfo implements os.FileInfo using the wrapped *TOCEntry.
+type fileInfo struct{ e *TOCEntry }
+
+var _ os.FileInfo = fileInfo{}
+
+func (fi fileInfo) Name() string { return path.Base(fi.e.Name) }
+func (fi fileInfo) IsDir() bool { return fi.e.Type == "dir" }
+func (fi fileInfo) Size() int64 { return fi.e.Size }
+func (fi fileInfo) ModTime() time.Time { return fi.e.ModTime() }
+func (fi fileInfo) Sys() interface{} { return fi.e }
+func (fi fileInfo) Mode() (m os.FileMode) {
+ // TOCEntry.Mode is tar.Header.Mode so we can understand the these bits using `tar` pkg.
+ m = (&tar.Header{Mode: fi.e.Mode}).FileInfo().Mode() &
+ (os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky)
+ switch fi.e.Type {
+ case "dir":
+ m |= os.ModeDir
+ case "symlink":
+ m |= os.ModeSymlink
+ case "char":
+ m |= os.ModeDevice | os.ModeCharDevice
+ case "block":
+ m |= os.ModeDevice
+ case "fifo":
+ m |= os.ModeNamedPipe
+ }
+ return m
+}
+
+// TOCEntryVerifier holds verifiers that are usable for verifying chunks contained
+// in a eStargz blob.
+type TOCEntryVerifier interface {
+
+ // Verifier provides a content verifier that can be used for verifying the
+ // contents of the specified TOCEntry.
+ Verifier(ce *TOCEntry) (digest.Verifier, error)
+}
+
+// Compression provides the compression helper to be used creating and parsing eStargz.
+// This package provides gzip-based Compression by default, but any compression
+// algorithm (e.g. zstd) can be used as long as it implements Compression.
+type Compression interface {
+ Compressor
+ Decompressor
+}
+
+// Compressor represents the helper mothods to be used for creating eStargz.
+type Compressor interface {
+ // Writer returns WriteCloser to be used for writing a chunk to eStargz.
+ // Everytime a chunk is written, the WriteCloser is closed and Writer is
+ // called again for writing the next chunk.
+ //
+ // The returned writer should implement "Flush() error" function that flushes
+ // any pending compressed data to the underlying writer.
+ Writer(w io.Writer) (WriteFlushCloser, error)
+
+ // WriteTOCAndFooter is called to write JTOC to the passed Writer.
+ // diffHash calculates the DiffID (uncompressed sha256 hash) of the blob
+ // WriteTOCAndFooter can optionally write anything that affects DiffID calculation
+ // (e.g. uncompressed TOC JSON).
+ //
+ // This function returns tocDgst that represents the digest of TOC that will be used
+ // to verify this blob when it's parsed.
+ WriteTOCAndFooter(w io.Writer, off int64, toc *JTOC, diffHash hash.Hash) (tocDgst digest.Digest, err error)
+}
+
+// Decompressor represents the helper mothods to be used for parsing eStargz.
+type Decompressor interface {
+ // Reader returns ReadCloser to be used for decompressing file payload.
+ Reader(r io.Reader) (io.ReadCloser, error)
+
+ // FooterSize returns the size of the footer of this blob.
+ FooterSize() int64
+
+ // ParseFooter parses the footer and returns the offset and (compressed) size of TOC.
+ // payloadBlobSize is the (compressed) size of the blob payload (i.e. the size between
+ // the top until the TOC JSON).
+ //
+ // If tocOffset < 0, we assume that TOC isn't contained in the blob and pass nil reader
+ // to ParseTOC. We expect that ParseTOC acquire TOC from the external location and return it.
+ //
+ // tocSize is optional. If tocSize <= 0, it's by default the size of the range from tocOffset until the beginning of the
+ // footer (blob size - tocOff - FooterSize).
+ // If blobPayloadSize < 0, blobPayloadSize become the blob size.
+ ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error)
+
+ // ParseTOC parses TOC from the passed reader. The reader provides the partial contents
+ // of the underlying blob that has the range specified by ParseFooter method.
+ //
+ // This function returns tocDgst that represents the digest of TOC that will be used
+ // to verify this blob. This must match to the value returned from
+ // Compressor.WriteTOCAndFooter that is used when creating this blob.
+ //
+ // If tocOffset returned by ParseFooter is < 0, we assume that TOC isn't contained in the blob.
+ // Pass nil reader to ParseTOC then we expect that ParseTOC acquire TOC from the external location
+ // and return it.
+ ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error)
+}
+
+type WriteFlushCloser interface {
+ io.WriteCloser
+ Flush() error
+}
diff --git a/vendor/github.com/containerd/typeurl/v2/.gitignore b/vendor/github.com/containerd/typeurl/v2/.gitignore
new file mode 100644
index 00000000..d5384677
--- /dev/null
+++ b/vendor/github.com/containerd/typeurl/v2/.gitignore
@@ -0,0 +1,2 @@
+*.test
+coverage.txt
diff --git a/vendor/github.com/containerd/typeurl/v2/LICENSE b/vendor/github.com/containerd/typeurl/v2/LICENSE
new file mode 100644
index 00000000..584149b6
--- /dev/null
+++ b/vendor/github.com/containerd/typeurl/v2/LICENSE
@@ -0,0 +1,191 @@
+
+ Apache License
+ Version 2.0, January 2004
+ https://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ Copyright The containerd Authors
+
+ 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
+
+ https://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.
diff --git a/vendor/github.com/containerd/typeurl/v2/README.md b/vendor/github.com/containerd/typeurl/v2/README.md
new file mode 100644
index 00000000..3098526a
--- /dev/null
+++ b/vendor/github.com/containerd/typeurl/v2/README.md
@@ -0,0 +1,26 @@
+# typeurl
+
+[](https://pkg.go.dev/github.com/containerd/typeurl)
+[](https://github.com/containerd/typeurl/actions?query=workflow%3ACI)
+[](https://codecov.io/gh/containerd/typeurl)
+[](https://goreportcard.com/report/github.com/containerd/typeurl)
+
+A Go package for managing the registration, marshaling, and unmarshaling of encoded types.
+
+This package helps when types are sent over a ttrpc/GRPC API and marshaled as a protobuf [Any](https://pkg.go.dev/google.golang.org/protobuf@v1.27.1/types/known/anypb#Any)
+
+## Project details
+
+**typeurl** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
+As a containerd sub-project, you will find the:
+ * [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
+ * [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
+ * and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
+
+information in our [`containerd/project`](https://github.com/containerd/project) repository.
+
+## Optional
+
+By default, support for gogoproto is available along side the standard Google
+protobuf types.
+You can choose to leave gogo support out by using the `!no_gogo` build tag.
diff --git a/vendor/github.com/containerd/typeurl/v2/doc.go b/vendor/github.com/containerd/typeurl/v2/doc.go
new file mode 100644
index 00000000..c0d0fd20
--- /dev/null
+++ b/vendor/github.com/containerd/typeurl/v2/doc.go
@@ -0,0 +1,83 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 typeurl
+
+// Package typeurl assists with managing the registration, marshaling, and
+// unmarshaling of types encoded as protobuf.Any.
+//
+// A protobuf.Any is a proto message that can contain any arbitrary data. It
+// consists of two components, a TypeUrl and a Value, and its proto definition
+// looks like this:
+//
+// message Any {
+// string type_url = 1;
+// bytes value = 2;
+// }
+//
+// The TypeUrl is used to distinguish the contents from other proto.Any
+// messages. This typeurl library manages these URLs to enable automagic
+// marshaling and unmarshaling of the contents.
+//
+// For example, consider this go struct:
+//
+// type Foo struct {
+// Field1 string
+// Field2 string
+// }
+//
+// To use typeurl, types must first be registered. This is typically done in
+// the init function
+//
+// func init() {
+// typeurl.Register(&Foo{}, "Foo")
+// }
+//
+// This will register the type Foo with the url path "Foo". The arguments to
+// Register are variadic, and are used to construct a url path. Consider this
+// example, from the github.com/containerd/containerd/client package:
+//
+// func init() {
+// const prefix = "types.containerd.io"
+// // register TypeUrls for commonly marshaled external types
+// major := strconv.Itoa(specs.VersionMajor)
+// typeurl.Register(&specs.Spec{}, prefix, "opencontainers/runtime-spec", major, "Spec")
+// // this function has more Register calls, which are elided.
+// }
+//
+// This registers several types under a more complex url, which ends up mapping
+// to `types.containerd.io/opencontainers/runtime-spec/1/Spec` (or some other
+// value for major).
+//
+// Once a type is registered, it can be marshaled to a proto.Any message simply
+// by calling `MarshalAny`, like this:
+//
+// foo := &Foo{Field1: "value1", Field2: "value2"}
+// anyFoo, err := typeurl.MarshalAny(foo)
+//
+// MarshalAny will resolve the correct URL for the type. If the type in
+// question implements the proto.Message interface, then it will be marshaled
+// as a proto message. Otherwise, it will be marshaled as json. This means that
+// typeurl will work on any arbitrary data, whether or not it has a proto
+// definition, as long as it can be serialized to json.
+//
+// To unmarshal, the process is simply inverse:
+//
+// iface, err := typeurl.UnmarshalAny(anyFoo)
+// foo := iface.(*Foo)
+//
+// The correct type is automatically chosen from the type registry, and the
+// returned interface can be cast straight to that type.
diff --git a/vendor/github.com/containerd/typeurl/v2/types.go b/vendor/github.com/containerd/typeurl/v2/types.go
new file mode 100644
index 00000000..9bf78104
--- /dev/null
+++ b/vendor/github.com/containerd/typeurl/v2/types.go
@@ -0,0 +1,309 @@
+/*
+ Copyright The containerd Authors.
+
+ 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 typeurl
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "path"
+ "reflect"
+ "sync"
+
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/reflect/protoregistry"
+ "google.golang.org/protobuf/types/known/anypb"
+)
+
+var (
+ mu sync.RWMutex
+ registry = make(map[reflect.Type]string)
+ handlers []handler
+)
+
+type handler interface {
+ Marshaller(interface{}) func() ([]byte, error)
+ Unmarshaller(interface{}) func([]byte) error
+ TypeURL(interface{}) string
+ GetType(url string) (reflect.Type, bool)
+}
+
+// Definitions of common error types used throughout typeurl.
+//
+// These error types are used with errors.Wrap and errors.Wrapf to add context
+// to an error.
+//
+// To detect an error class, use errors.Is() functions to tell whether an
+// error is of this type.
+
+var (
+ ErrNotFound = errors.New("not found")
+)
+
+// Any contains an arbitrary protcol buffer message along with its type.
+//
+// While there is google.golang.org/protobuf/types/known/anypb.Any,
+// we'd like to have our own to hide the underlying protocol buffer
+// implementations from containerd clients.
+//
+// https://developers.google.com/protocol-buffers/docs/proto3#any
+type Any interface {
+ // GetTypeUrl returns a URL/resource name that uniquely identifies
+ // the type of the serialized protocol buffer message.
+ GetTypeUrl() string
+
+ // GetValue returns a valid serialized protocol buffer of the type that
+ // GetTypeUrl() indicates.
+ GetValue() []byte
+}
+
+type anyType struct {
+ typeURL string
+ value []byte
+}
+
+func (a *anyType) GetTypeUrl() string {
+ if a == nil {
+ return ""
+ }
+ return a.typeURL
+}
+
+func (a *anyType) GetValue() []byte {
+ if a == nil {
+ return nil
+ }
+ return a.value
+}
+
+// Register a type with a base URL for JSON marshaling. When the MarshalAny and
+// UnmarshalAny functions are called they will treat the Any type value as JSON.
+// To use protocol buffers for handling the Any value the proto.Register
+// function should be used instead of this function.
+func Register(v interface{}, args ...string) {
+ var (
+ t = tryDereference(v)
+ p = path.Join(args...)
+ )
+ mu.Lock()
+ defer mu.Unlock()
+ if et, ok := registry[t]; ok {
+ if et != p {
+ panic(fmt.Errorf("type registered with alternate path %q != %q", et, p))
+ }
+ return
+ }
+ registry[t] = p
+}
+
+// TypeURL returns the type url for a registered type.
+func TypeURL(v interface{}) (string, error) {
+ mu.RLock()
+ u, ok := registry[tryDereference(v)]
+ mu.RUnlock()
+ if !ok {
+ switch t := v.(type) {
+ case proto.Message:
+ return string(t.ProtoReflect().Descriptor().FullName()), nil
+ default:
+ for _, h := range handlers {
+ if u := h.TypeURL(v); u != "" {
+ return u, nil
+ }
+ }
+ return "", fmt.Errorf("type %s: %w", reflect.TypeOf(v), ErrNotFound)
+ }
+ }
+ return u, nil
+}
+
+// Is returns true if the type of the Any is the same as v.
+func Is(any Any, v interface{}) bool {
+ if any == nil {
+ return false
+ }
+ // call to check that v is a pointer
+ tryDereference(v)
+ url, err := TypeURL(v)
+ if err != nil {
+ return false
+ }
+ return any.GetTypeUrl() == url
+}
+
+// MarshalAny marshals the value v into an any with the correct TypeUrl.
+// If the provided object is already a proto.Any message, then it will be
+// returned verbatim. If it is of type proto.Message, it will be marshaled as a
+// protocol buffer. Otherwise, the object will be marshaled to json.
+func MarshalAny(v interface{}) (Any, error) {
+ var marshal func(v interface{}) ([]byte, error)
+ switch t := v.(type) {
+ case Any:
+ // avoid reserializing the type if we have an any.
+ return t, nil
+ case proto.Message:
+ marshal = func(v interface{}) ([]byte, error) {
+ return proto.Marshal(t)
+ }
+ default:
+ for _, h := range handlers {
+ if m := h.Marshaller(v); m != nil {
+ marshal = func(v interface{}) ([]byte, error) {
+ return m()
+ }
+ break
+ }
+ }
+
+ if marshal == nil {
+ marshal = json.Marshal
+ }
+ }
+
+ url, err := TypeURL(v)
+ if err != nil {
+ return nil, err
+ }
+
+ data, err := marshal(v)
+ if err != nil {
+ return nil, err
+ }
+ return &anyType{
+ typeURL: url,
+ value: data,
+ }, nil
+}
+
+// UnmarshalAny unmarshals the any type into a concrete type.
+func UnmarshalAny(any Any) (interface{}, error) {
+ return UnmarshalByTypeURL(any.GetTypeUrl(), any.GetValue())
+}
+
+// UnmarshalByTypeURL unmarshals the given type and value to into a concrete type.
+func UnmarshalByTypeURL(typeURL string, value []byte) (interface{}, error) {
+ return unmarshal(typeURL, value, nil)
+}
+
+// UnmarshalTo unmarshals the any type into a concrete type passed in the out
+// argument. It is identical to UnmarshalAny, but lets clients provide a
+// destination type through the out argument.
+func UnmarshalTo(any Any, out interface{}) error {
+ return UnmarshalToByTypeURL(any.GetTypeUrl(), any.GetValue(), out)
+}
+
+// UnmarshalToByTypeURL unmarshals the given type and value into a concrete type passed
+// in the out argument. It is identical to UnmarshalByTypeURL, but lets clients
+// provide a destination type through the out argument.
+func UnmarshalToByTypeURL(typeURL string, value []byte, out interface{}) error {
+ _, err := unmarshal(typeURL, value, out)
+ return err
+}
+
+// MarshalProto converts typeurl.Any to google.golang.org/protobuf/types/known/anypb.Any.
+func MarshalProto(from Any) *anypb.Any {
+ if from == nil {
+ return nil
+ }
+
+ if pbany, ok := from.(*anypb.Any); ok {
+ return pbany
+ }
+
+ return &anypb.Any{
+ TypeUrl: from.GetTypeUrl(),
+ Value: from.GetValue(),
+ }
+}
+
+// MarshalAnyToProto converts an arbitrary interface to google.golang.org/protobuf/types/known/anypb.Any.
+func MarshalAnyToProto(from interface{}) (*anypb.Any, error) {
+ anyType, err := MarshalAny(from)
+ if err != nil {
+ return nil, err
+ }
+ return MarshalProto(anyType), nil
+}
+
+func unmarshal(typeURL string, value []byte, v interface{}) (interface{}, error) {
+ t, isProto, err := getTypeByUrl(typeURL)
+ if err != nil {
+ return nil, err
+ }
+
+ if v == nil {
+ v = reflect.New(t).Interface()
+ } else {
+ // Validate interface type provided by client
+ vURL, err := TypeURL(v)
+ if err != nil {
+ return nil, err
+ }
+ if typeURL != vURL {
+ return nil, fmt.Errorf("can't unmarshal type %q to output %q", typeURL, vURL)
+ }
+ }
+
+ if isProto {
+ pm, ok := v.(proto.Message)
+ if ok {
+ return v, proto.Unmarshal(value, pm)
+ }
+
+ for _, h := range handlers {
+ if unmarshal := h.Unmarshaller(v); unmarshal != nil {
+ return v, unmarshal(value)
+ }
+ }
+ }
+
+ // fallback to json unmarshaller
+ return v, json.Unmarshal(value, v)
+}
+
+func getTypeByUrl(url string) (_ reflect.Type, isProto bool, _ error) {
+ mu.RLock()
+ for t, u := range registry {
+ if u == url {
+ mu.RUnlock()
+ return t, false, nil
+ }
+ }
+ mu.RUnlock()
+ mt, err := protoregistry.GlobalTypes.FindMessageByURL(url)
+ if err != nil {
+ if errors.Is(err, protoregistry.NotFound) {
+ for _, h := range handlers {
+ if t, isProto := h.GetType(url); t != nil {
+ return t, isProto, nil
+ }
+ }
+ }
+ return nil, false, fmt.Errorf("type with url %s: %w", url, ErrNotFound)
+ }
+ empty := mt.New().Interface()
+ return reflect.TypeOf(empty).Elem(), true, nil
+}
+
+func tryDereference(v interface{}) reflect.Type {
+ t := reflect.TypeOf(v)
+ if t.Kind() == reflect.Ptr {
+ // require check of pointer but dereference to register
+ return t.Elem()
+ }
+ panic("v is not a pointer to a type")
+}
diff --git a/vendor/github.com/containerd/typeurl/v2/types_gogo.go b/vendor/github.com/containerd/typeurl/v2/types_gogo.go
new file mode 100644
index 00000000..adb892ec
--- /dev/null
+++ b/vendor/github.com/containerd/typeurl/v2/types_gogo.go
@@ -0,0 +1,68 @@
+//go:build !no_gogo
+
+/*
+ Copyright The containerd Authors.
+
+ 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 typeurl
+
+import (
+ "reflect"
+
+ gogoproto "github.com/gogo/protobuf/proto"
+)
+
+func init() {
+ handlers = append(handlers, gogoHandler{})
+}
+
+type gogoHandler struct{}
+
+func (gogoHandler) Marshaller(v interface{}) func() ([]byte, error) {
+ pm, ok := v.(gogoproto.Message)
+ if !ok {
+ return nil
+ }
+ return func() ([]byte, error) {
+ return gogoproto.Marshal(pm)
+ }
+}
+
+func (gogoHandler) Unmarshaller(v interface{}) func([]byte) error {
+ pm, ok := v.(gogoproto.Message)
+ if !ok {
+ return nil
+ }
+
+ return func(dt []byte) error {
+ return gogoproto.Unmarshal(dt, pm)
+ }
+}
+
+func (gogoHandler) TypeURL(v interface{}) string {
+ pm, ok := v.(gogoproto.Message)
+ if !ok {
+ return ""
+ }
+ return gogoproto.MessageName(pm)
+}
+
+func (gogoHandler) GetType(url string) (reflect.Type, bool) {
+ t := gogoproto.MessageType(url)
+ if t == nil {
+ return nil, false
+ }
+ return t.Elem(), true
+}
diff --git a/vendor/github.com/containernetworking/cni/LICENSE b/vendor/github.com/containernetworking/cni/LICENSE
new file mode 100644
index 00000000..8f71f43f
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/LICENSE
@@ -0,0 +1,202 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ 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.
+
diff --git a/vendor/github.com/containernetworking/cni/libcni/api.go b/vendor/github.com/containernetworking/cni/libcni/api.go
new file mode 100644
index 00000000..6ac26949
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/libcni/api.go
@@ -0,0 +1,900 @@
+// Copyright 2015 CNI authors
+//
+// 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 libcni
+
+// Note this is the actual implementation of the CNI specification, which
+// is reflected in the SPEC.md file.
+// it is typically bundled into runtime providers (i.e. containerd or cri-o would use this
+// before calling runc or hcsshim). It is also bundled into CNI providers as well, for example,
+// to add an IP to a container, to parse the configuration of the CNI and so on.
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "github.com/containernetworking/cni/pkg/invoke"
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/create"
+ "github.com/containernetworking/cni/pkg/utils"
+ "github.com/containernetworking/cni/pkg/version"
+)
+
+var (
+ CacheDir = "/var/lib/cni"
+ // slightly awkward wording to preserve anyone matching on error strings
+ ErrorCheckNotSupp = fmt.Errorf("does not support the CHECK command")
+)
+
+const (
+ CNICacheV1 = "cniCacheV1"
+)
+
+// A RuntimeConf holds the arguments to one invocation of a CNI plugin
+// excepting the network configuration, with the nested exception that
+// the `runtimeConfig` from the network configuration is included
+// here.
+type RuntimeConf struct {
+ ContainerID string
+ NetNS string
+ IfName string
+ Args [][2]string
+ // A dictionary of capability-specific data passed by the runtime
+ // to plugins as top-level keys in the 'runtimeConfig' dictionary
+ // of the plugin's stdin data. libcni will ensure that only keys
+ // in this map which match the capabilities of the plugin are passed
+ // to the plugin
+ CapabilityArgs map[string]interface{}
+
+ // DEPRECATED. Will be removed in a future release.
+ CacheDir string
+}
+
+// Use PluginConfig instead of NetworkConfig, the NetworkConfig
+// backwards-compat alias will be removed in a future release.
+type NetworkConfig = PluginConfig
+
+type PluginConfig struct {
+ Network *types.PluginConf
+ Bytes []byte
+}
+
+type NetworkConfigList struct {
+ Name string
+ CNIVersion string
+ DisableCheck bool
+ DisableGC bool
+ LoadOnlyInlinedPlugins bool
+ Plugins []*PluginConfig
+ Bytes []byte
+}
+
+type NetworkAttachment struct {
+ ContainerID string
+ Network string
+ IfName string
+ Config []byte
+ NetNS string
+ CniArgs [][2]string
+ CapabilityArgs map[string]interface{}
+}
+
+type GCArgs struct {
+ ValidAttachments []types.GCAttachment
+}
+
+type CNI interface {
+ AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
+ CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
+ DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
+ GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
+ GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
+
+ AddNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) (types.Result, error)
+ CheckNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error
+ DelNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error
+ GetNetworkCachedResult(net *PluginConfig, rt *RuntimeConf) (types.Result, error)
+ GetNetworkCachedConfig(net *PluginConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
+
+ ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
+ ValidateNetwork(ctx context.Context, net *PluginConfig) ([]string, error)
+
+ GCNetworkList(ctx context.Context, net *NetworkConfigList, args *GCArgs) error
+ GetStatusNetworkList(ctx context.Context, net *NetworkConfigList) error
+
+ GetCachedAttachments(containerID string) ([]*NetworkAttachment, error)
+
+ GetVersionInfo(ctx context.Context, pluginType string) (version.PluginInfo, error)
+}
+
+type CNIConfig struct {
+ Path []string
+ exec invoke.Exec
+ cacheDir string
+}
+
+// CNIConfig implements the CNI interface
+var _ CNI = &CNIConfig{}
+
+// NewCNIConfig returns a new CNIConfig object that will search for plugins
+// in the given paths and use the given exec interface to run those plugins,
+// or if the exec interface is not given, will use a default exec handler.
+func NewCNIConfig(path []string, exec invoke.Exec) *CNIConfig {
+ return NewCNIConfigWithCacheDir(path, "", exec)
+}
+
+// NewCNIConfigWithCacheDir returns a new CNIConfig object that will search for plugins
+// in the given paths use the given exec interface to run those plugins,
+// or if the exec interface is not given, will use a default exec handler.
+// The given cache directory will be used for temporary data storage when needed.
+func NewCNIConfigWithCacheDir(path []string, cacheDir string, exec invoke.Exec) *CNIConfig {
+ return &CNIConfig{
+ Path: path,
+ cacheDir: cacheDir,
+ exec: exec,
+ }
+}
+
+func buildOneConfig(name, cniVersion string, orig *PluginConfig, prevResult types.Result, rt *RuntimeConf) (*PluginConfig, error) {
+ var err error
+
+ inject := map[string]interface{}{
+ "name": name,
+ "cniVersion": cniVersion,
+ }
+ // Add previous plugin result
+ if prevResult != nil {
+ inject["prevResult"] = prevResult
+ }
+
+ // Ensure every config uses the same name and version
+ orig, err = InjectConf(orig, inject)
+ if err != nil {
+ return nil, err
+ }
+ if rt != nil {
+ return injectRuntimeConfig(orig, rt)
+ }
+
+ return orig, nil
+}
+
+// This function takes a libcni RuntimeConf structure and injects values into
+// a "runtimeConfig" dictionary in the CNI network configuration JSON that
+// will be passed to the plugin on stdin.
+//
+// Only "capabilities arguments" passed by the runtime are currently injected.
+// These capabilities arguments are filtered through the plugin's advertised
+// capabilities from its config JSON, and any keys in the CapabilityArgs
+// matching plugin capabilities are added to the "runtimeConfig" dictionary
+// sent to the plugin via JSON on stdin. For example, if the plugin's
+// capabilities include "portMappings", and the CapabilityArgs map includes a
+// "portMappings" key, that key and its value are added to the "runtimeConfig"
+// dictionary to be passed to the plugin's stdin.
+func injectRuntimeConfig(orig *PluginConfig, rt *RuntimeConf) (*PluginConfig, error) {
+ var err error
+
+ rc := make(map[string]interface{})
+ for capability, supported := range orig.Network.Capabilities {
+ if !supported {
+ continue
+ }
+ if data, ok := rt.CapabilityArgs[capability]; ok {
+ rc[capability] = data
+ }
+ }
+
+ if len(rc) > 0 {
+ orig, err = InjectConf(orig, map[string]interface{}{"runtimeConfig": rc})
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return orig, nil
+}
+
+// ensure we have a usable exec if the CNIConfig was not given one
+func (c *CNIConfig) ensureExec() invoke.Exec {
+ if c.exec == nil {
+ c.exec = &invoke.DefaultExec{
+ RawExec: &invoke.RawExec{Stderr: os.Stderr},
+ PluginDecoder: version.PluginDecoder{},
+ }
+ }
+ return c.exec
+}
+
+type cachedInfo struct {
+ Kind string `json:"kind"`
+ ContainerID string `json:"containerId"`
+ Config []byte `json:"config"`
+ IfName string `json:"ifName"`
+ NetworkName string `json:"networkName"`
+ NetNS string `json:"netns,omitempty"`
+ CniArgs [][2]string `json:"cniArgs,omitempty"`
+ CapabilityArgs map[string]interface{} `json:"capabilityArgs,omitempty"`
+ RawResult map[string]interface{} `json:"result,omitempty"`
+ Result types.Result `json:"-"`
+}
+
+// getCacheDir returns the cache directory in this order:
+// 1) global cacheDir from CNIConfig object
+// 2) deprecated cacheDir from RuntimeConf object
+// 3) fall back to default cache directory
+func (c *CNIConfig) getCacheDir(rt *RuntimeConf) string {
+ if c.cacheDir != "" {
+ return c.cacheDir
+ }
+ if rt.CacheDir != "" {
+ return rt.CacheDir
+ }
+ return CacheDir
+}
+
+func (c *CNIConfig) getCacheFilePath(netName string, rt *RuntimeConf) (string, error) {
+ if netName == "" || rt.ContainerID == "" || rt.IfName == "" {
+ return "", fmt.Errorf("cache file path requires network name (%q), container ID (%q), and interface name (%q)", netName, rt.ContainerID, rt.IfName)
+ }
+ return filepath.Join(c.getCacheDir(rt), "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName)), nil
+}
+
+func (c *CNIConfig) cacheAdd(result types.Result, config []byte, netName string, rt *RuntimeConf) error {
+ cached := cachedInfo{
+ Kind: CNICacheV1,
+ ContainerID: rt.ContainerID,
+ Config: config,
+ IfName: rt.IfName,
+ NetworkName: netName,
+ NetNS: rt.NetNS,
+ CniArgs: rt.Args,
+ CapabilityArgs: rt.CapabilityArgs,
+ }
+
+ // We need to get type.Result into cachedInfo as JSON map
+ // Marshal to []byte, then Unmarshal into cached.RawResult
+ data, err := json.Marshal(result)
+ if err != nil {
+ return err
+ }
+
+ err = json.Unmarshal(data, &cached.RawResult)
+ if err != nil {
+ return err
+ }
+
+ newBytes, err := json.Marshal(&cached)
+ if err != nil {
+ return err
+ }
+
+ fname, err := c.getCacheFilePath(netName, rt)
+ if err != nil {
+ return err
+ }
+ if err := os.MkdirAll(filepath.Dir(fname), 0o700); err != nil {
+ return err
+ }
+
+ return os.WriteFile(fname, newBytes, 0o600)
+}
+
+func (c *CNIConfig) cacheDel(netName string, rt *RuntimeConf) error {
+ fname, err := c.getCacheFilePath(netName, rt)
+ if err != nil {
+ // Ignore error
+ return nil
+ }
+ return os.Remove(fname)
+}
+
+func (c *CNIConfig) getCachedConfig(netName string, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
+ var bytes []byte
+
+ fname, err := c.getCacheFilePath(netName, rt)
+ if err != nil {
+ return nil, nil, err
+ }
+ bytes, err = os.ReadFile(fname)
+ if err != nil {
+ // Ignore read errors; the cached result may not exist on-disk
+ return nil, nil, nil
+ }
+
+ unmarshaled := cachedInfo{}
+ if err := json.Unmarshal(bytes, &unmarshaled); err != nil {
+ return nil, nil, fmt.Errorf("failed to unmarshal cached network %q config: %w", netName, err)
+ }
+ if unmarshaled.Kind != CNICacheV1 {
+ return nil, nil, fmt.Errorf("read cached network %q config has wrong kind: %v", netName, unmarshaled.Kind)
+ }
+
+ newRt := *rt
+ if unmarshaled.CniArgs != nil {
+ newRt.Args = unmarshaled.CniArgs
+ }
+ newRt.CapabilityArgs = unmarshaled.CapabilityArgs
+
+ return unmarshaled.Config, &newRt, nil
+}
+
+func (c *CNIConfig) getLegacyCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
+ fname, err := c.getCacheFilePath(netName, rt)
+ if err != nil {
+ return nil, err
+ }
+ data, err := os.ReadFile(fname)
+ if err != nil {
+ // Ignore read errors; the cached result may not exist on-disk
+ return nil, nil
+ }
+
+ // Load the cached result
+ result, err := create.CreateFromBytes(data)
+ if err != nil {
+ return nil, err
+ }
+
+ // Convert to the config version to ensure plugins get prevResult
+ // in the same version as the config. The cached result version
+ // should match the config version unless the config was changed
+ // while the container was running.
+ result, err = result.GetAsVersion(cniVersion)
+ if err != nil {
+ return nil, fmt.Errorf("failed to convert cached result to config version %q: %w", cniVersion, err)
+ }
+ return result, nil
+}
+
+func (c *CNIConfig) getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
+ fname, err := c.getCacheFilePath(netName, rt)
+ if err != nil {
+ return nil, err
+ }
+ fdata, err := os.ReadFile(fname)
+ if err != nil {
+ // Ignore read errors; the cached result may not exist on-disk
+ return nil, nil
+ }
+
+ cachedInfo := cachedInfo{}
+ if err := json.Unmarshal(fdata, &cachedInfo); err != nil || cachedInfo.Kind != CNICacheV1 {
+ return c.getLegacyCachedResult(netName, cniVersion, rt)
+ }
+
+ newBytes, err := json.Marshal(&cachedInfo.RawResult)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal cached network %q config: %w", netName, err)
+ }
+
+ // Load the cached result
+ result, err := create.CreateFromBytes(newBytes)
+ if err != nil {
+ return nil, err
+ }
+
+ // Convert to the config version to ensure plugins get prevResult
+ // in the same version as the config. The cached result version
+ // should match the config version unless the config was changed
+ // while the container was running.
+ result, err = result.GetAsVersion(cniVersion)
+ if err != nil {
+ return nil, fmt.Errorf("failed to convert cached result to config version %q: %w", cniVersion, err)
+ }
+ return result, nil
+}
+
+// GetNetworkListCachedResult returns the cached Result of the previous
+// AddNetworkList() operation for a network list, or an error.
+func (c *CNIConfig) GetNetworkListCachedResult(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
+ return c.getCachedResult(list.Name, list.CNIVersion, rt)
+}
+
+// GetNetworkCachedResult returns the cached Result of the previous
+// AddNetwork() operation for a network, or an error.
+func (c *CNIConfig) GetNetworkCachedResult(net *PluginConfig, rt *RuntimeConf) (types.Result, error) {
+ return c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
+}
+
+// GetNetworkListCachedConfig copies the input RuntimeConf to output
+// RuntimeConf with fields updated with info from the cached Config.
+func (c *CNIConfig) GetNetworkListCachedConfig(list *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
+ return c.getCachedConfig(list.Name, rt)
+}
+
+// GetNetworkCachedConfig copies the input RuntimeConf to output
+// RuntimeConf with fields updated with info from the cached Config.
+func (c *CNIConfig) GetNetworkCachedConfig(net *PluginConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
+ return c.getCachedConfig(net.Network.Name, rt)
+}
+
+// GetCachedAttachments returns a list of network attachments from the cache.
+// The returned list will be filtered by the containerID if the value is not empty.
+func (c *CNIConfig) GetCachedAttachments(containerID string) ([]*NetworkAttachment, error) {
+ dirPath := filepath.Join(c.getCacheDir(&RuntimeConf{}), "results")
+ entries, err := os.ReadDir(dirPath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil, nil
+ }
+ return nil, err
+ }
+
+ fileNames := make([]string, 0, len(entries))
+ for _, e := range entries {
+ fileNames = append(fileNames, e.Name())
+ }
+ sort.Strings(fileNames)
+
+ attachments := []*NetworkAttachment{}
+ for _, fname := range fileNames {
+ if len(containerID) > 0 {
+ part := fmt.Sprintf("-%s-", containerID)
+ pos := strings.Index(fname, part)
+ if pos <= 0 || pos+len(part) >= len(fname) {
+ continue
+ }
+ }
+
+ cacheFile := filepath.Join(dirPath, fname)
+ bytes, err := os.ReadFile(cacheFile)
+ if err != nil {
+ continue
+ }
+
+ cachedInfo := cachedInfo{}
+
+ if err := json.Unmarshal(bytes, &cachedInfo); err != nil {
+ continue
+ }
+ if cachedInfo.Kind != CNICacheV1 {
+ continue
+ }
+ if len(containerID) > 0 && cachedInfo.ContainerID != containerID {
+ continue
+ }
+ if cachedInfo.IfName == "" || cachedInfo.NetworkName == "" {
+ continue
+ }
+
+ attachments = append(attachments, &NetworkAttachment{
+ ContainerID: cachedInfo.ContainerID,
+ Network: cachedInfo.NetworkName,
+ IfName: cachedInfo.IfName,
+ Config: cachedInfo.Config,
+ NetNS: cachedInfo.NetNS,
+ CniArgs: cachedInfo.CniArgs,
+ CapabilityArgs: cachedInfo.CapabilityArgs,
+ })
+ }
+ return attachments, nil
+}
+
+func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
+ c.ensureExec()
+ pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
+ if err != nil {
+ return nil, err
+ }
+ if err := utils.ValidateContainerID(rt.ContainerID); err != nil {
+ return nil, err
+ }
+ if err := utils.ValidateNetworkName(name); err != nil {
+ return nil, err
+ }
+ if err := utils.ValidateInterfaceName(rt.IfName); err != nil {
+ return nil, err
+ }
+
+ newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
+ if err != nil {
+ return nil, err
+ }
+
+ return invoke.ExecPluginWithResult(ctx, pluginPath, newConf.Bytes, c.args("ADD", rt), c.exec)
+}
+
+// AddNetworkList executes a sequence of plugins with the ADD command
+func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
+ var err error
+ var result types.Result
+ for _, net := range list.Plugins {
+ result, err = c.addNetwork(ctx, list.Name, list.CNIVersion, net, result, rt)
+ if err != nil {
+ return nil, fmt.Errorf("plugin %s failed (add): %w", pluginDescription(net.Network), err)
+ }
+ }
+
+ if err = c.cacheAdd(result, list.Bytes, list.Name, rt); err != nil {
+ return nil, fmt.Errorf("failed to set network %q cached result: %w", list.Name, err)
+ }
+
+ return result, nil
+}
+
+func (c *CNIConfig) checkNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) error {
+ c.ensureExec()
+ pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
+ if err != nil {
+ return err
+ }
+
+ newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
+ if err != nil {
+ return err
+ }
+
+ return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("CHECK", rt), c.exec)
+}
+
+// CheckNetworkList executes a sequence of plugins with the CHECK command
+func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
+ // CHECK was added in CNI spec version 0.4.0 and higher
+ if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
+ return err
+ } else if !gtet {
+ return fmt.Errorf("configuration version %q %w", list.CNIVersion, ErrorCheckNotSupp)
+ }
+
+ if list.DisableCheck {
+ return nil
+ }
+
+ cachedResult, err := c.getCachedResult(list.Name, list.CNIVersion, rt)
+ if err != nil {
+ return fmt.Errorf("failed to get network %q cached result: %w", list.Name, err)
+ }
+
+ for _, net := range list.Plugins {
+ if err := c.checkNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) error {
+ c.ensureExec()
+ pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
+ if err != nil {
+ return err
+ }
+
+ newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
+ if err != nil {
+ return err
+ }
+
+ return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("DEL", rt), c.exec)
+}
+
+// DelNetworkList executes a sequence of plugins with the DEL command
+func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
+ var cachedResult types.Result
+
+ // Cached result on DEL was added in CNI spec version 0.4.0 and higher
+ if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
+ return err
+ } else if gtet {
+ if cachedResult, err = c.getCachedResult(list.Name, list.CNIVersion, rt); err != nil {
+ _ = c.cacheDel(list.Name, rt)
+ cachedResult = nil
+ }
+ }
+
+ for i := len(list.Plugins) - 1; i >= 0; i-- {
+ net := list.Plugins[i]
+ if err := c.delNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil {
+ return fmt.Errorf("plugin %s failed (delete): %w", pluginDescription(net.Network), err)
+ }
+ }
+
+ _ = c.cacheDel(list.Name, rt)
+
+ return nil
+}
+
+func pluginDescription(net *types.PluginConf) string {
+ if net == nil {
+ return ""
+ }
+ pluginType := net.Type
+ out := fmt.Sprintf("type=%q", pluginType)
+ name := net.Name
+ if name != "" {
+ out += fmt.Sprintf(" name=%q", name)
+ }
+ return out
+}
+
+// AddNetwork executes the plugin with the ADD command
+func (c *CNIConfig) AddNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) (types.Result, error) {
+ result, err := c.addNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, nil, rt)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = c.cacheAdd(result, net.Bytes, net.Network.Name, rt); err != nil {
+ return nil, fmt.Errorf("failed to set network %q cached result: %w", net.Network.Name, err)
+ }
+
+ return result, nil
+}
+
+// CheckNetwork executes the plugin with the CHECK command
+func (c *CNIConfig) CheckNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error {
+ // CHECK was added in CNI spec version 0.4.0 and higher
+ if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
+ return err
+ } else if !gtet {
+ return fmt.Errorf("configuration version %q %w", net.Network.CNIVersion, ErrorCheckNotSupp)
+ }
+
+ cachedResult, err := c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
+ if err != nil {
+ return fmt.Errorf("failed to get network %q cached result: %w", net.Network.Name, err)
+ }
+ return c.checkNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt)
+}
+
+// DelNetwork executes the plugin with the DEL command
+func (c *CNIConfig) DelNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error {
+ var cachedResult types.Result
+
+ // Cached result on DEL was added in CNI spec version 0.4.0 and higher
+ if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
+ return err
+ } else if gtet {
+ cachedResult, err = c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
+ if err != nil {
+ return fmt.Errorf("failed to get network %q cached result: %w", net.Network.Name, err)
+ }
+ }
+
+ if err := c.delNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil {
+ return err
+ }
+ _ = c.cacheDel(net.Network.Name, rt)
+ return nil
+}
+
+// ValidateNetworkList checks that a configuration is reasonably valid.
+// - all the specified plugins exist on disk
+// - every plugin supports the desired version.
+//
+// Returns a list of all capabilities supported by the configuration, or error
+func (c *CNIConfig) ValidateNetworkList(ctx context.Context, list *NetworkConfigList) ([]string, error) {
+ version := list.CNIVersion
+
+ // holding map for seen caps (in case of duplicates)
+ caps := map[string]interface{}{}
+
+ errs := []error{}
+ for _, net := range list.Plugins {
+ if err := c.validatePlugin(ctx, net.Network.Type, version); err != nil {
+ errs = append(errs, err)
+ }
+ for c, enabled := range net.Network.Capabilities {
+ if !enabled {
+ continue
+ }
+ caps[c] = struct{}{}
+ }
+ }
+
+ if len(errs) > 0 {
+ return nil, fmt.Errorf("%v", errs)
+ }
+
+ // make caps list
+ cc := make([]string, 0, len(caps))
+ for c := range caps {
+ cc = append(cc, c)
+ }
+
+ return cc, nil
+}
+
+// ValidateNetwork checks that a configuration is reasonably valid.
+// It uses the same logic as ValidateNetworkList)
+// Returns a list of capabilities
+func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *PluginConfig) ([]string, error) {
+ caps := []string{}
+ for c, ok := range net.Network.Capabilities {
+ if ok {
+ caps = append(caps, c)
+ }
+ }
+ if err := c.validatePlugin(ctx, net.Network.Type, net.Network.CNIVersion); err != nil {
+ return nil, err
+ }
+ return caps, nil
+}
+
+// validatePlugin checks that an individual plugin's configuration is sane
+func (c *CNIConfig) validatePlugin(ctx context.Context, pluginName, expectedVersion string) error {
+ c.ensureExec()
+ pluginPath, err := c.exec.FindInPath(pluginName, c.Path)
+ if err != nil {
+ return err
+ }
+ if expectedVersion == "" {
+ expectedVersion = "0.1.0"
+ }
+
+ vi, err := invoke.GetVersionInfo(ctx, pluginPath, c.exec)
+ if err != nil {
+ return err
+ }
+ for _, vers := range vi.SupportedVersions() {
+ if vers == expectedVersion {
+ return nil
+ }
+ }
+ return fmt.Errorf("plugin %s does not support config version %q", pluginName, expectedVersion)
+}
+
+// GetVersionInfo reports which versions of the CNI spec are supported by
+// the given plugin.
+func (c *CNIConfig) GetVersionInfo(ctx context.Context, pluginType string) (version.PluginInfo, error) {
+ c.ensureExec()
+ pluginPath, err := c.exec.FindInPath(pluginType, c.Path)
+ if err != nil {
+ return nil, err
+ }
+
+ return invoke.GetVersionInfo(ctx, pluginPath, c.exec)
+}
+
+// GCNetworkList will do two things
+// - dump the list of cached attachments, and issue deletes as necessary
+// - issue a GC to the underlying plugins (if the version is high enough)
+func (c *CNIConfig) GCNetworkList(ctx context.Context, list *NetworkConfigList, args *GCArgs) error {
+ // If DisableGC is set, then don't bother GCing at all.
+ if list.DisableGC {
+ return nil
+ }
+
+ // First, get the list of cached attachments
+ cachedAttachments, err := c.GetCachedAttachments("")
+ if err != nil {
+ return nil
+ }
+
+ var validAttachments map[types.GCAttachment]interface{}
+ if args != nil {
+ validAttachments = make(map[types.GCAttachment]interface{}, len(args.ValidAttachments))
+ for _, a := range args.ValidAttachments {
+ validAttachments[a] = nil
+ }
+ }
+
+ var errs []error
+
+ for _, cachedAttachment := range cachedAttachments {
+ if cachedAttachment.Network != list.Name {
+ continue
+ }
+ // we found this attachment
+ gca := types.GCAttachment{
+ ContainerID: cachedAttachment.ContainerID,
+ IfName: cachedAttachment.IfName,
+ }
+ if _, ok := validAttachments[gca]; ok {
+ continue
+ }
+ // otherwise, this attachment wasn't valid and we should issue a CNI DEL
+ rt := RuntimeConf{
+ ContainerID: cachedAttachment.ContainerID,
+ NetNS: cachedAttachment.NetNS,
+ IfName: cachedAttachment.IfName,
+ Args: cachedAttachment.CniArgs,
+ CapabilityArgs: cachedAttachment.CapabilityArgs,
+ }
+ if err := c.DelNetworkList(ctx, list, &rt); err != nil {
+ errs = append(errs, fmt.Errorf("failed to delete stale attachment %s %s: %w", rt.ContainerID, rt.IfName, err))
+ }
+ }
+
+ // now, if the version supports it, issue a GC
+ if gt, _ := version.GreaterThanOrEqualTo(list.CNIVersion, "1.1.0"); gt {
+ inject := map[string]interface{}{
+ "name": list.Name,
+ "cniVersion": list.CNIVersion,
+ }
+ if args != nil {
+ inject["cni.dev/valid-attachments"] = args.ValidAttachments
+ // #1101: spec used incorrect variable name
+ inject["cni.dev/attachments"] = args.ValidAttachments
+ }
+
+ for _, plugin := range list.Plugins {
+ // build config here
+ pluginConfig, err := InjectConf(plugin, inject)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("failed to generate configuration to GC plugin %s: %w", plugin.Network.Type, err))
+ }
+ if err := c.gcNetwork(ctx, pluginConfig); err != nil {
+ errs = append(errs, fmt.Errorf("failed to GC plugin %s: %w", plugin.Network.Type, err))
+ }
+ }
+ }
+
+ return errors.Join(errs...)
+}
+
+func (c *CNIConfig) gcNetwork(ctx context.Context, net *PluginConfig) error {
+ c.ensureExec()
+ pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
+ if err != nil {
+ return err
+ }
+ args := c.args("GC", &RuntimeConf{})
+
+ return invoke.ExecPluginWithoutResult(ctx, pluginPath, net.Bytes, args, c.exec)
+}
+
+func (c *CNIConfig) GetStatusNetworkList(ctx context.Context, list *NetworkConfigList) error {
+ // If the version doesn't support status, abort.
+ if gt, _ := version.GreaterThanOrEqualTo(list.CNIVersion, "1.1.0"); !gt {
+ return nil
+ }
+
+ inject := map[string]interface{}{
+ "name": list.Name,
+ "cniVersion": list.CNIVersion,
+ }
+
+ for _, plugin := range list.Plugins {
+ // build config here
+ pluginConfig, err := InjectConf(plugin, inject)
+ if err != nil {
+ return fmt.Errorf("failed to generate configuration to get plugin STATUS %s: %w", plugin.Network.Type, err)
+ }
+ if err := c.getStatusNetwork(ctx, pluginConfig); err != nil {
+ return err // Don't collect errors here, so we return a clean error code.
+ }
+ }
+ return nil
+}
+
+func (c *CNIConfig) getStatusNetwork(ctx context.Context, net *PluginConfig) error {
+ c.ensureExec()
+ pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
+ if err != nil {
+ return err
+ }
+ args := c.args("STATUS", &RuntimeConf{})
+
+ return invoke.ExecPluginWithoutResult(ctx, pluginPath, net.Bytes, args, c.exec)
+}
+
+// =====
+func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {
+ return &invoke.Args{
+ Command: action,
+ ContainerID: rt.ContainerID,
+ NetNS: rt.NetNS,
+ PluginArgs: rt.Args,
+ IfName: rt.IfName,
+ Path: strings.Join(c.Path, string(os.PathListSeparator)),
+ }
+}
diff --git a/vendor/github.com/containernetworking/cni/libcni/conf.go b/vendor/github.com/containernetworking/cni/libcni/conf.go
new file mode 100644
index 00000000..7f8482e7
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/libcni/conf.go
@@ -0,0 +1,445 @@
+// Copyright 2015 CNI authors
+//
+// 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 libcni
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "slices"
+ "sort"
+ "strings"
+
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/version"
+)
+
+type NotFoundError struct {
+ Dir string
+ Name string
+}
+
+func (e NotFoundError) Error() string {
+ return fmt.Sprintf(`no net configuration with name "%s" in %s`, e.Name, e.Dir)
+}
+
+type NoConfigsFoundError struct {
+ Dir string
+}
+
+func (e NoConfigsFoundError) Error() string {
+ return fmt.Sprintf(`no net configurations found in %s`, e.Dir)
+}
+
+// This will not validate that the plugins actually belong to the netconfig by ensuring
+// that they are loaded from a directory named after the networkName, relative to the network config.
+//
+// Since here we are just accepting raw bytes, the caller is responsible for ensuring that the plugin
+// config provided here actually "belongs" to the networkconfig in question.
+func NetworkPluginConfFromBytes(pluginConfBytes []byte) (*PluginConfig, error) {
+ // TODO why are we creating a struct that holds both the byte representation and the deserialized
+ // representation, and returning that, instead of just returning the deserialized representation?
+ conf := &PluginConfig{Bytes: pluginConfBytes, Network: &types.PluginConf{}}
+ if err := json.Unmarshal(pluginConfBytes, conf.Network); err != nil {
+ return nil, fmt.Errorf("error parsing configuration: %w", err)
+ }
+ if conf.Network.Type == "" {
+ return nil, fmt.Errorf("error parsing configuration: missing 'type'")
+ }
+ return conf, nil
+}
+
+// Given a path to a directory containing a network configuration, and the name of a network,
+// loads all plugin definitions found at path `networkConfPath/networkName/*.conf`
+func NetworkPluginConfsFromFiles(networkConfPath, networkName string) ([]*PluginConfig, error) {
+ var pConfs []*PluginConfig
+
+ pluginConfPath := filepath.Join(networkConfPath, networkName)
+
+ pluginConfFiles, err := ConfFiles(pluginConfPath, []string{".conf"})
+ if err != nil {
+ return nil, fmt.Errorf("failed to read plugin config files in %s: %w", pluginConfPath, err)
+ }
+
+ for _, pluginConfFile := range pluginConfFiles {
+ pluginConfBytes, err := os.ReadFile(pluginConfFile)
+ if err != nil {
+ return nil, fmt.Errorf("error reading %s: %w", pluginConfFile, err)
+ }
+ pluginConf, err := NetworkPluginConfFromBytes(pluginConfBytes)
+ if err != nil {
+ return nil, err
+ }
+ pConfs = append(pConfs, pluginConf)
+ }
+ return pConfs, nil
+}
+
+func NetworkConfFromBytes(confBytes []byte) (*NetworkConfigList, error) {
+ rawList := make(map[string]interface{})
+ if err := json.Unmarshal(confBytes, &rawList); err != nil {
+ return nil, fmt.Errorf("error parsing configuration list: %w", err)
+ }
+
+ rawName, ok := rawList["name"]
+ if !ok {
+ return nil, fmt.Errorf("error parsing configuration list: no name")
+ }
+ name, ok := rawName.(string)
+ if !ok {
+ return nil, fmt.Errorf("error parsing configuration list: invalid name type %T", rawName)
+ }
+
+ var cniVersion string
+ rawVersion, ok := rawList["cniVersion"]
+ if ok {
+ cniVersion, ok = rawVersion.(string)
+ if !ok {
+ return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion type %T", rawVersion)
+ }
+ }
+
+ rawVersions, ok := rawList["cniVersions"]
+ if ok {
+ // Parse the current package CNI version
+ rvs, ok := rawVersions.([]interface{})
+ if !ok {
+ return nil, fmt.Errorf("error parsing configuration list: invalid type for cniVersions: %T", rvs)
+ }
+ vs := make([]string, 0, len(rvs))
+ for i, rv := range rvs {
+ v, ok := rv.(string)
+ if !ok {
+ return nil, fmt.Errorf("error parsing configuration list: invalid type for cniVersions index %d: %T", i, rv)
+ }
+ gt, err := version.GreaterThan(v, version.Current())
+ if err != nil {
+ return nil, fmt.Errorf("error parsing configuration list: invalid cniVersions entry %s at index %d: %w", v, i, err)
+ } else if !gt {
+ // Skip versions "greater" than this implementation of the spec
+ vs = append(vs, v)
+ }
+ }
+
+ // if cniVersion was already set, append it to the list for sorting.
+ if cniVersion != "" {
+ gt, err := version.GreaterThan(cniVersion, version.Current())
+ if err != nil {
+ return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion %s: %w", cniVersion, err)
+ } else if !gt {
+ // ignore any versions higher than the current implemented spec version
+ vs = append(vs, cniVersion)
+ }
+ }
+ slices.SortFunc[[]string](vs, func(v1, v2 string) int {
+ if v1 == v2 {
+ return 0
+ }
+ if gt, _ := version.GreaterThan(v1, v2); gt {
+ return 1
+ }
+ return -1
+ })
+ if len(vs) > 0 {
+ cniVersion = vs[len(vs)-1]
+ }
+ }
+
+ readBool := func(key string) (bool, error) {
+ rawVal, ok := rawList[key]
+ if !ok {
+ return false, nil
+ }
+ if b, ok := rawVal.(bool); ok {
+ return b, nil
+ }
+
+ s, ok := rawVal.(string)
+ if !ok {
+ return false, fmt.Errorf("error parsing configuration list: invalid type %T for %s", rawVal, key)
+ }
+ s = strings.ToLower(s)
+ switch s {
+ case "false":
+ return false, nil
+ case "true":
+ return true, nil
+ }
+ return false, fmt.Errorf("error parsing configuration list: invalid value %q for %s", s, key)
+ }
+
+ disableCheck, err := readBool("disableCheck")
+ if err != nil {
+ return nil, err
+ }
+
+ disableGC, err := readBool("disableGC")
+ if err != nil {
+ return nil, err
+ }
+
+ loadOnlyInlinedPlugins, err := readBool("loadOnlyInlinedPlugins")
+ if err != nil {
+ return nil, err
+ }
+
+ list := &NetworkConfigList{
+ Name: name,
+ DisableCheck: disableCheck,
+ DisableGC: disableGC,
+ LoadOnlyInlinedPlugins: loadOnlyInlinedPlugins,
+ CNIVersion: cniVersion,
+ Bytes: confBytes,
+ }
+
+ var plugins []interface{}
+ plug, ok := rawList["plugins"]
+ // We can have a `plugins` list key in the main conf,
+ // We can also have `loadOnlyInlinedPlugins == true`
+ //
+ // If `plugins` is there, then `loadOnlyInlinedPlugins` can be true
+ //
+ // If plugins is NOT there, then `loadOnlyInlinedPlugins` cannot be true
+ //
+ // We have to have at least some plugins.
+ if !ok && loadOnlyInlinedPlugins {
+ return nil, fmt.Errorf("error parsing configuration list: `loadOnlyInlinedPlugins` is true, and no 'plugins' key")
+ } else if !ok && !loadOnlyInlinedPlugins {
+ return list, nil
+ }
+
+ plugins, ok = plug.([]interface{})
+ if !ok {
+ return nil, fmt.Errorf("error parsing configuration list: invalid 'plugins' type %T", plug)
+ }
+ if len(plugins) == 0 {
+ return nil, fmt.Errorf("error parsing configuration list: no plugins in list")
+ }
+
+ for i, conf := range plugins {
+ newBytes, err := json.Marshal(conf)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal plugin config %d: %w", i, err)
+ }
+ netConf, err := ConfFromBytes(newBytes)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse plugin config %d: %w", i, err)
+ }
+ list.Plugins = append(list.Plugins, netConf)
+ }
+ return list, nil
+}
+
+func NetworkConfFromFile(filename string) (*NetworkConfigList, error) {
+ bytes, err := os.ReadFile(filename)
+ if err != nil {
+ return nil, fmt.Errorf("error reading %s: %w", filename, err)
+ }
+
+ conf, err := NetworkConfFromBytes(bytes)
+ if err != nil {
+ return nil, err
+ }
+
+ if !conf.LoadOnlyInlinedPlugins {
+ plugins, err := NetworkPluginConfsFromFiles(filepath.Dir(filename), conf.Name)
+ if err != nil {
+ return nil, err
+ }
+ conf.Plugins = append(conf.Plugins, plugins...)
+ }
+
+ if len(conf.Plugins) == 0 {
+ // Having 0 plugins for a given network is not necessarily a problem,
+ // but return as error for caller to decide, since they tried to load
+ return nil, fmt.Errorf("no plugin configs found")
+ }
+ return conf, nil
+}
+
+// Deprecated: This file format is no longer supported, use NetworkConfXXX and NetworkPluginXXX functions
+func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
+ return NetworkPluginConfFromBytes(bytes)
+}
+
+// Deprecated: This file format is no longer supported, use NetworkConfXXX and NetworkPluginXXX functions
+func ConfFromFile(filename string) (*NetworkConfig, error) {
+ bytes, err := os.ReadFile(filename)
+ if err != nil {
+ return nil, fmt.Errorf("error reading %s: %w", filename, err)
+ }
+ return ConfFromBytes(bytes)
+}
+
+func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
+ return NetworkConfFromBytes(bytes)
+}
+
+func ConfListFromFile(filename string) (*NetworkConfigList, error) {
+ return NetworkConfFromFile(filename)
+}
+
+// ConfFiles simply returns a slice of all files in the provided directory
+// with extensions matching the provided set.
+func ConfFiles(dir string, extensions []string) ([]string, error) {
+ // In part, adapted from rkt/networking/podenv.go#listFiles
+ files, err := os.ReadDir(dir)
+ switch {
+ case err == nil: // break
+ case os.IsNotExist(err):
+ // If folder not there, return no error - only return an
+ // error if we cannot read contents or there are no contents.
+ return nil, nil
+ default:
+ return nil, err
+ }
+
+ confFiles := []string{}
+ for _, f := range files {
+ if f.IsDir() {
+ continue
+ }
+ fileExt := filepath.Ext(f.Name())
+ for _, ext := range extensions {
+ if fileExt == ext {
+ confFiles = append(confFiles, filepath.Join(dir, f.Name()))
+ }
+ }
+ }
+ return confFiles, nil
+}
+
+// Deprecated: This file format is no longer supported, use NetworkConfXXX and NetworkPluginXXX functions
+func LoadConf(dir, name string) (*NetworkConfig, error) {
+ files, err := ConfFiles(dir, []string{".conf", ".json"})
+ switch {
+ case err != nil:
+ return nil, err
+ case len(files) == 0:
+ return nil, NoConfigsFoundError{Dir: dir}
+ }
+ sort.Strings(files)
+
+ for _, confFile := range files {
+ conf, err := ConfFromFile(confFile)
+ if err != nil {
+ return nil, err
+ }
+ if conf.Network.Name == name {
+ return conf, nil
+ }
+ }
+ return nil, NotFoundError{dir, name}
+}
+
+func LoadConfList(dir, name string) (*NetworkConfigList, error) {
+ return LoadNetworkConf(dir, name)
+}
+
+// LoadNetworkConf looks at all the network configs in a given dir,
+// loads and parses them all, and returns the first one with an extension of `.conf`
+// that matches the provided network name predicate.
+func LoadNetworkConf(dir, name string) (*NetworkConfigList, error) {
+ // TODO this .conflist/.conf extension thing is confusing and inexact
+ // for implementors. We should pick one extension for everything and stick with it.
+ files, err := ConfFiles(dir, []string{".conflist"})
+ if err != nil {
+ return nil, err
+ }
+ sort.Strings(files)
+
+ for _, confFile := range files {
+ conf, err := NetworkConfFromFile(confFile)
+ if err != nil {
+ return nil, err
+ }
+ if conf.Name == name {
+ return conf, nil
+ }
+ }
+
+ // Deprecated: Try and load a network configuration file (instead of list)
+ // from the same name, then upconvert.
+ singleConf, err := LoadConf(dir, name)
+ if err != nil {
+ // A little extra logic so the error makes sense
+ var ncfErr NoConfigsFoundError
+ if len(files) != 0 && errors.As(err, &ncfErr) {
+ // Config lists found but no config files found
+ return nil, NotFoundError{dir, name}
+ }
+
+ return nil, err
+ }
+ return ConfListFromConf(singleConf)
+}
+
+// InjectConf takes a PluginConfig and inserts additional values into it, ensuring the result is serializable.
+func InjectConf(original *PluginConfig, newValues map[string]interface{}) (*PluginConfig, error) {
+ config := make(map[string]interface{})
+ err := json.Unmarshal(original.Bytes, &config)
+ if err != nil {
+ return nil, fmt.Errorf("unmarshal existing network bytes: %w", err)
+ }
+
+ for key, value := range newValues {
+ if key == "" {
+ return nil, fmt.Errorf("keys cannot be empty")
+ }
+
+ if value == nil {
+ return nil, fmt.Errorf("key '%s' value must not be nil", key)
+ }
+
+ config[key] = value
+ }
+
+ newBytes, err := json.Marshal(config)
+ if err != nil {
+ return nil, err
+ }
+
+ return NetworkPluginConfFromBytes(newBytes)
+}
+
+// ConfListFromConf "upconverts" a network config in to a NetworkConfigList,
+// with the single network as the only entry in the list.
+//
+// Deprecated: Non-conflist file formats are unsupported, use NetworkConfXXX and NetworkPluginXXX functions
+func ConfListFromConf(original *PluginConfig) (*NetworkConfigList, error) {
+ // Re-deserialize the config's json, then make a raw map configlist.
+ // This may seem a bit strange, but it's to make the Bytes fields
+ // actually make sense. Otherwise, the generated json is littered with
+ // golang default values.
+
+ rawConfig := make(map[string]interface{})
+ if err := json.Unmarshal(original.Bytes, &rawConfig); err != nil {
+ return nil, err
+ }
+
+ rawConfigList := map[string]interface{}{
+ "name": original.Network.Name,
+ "cniVersion": original.Network.CNIVersion,
+ "plugins": []interface{}{rawConfig},
+ }
+
+ b, err := json.Marshal(rawConfigList)
+ if err != nil {
+ return nil, err
+ }
+ return ConfListFromBytes(b)
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go
new file mode 100644
index 00000000..3cdb4bc8
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go
@@ -0,0 +1,128 @@
+// Copyright 2015 CNI authors
+//
+// 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 invoke
+
+import (
+ "fmt"
+ "os"
+ "strings"
+)
+
+type CNIArgs interface {
+ // For use with os/exec; i.e., return nil to inherit the
+ // environment from this process
+ // For use in delegation; inherit the environment from this
+ // process and allow overrides
+ AsEnv() []string
+}
+
+type inherited struct{}
+
+var inheritArgsFromEnv inherited
+
+func (*inherited) AsEnv() []string {
+ return nil
+}
+
+func ArgsFromEnv() CNIArgs {
+ return &inheritArgsFromEnv
+}
+
+type Args struct {
+ Command string
+ ContainerID string
+ NetNS string
+ PluginArgs [][2]string
+ PluginArgsStr string
+ IfName string
+ Path string
+}
+
+// Args implements the CNIArgs interface
+var _ CNIArgs = &Args{}
+
+func (args *Args) AsEnv() []string {
+ env := os.Environ()
+ pluginArgsStr := args.PluginArgsStr
+ if pluginArgsStr == "" {
+ pluginArgsStr = stringify(args.PluginArgs)
+ }
+
+ // Duplicated values which come first will be overridden, so we must put the
+ // custom values in the end to avoid being overridden by the process environments.
+ env = append(env,
+ "CNI_COMMAND="+args.Command,
+ "CNI_CONTAINERID="+args.ContainerID,
+ "CNI_NETNS="+args.NetNS,
+ "CNI_ARGS="+pluginArgsStr,
+ "CNI_IFNAME="+args.IfName,
+ "CNI_PATH="+args.Path,
+ )
+ return dedupEnv(env)
+}
+
+// taken from rkt/networking/net_plugin.go
+func stringify(pluginArgs [][2]string) string {
+ entries := make([]string, len(pluginArgs))
+
+ for i, kv := range pluginArgs {
+ entries[i] = strings.Join(kv[:], "=")
+ }
+
+ return strings.Join(entries, ";")
+}
+
+// DelegateArgs implements the CNIArgs interface
+// used for delegation to inherit from environments
+// and allow some overrides like CNI_COMMAND
+var _ CNIArgs = &DelegateArgs{}
+
+type DelegateArgs struct {
+ Command string
+}
+
+func (d *DelegateArgs) AsEnv() []string {
+ env := os.Environ()
+
+ // The custom values should come in the end to override the existing
+ // process environment of the same key.
+ env = append(env,
+ "CNI_COMMAND="+d.Command,
+ )
+ return dedupEnv(env)
+}
+
+// dedupEnv returns a copy of env with any duplicates removed, in favor of later values.
+// Items not of the normal environment "key=value" form are preserved unchanged.
+func dedupEnv(env []string) []string {
+ out := make([]string, 0, len(env))
+ envMap := map[string]string{}
+
+ for _, kv := range env {
+ // find the first "=" in environment, if not, just keep it
+ eq := strings.Index(kv, "=")
+ if eq < 0 {
+ out = append(out, kv)
+ continue
+ }
+ envMap[kv[:eq]] = kv[eq+1:]
+ }
+
+ for k, v := range envMap {
+ out = append(out, fmt.Sprintf("%s=%s", k, v))
+ }
+
+ return out
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go
new file mode 100644
index 00000000..c8b548e7
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go
@@ -0,0 +1,89 @@
+// Copyright 2016 CNI authors
+//
+// 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 invoke
+
+import (
+ "context"
+ "os"
+ "path/filepath"
+
+ "github.com/containernetworking/cni/pkg/types"
+)
+
+func delegateCommon(delegatePlugin string, exec Exec) (string, Exec, error) {
+ if exec == nil {
+ exec = defaultExec
+ }
+
+ paths := filepath.SplitList(os.Getenv("CNI_PATH"))
+ pluginPath, err := exec.FindInPath(delegatePlugin, paths)
+ if err != nil {
+ return "", nil, err
+ }
+
+ return pluginPath, exec, nil
+}
+
+// DelegateAdd calls the given delegate plugin with the CNI ADD action and
+// JSON configuration
+func DelegateAdd(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) {
+ pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
+ if err != nil {
+ return nil, err
+ }
+
+ // DelegateAdd will override the original "CNI_COMMAND" env from process with ADD
+ return ExecPluginWithResult(ctx, pluginPath, netconf, delegateArgs("ADD"), realExec)
+}
+
+// DelegateCheck calls the given delegate plugin with the CNI CHECK action and
+// JSON configuration
+func DelegateCheck(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
+ return delegateNoResult(ctx, delegatePlugin, netconf, exec, "CHECK")
+}
+
+func delegateNoResult(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec, verb string) error {
+ pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
+ if err != nil {
+ return err
+ }
+
+ return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs(verb), realExec)
+}
+
+// DelegateDel calls the given delegate plugin with the CNI DEL action and
+// JSON configuration
+func DelegateDel(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
+ return delegateNoResult(ctx, delegatePlugin, netconf, exec, "DEL")
+}
+
+// DelegateStatus calls the given delegate plugin with the CNI STATUS action and
+// JSON configuration
+func DelegateStatus(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
+ return delegateNoResult(ctx, delegatePlugin, netconf, exec, "STATUS")
+}
+
+// DelegateGC calls the given delegate plugin with the CNI GC action and
+// JSON configuration
+func DelegateGC(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
+ return delegateNoResult(ctx, delegatePlugin, netconf, exec, "GC")
+}
+
+// return CNIArgs used by delegation
+func delegateArgs(action string) *DelegateArgs {
+ return &DelegateArgs{
+ Command: action,
+ }
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go b/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go
new file mode 100644
index 00000000..a5e015fc
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go
@@ -0,0 +1,187 @@
+// Copyright 2015 CNI authors
+//
+// 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 invoke
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "os"
+
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/create"
+ "github.com/containernetworking/cni/pkg/version"
+)
+
+// Exec is an interface encapsulates all operations that deal with finding
+// and executing a CNI plugin. Tests may provide a fake implementation
+// to avoid writing fake plugins to temporary directories during the test.
+type Exec interface {
+ ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error)
+ FindInPath(plugin string, paths []string) (string, error)
+ Decode(jsonBytes []byte) (version.PluginInfo, error)
+}
+
+// Plugin must return result in same version as specified in netconf; but
+// for backwards compatibility reasons if the result version is empty use
+// config version (rather than technically correct 0.1.0).
+// https://github.com/containernetworking/cni/issues/895
+func fixupResultVersion(netconf, result []byte) (string, []byte, error) {
+ versionDecoder := &version.ConfigDecoder{}
+ confVersion, err := versionDecoder.Decode(netconf)
+ if err != nil {
+ return "", nil, err
+ }
+
+ var rawResult map[string]interface{}
+ if err := json.Unmarshal(result, &rawResult); err != nil {
+ return "", nil, fmt.Errorf("failed to unmarshal raw result: %w", err)
+ }
+
+ // plugin output of "null" is successfully unmarshalled, but results in a nil
+ // map which causes a panic when the confVersion is assigned below.
+ if rawResult == nil {
+ rawResult = make(map[string]interface{})
+ }
+
+ // Manually decode Result version; we need to know whether its cniVersion
+ // is empty, while built-in decoders (correctly) substitute 0.1.0 for an
+ // empty version per the CNI spec.
+ if resultVerRaw, ok := rawResult["cniVersion"]; ok {
+ resultVer, ok := resultVerRaw.(string)
+ if ok && resultVer != "" {
+ return resultVer, result, nil
+ }
+ }
+
+ // If the cniVersion is not present or empty, assume the result is
+ // the same CNI spec version as the config
+ rawResult["cniVersion"] = confVersion
+ newBytes, err := json.Marshal(rawResult)
+ if err != nil {
+ return "", nil, fmt.Errorf("failed to remarshal fixed result: %w", err)
+ }
+
+ return confVersion, newBytes, nil
+}
+
+// For example, a testcase could pass an instance of the following fakeExec
+// object to ExecPluginWithResult() to verify the incoming stdin and environment
+// and provide a tailored response:
+//
+// import (
+// "encoding/json"
+// "path"
+// "strings"
+// )
+//
+// type fakeExec struct {
+// version.PluginDecoder
+// }
+//
+// func (f *fakeExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
+// net := &types.NetConf{}
+// err := json.Unmarshal(stdinData, net)
+// if err != nil {
+// return nil, fmt.Errorf("failed to unmarshal configuration: %v", err)
+// }
+// pluginName := path.Base(pluginPath)
+// if pluginName != net.Type {
+// return nil, fmt.Errorf("plugin name %q did not match config type %q", pluginName, net.Type)
+// }
+// for _, e := range environ {
+// // Check environment for forced failure request
+// parts := strings.Split(e, "=")
+// if len(parts) > 0 && parts[0] == "FAIL" {
+// return nil, fmt.Errorf("failed to execute plugin %s", pluginName)
+// }
+// }
+// return []byte("{\"CNIVersion\":\"0.4.0\"}"), nil
+// }
+//
+// func (f *fakeExec) FindInPath(plugin string, paths []string) (string, error) {
+// if len(paths) > 0 {
+// return path.Join(paths[0], plugin), nil
+// }
+// return "", fmt.Errorf("failed to find plugin %s in paths %v", plugin, paths)
+// }
+
+func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) {
+ if exec == nil {
+ exec = defaultExec
+ }
+
+ stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv())
+ if err != nil {
+ return nil, err
+ }
+
+ resultVersion, fixedBytes, err := fixupResultVersion(netconf, stdoutBytes)
+ if err != nil {
+ return nil, err
+ }
+
+ return create.Create(resultVersion, fixedBytes)
+}
+
+func ExecPluginWithoutResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) error {
+ if exec == nil {
+ exec = defaultExec
+ }
+ _, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv())
+ return err
+}
+
+// GetVersionInfo returns the version information available about the plugin.
+// For recent-enough plugins, it uses the information returned by the VERSION
+// command. For older plugins which do not recognize that command, it reports
+// version 0.1.0
+func GetVersionInfo(ctx context.Context, pluginPath string, exec Exec) (version.PluginInfo, error) {
+ if exec == nil {
+ exec = defaultExec
+ }
+ args := &Args{
+ Command: "VERSION",
+
+ // set fake values required by plugins built against an older version of skel
+ NetNS: "dummy",
+ IfName: "dummy",
+ Path: "dummy",
+ }
+ stdin := []byte(fmt.Sprintf(`{"cniVersion":%q}`, version.Current()))
+ stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, stdin, args.AsEnv())
+ if err != nil {
+ if err.Error() == "unknown CNI_COMMAND: VERSION" {
+ return version.PluginSupports("0.1.0"), nil
+ }
+ return nil, err
+ }
+
+ return exec.Decode(stdoutBytes)
+}
+
+// DefaultExec is an object that implements the Exec interface which looks
+// for and executes plugins from disk.
+type DefaultExec struct {
+ *RawExec
+ version.PluginDecoder
+}
+
+// DefaultExec implements the Exec interface
+var _ Exec = &DefaultExec{}
+
+var defaultExec = &DefaultExec{
+ RawExec: &RawExec{Stderr: os.Stderr},
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/find.go b/vendor/github.com/containernetworking/cni/pkg/invoke/find.go
new file mode 100644
index 00000000..e62029eb
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/find.go
@@ -0,0 +1,48 @@
+// Copyright 2015 CNI authors
+//
+// 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 invoke
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+// FindInPath returns the full path of the plugin by searching in the provided path
+func FindInPath(plugin string, paths []string) (string, error) {
+ if plugin == "" {
+ return "", fmt.Errorf("no plugin name provided")
+ }
+
+ if strings.ContainsRune(plugin, os.PathSeparator) {
+ return "", fmt.Errorf("invalid plugin name: %s", plugin)
+ }
+
+ if len(paths) == 0 {
+ return "", fmt.Errorf("no paths provided")
+ }
+
+ for _, path := range paths {
+ for _, fe := range ExecutableFileExtensions {
+ fullpath := filepath.Join(path, plugin) + fe
+ if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() {
+ return fullpath, nil
+ }
+ }
+ }
+
+ return "", fmt.Errorf("failed to find plugin %q in path %s", plugin, paths)
+}
diff --git a/vendor/github.com/petermattis/goid/goid_go1.5_amd64.s b/vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go
similarity index 50%
rename from vendor/github.com/petermattis/goid/goid_go1.5_amd64.s
rename to vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go
index 416665dd..ed0999bd 100644
--- a/vendor/github.com/petermattis/goid/goid_go1.5_amd64.s
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go
@@ -1,4 +1,4 @@
-// Copyright 2016 Peter Mattis.
+// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -8,22 +8,14 @@
//
// 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. See the AUTHORS file
-// for names of contributors.
+// 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.
-// Assembly to mimic runtime.getg.
+//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
-// +build amd64 amd64p32
-// +build gc,go1.5
+package invoke
-#include "go_asm.h"
-#include "textflag.h"
-
-// func Get() int64
-TEXT ·Get(SB),NOSPLIT,$0-8
- MOVQ (TLS), R14
- MOVQ g_goid(R14), R13
- MOVQ R13, ret+0(FP)
- RET
+// Valid file extensions for plugin executables.
+var ExecutableFileExtensions = []string{""}
diff --git a/vendor/github.com/petermattis/goid/goid_go1.5_amd64.go b/vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go
similarity index 61%
rename from vendor/github.com/petermattis/goid/goid_go1.5_amd64.go
rename to vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go
index 269abb3f..7665125b 100644
--- a/vendor/github.com/petermattis/goid/goid_go1.5_amd64.go
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go
@@ -1,4 +1,4 @@
-// Copyright 2016 Peter Mattis.
+// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -8,14 +8,11 @@
//
// 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. See the AUTHORS file
-// for names of contributors.
+// 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.
-// +build amd64 amd64p32
-// +build gc,go1.5
+package invoke
-package goid
-
-func Get() int64
+// Valid file extensions for plugin executables.
+var ExecutableFileExtensions = []string{".exe", ""}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go b/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
new file mode 100644
index 00000000..5ab5cc88
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
@@ -0,0 +1,88 @@
+// Copyright 2016 CNI authors
+//
+// 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 invoke
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os/exec"
+ "strings"
+ "time"
+
+ "github.com/containernetworking/cni/pkg/types"
+)
+
+type RawExec struct {
+ Stderr io.Writer
+}
+
+func (e *RawExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
+ stdout := &bytes.Buffer{}
+ stderr := &bytes.Buffer{}
+ c := exec.CommandContext(ctx, pluginPath)
+ c.Env = environ
+ c.Stdin = bytes.NewBuffer(stdinData)
+ c.Stdout = stdout
+ c.Stderr = stderr
+
+ // Retry the command on "text file busy" errors
+ for i := 0; i <= 5; i++ {
+ err := c.Run()
+
+ // Command succeeded
+ if err == nil {
+ break
+ }
+
+ // If the plugin is currently about to be written, then we wait a
+ // second and try it again
+ if strings.Contains(err.Error(), "text file busy") {
+ time.Sleep(time.Second)
+ continue
+ }
+
+ // All other errors except than the busy text file
+ return nil, e.pluginErr(err, stdout.Bytes(), stderr.Bytes())
+ }
+
+ // Copy stderr to caller's buffer in case plugin printed to both
+ // stdout and stderr for some reason. Ignore failures as stderr is
+ // only informational.
+ if e.Stderr != nil && stderr.Len() > 0 {
+ _, _ = stderr.WriteTo(e.Stderr)
+ }
+ return stdout.Bytes(), nil
+}
+
+func (e *RawExec) pluginErr(err error, stdout, stderr []byte) error {
+ emsg := types.Error{}
+ if len(stdout) == 0 {
+ if len(stderr) == 0 {
+ emsg.Msg = fmt.Sprintf("netplugin failed with no error message: %v", err)
+ } else {
+ emsg.Msg = fmt.Sprintf("netplugin failed: %q", string(stderr))
+ }
+ } else if perr := json.Unmarshal(stdout, &emsg); perr != nil {
+ emsg.Msg = fmt.Sprintf("netplugin failed but error parsing its diagnostic message %q: %v", string(stdout), perr)
+ }
+ return &emsg
+}
+
+func (e *RawExec) FindInPath(plugin string, paths []string) (string, error) {
+ return FindInPath(plugin, paths)
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/020/types.go b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go
new file mode 100644
index 00000000..99b151ff
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go
@@ -0,0 +1,189 @@
+// Copyright 2016 CNI authors
+//
+// 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 types020
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net"
+ "os"
+
+ "github.com/containernetworking/cni/pkg/types"
+ convert "github.com/containernetworking/cni/pkg/types/internal"
+)
+
+const ImplementedSpecVersion string = "0.2.0"
+
+var supportedVersions = []string{"", "0.1.0", ImplementedSpecVersion}
+
+// Register converters for all versions less than the implemented spec version
+func init() {
+ convert.RegisterConverter("0.1.0", []string{ImplementedSpecVersion}, convertFrom010)
+ convert.RegisterConverter(ImplementedSpecVersion, []string{"0.1.0"}, convertTo010)
+
+ // Creator
+ convert.RegisterCreator(supportedVersions, NewResult)
+}
+
+// Compatibility types for CNI version 0.1.0 and 0.2.0
+
+// NewResult creates a new Result object from JSON data. The JSON data
+// must be compatible with the CNI versions implemented by this type.
+func NewResult(data []byte) (types.Result, error) {
+ result := &Result{}
+ if err := json.Unmarshal(data, result); err != nil {
+ return nil, err
+ }
+ for _, v := range supportedVersions {
+ if result.CNIVersion == v {
+ if result.CNIVersion == "" {
+ result.CNIVersion = "0.1.0"
+ }
+ return result, nil
+ }
+ }
+ return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q",
+ supportedVersions, result.CNIVersion)
+}
+
+// GetResult converts the given Result object to the ImplementedSpecVersion
+// and returns the concrete type or an error
+func GetResult(r types.Result) (*Result, error) {
+ result020, err := convert.Convert(r, ImplementedSpecVersion)
+ if err != nil {
+ return nil, err
+ }
+ result, ok := result020.(*Result)
+ if !ok {
+ return nil, fmt.Errorf("failed to convert result")
+ }
+ return result, nil
+}
+
+func convertFrom010(from types.Result, toVersion string) (types.Result, error) {
+ if toVersion != "0.2.0" {
+ panic("only converts to version 0.2.0")
+ }
+ fromResult := from.(*Result)
+ return &Result{
+ CNIVersion: ImplementedSpecVersion,
+ IP4: fromResult.IP4.Copy(),
+ IP6: fromResult.IP6.Copy(),
+ DNS: *fromResult.DNS.Copy(),
+ }, nil
+}
+
+func convertTo010(from types.Result, toVersion string) (types.Result, error) {
+ if toVersion != "0.1.0" {
+ panic("only converts to version 0.1.0")
+ }
+ fromResult := from.(*Result)
+ return &Result{
+ CNIVersion: "0.1.0",
+ IP4: fromResult.IP4.Copy(),
+ IP6: fromResult.IP6.Copy(),
+ DNS: *fromResult.DNS.Copy(),
+ }, nil
+}
+
+// Result is what gets returned from the plugin (via stdout) to the caller
+type Result struct {
+ CNIVersion string `json:"cniVersion,omitempty"`
+ IP4 *IPConfig `json:"ip4,omitempty"`
+ IP6 *IPConfig `json:"ip6,omitempty"`
+ DNS types.DNS `json:"dns,omitempty"`
+}
+
+func (r *Result) Version() string {
+ return r.CNIVersion
+}
+
+func (r *Result) GetAsVersion(version string) (types.Result, error) {
+ // If the creator of the result did not set the CNIVersion, assume it
+ // should be the highest spec version implemented by this Result
+ if r.CNIVersion == "" {
+ r.CNIVersion = ImplementedSpecVersion
+ }
+ return convert.Convert(r, version)
+}
+
+func (r *Result) Print() error {
+ return r.PrintTo(os.Stdout)
+}
+
+func (r *Result) PrintTo(writer io.Writer) error {
+ data, err := json.MarshalIndent(r, "", " ")
+ if err != nil {
+ return err
+ }
+ _, err = writer.Write(data)
+ return err
+}
+
+// IPConfig contains values necessary to configure an interface
+type IPConfig struct {
+ IP net.IPNet
+ Gateway net.IP
+ Routes []types.Route
+}
+
+func (i *IPConfig) Copy() *IPConfig {
+ if i == nil {
+ return nil
+ }
+
+ var routes []types.Route
+ for _, fromRoute := range i.Routes {
+ routes = append(routes, *fromRoute.Copy())
+ }
+ return &IPConfig{
+ IP: i.IP,
+ Gateway: i.Gateway,
+ Routes: routes,
+ }
+}
+
+// net.IPNet is not JSON (un)marshallable so this duality is needed
+// for our custom IPNet type
+
+// JSON (un)marshallable types
+type ipConfig struct {
+ IP types.IPNet `json:"ip"`
+ Gateway net.IP `json:"gateway,omitempty"`
+ Routes []types.Route `json:"routes,omitempty"`
+}
+
+func (c *IPConfig) MarshalJSON() ([]byte, error) {
+ ipc := ipConfig{
+ IP: types.IPNet(c.IP),
+ Gateway: c.Gateway,
+ Routes: c.Routes,
+ }
+
+ return json.Marshal(ipc)
+}
+
+func (c *IPConfig) UnmarshalJSON(data []byte) error {
+ ipc := ipConfig{}
+ if err := json.Unmarshal(data, &ipc); err != nil {
+ return err
+ }
+
+ c.IP = net.IPNet(ipc.IP)
+ c.Gateway = ipc.Gateway
+ c.Routes = ipc.Routes
+ return nil
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/040/types.go b/vendor/github.com/containernetworking/cni/pkg/types/040/types.go
new file mode 100644
index 00000000..3633b0ea
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/types/040/types.go
@@ -0,0 +1,306 @@
+// Copyright 2016 CNI authors
+//
+// 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 types040
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net"
+ "os"
+
+ "github.com/containernetworking/cni/pkg/types"
+ types020 "github.com/containernetworking/cni/pkg/types/020"
+ convert "github.com/containernetworking/cni/pkg/types/internal"
+)
+
+const ImplementedSpecVersion string = "0.4.0"
+
+var supportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion}
+
+// Register converters for all versions less than the implemented spec version
+func init() {
+ // Up-converters
+ convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x)
+ convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x)
+ convert.RegisterConverter("0.3.0", supportedVersions, convertInternal)
+ convert.RegisterConverter("0.3.1", supportedVersions, convertInternal)
+
+ // Down-converters
+ convert.RegisterConverter("0.4.0", []string{"0.3.0", "0.3.1"}, convertInternal)
+ convert.RegisterConverter("0.4.0", []string{"0.1.0", "0.2.0"}, convertTo02x)
+ convert.RegisterConverter("0.3.1", []string{"0.1.0", "0.2.0"}, convertTo02x)
+ convert.RegisterConverter("0.3.0", []string{"0.1.0", "0.2.0"}, convertTo02x)
+
+ // Creator
+ convert.RegisterCreator(supportedVersions, NewResult)
+}
+
+func NewResult(data []byte) (types.Result, error) {
+ result := &Result{}
+ if err := json.Unmarshal(data, result); err != nil {
+ return nil, err
+ }
+ for _, v := range supportedVersions {
+ if result.CNIVersion == v {
+ return result, nil
+ }
+ }
+ return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q",
+ supportedVersions, result.CNIVersion)
+}
+
+func GetResult(r types.Result) (*Result, error) {
+ resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion)
+ if err != nil {
+ return nil, err
+ }
+ result, ok := resultCurrent.(*Result)
+ if !ok {
+ return nil, fmt.Errorf("failed to convert result")
+ }
+ return result, nil
+}
+
+func NewResultFromResult(result types.Result) (*Result, error) {
+ newResult, err := convert.Convert(result, ImplementedSpecVersion)
+ if err != nil {
+ return nil, err
+ }
+ return newResult.(*Result), nil
+}
+
+// Result is what gets returned from the plugin (via stdout) to the caller
+type Result struct {
+ CNIVersion string `json:"cniVersion,omitempty"`
+ Interfaces []*Interface `json:"interfaces,omitempty"`
+ IPs []*IPConfig `json:"ips,omitempty"`
+ Routes []*types.Route `json:"routes,omitempty"`
+ DNS types.DNS `json:"dns,omitempty"`
+}
+
+func convert020IPConfig(from *types020.IPConfig, ipVersion string) *IPConfig {
+ return &IPConfig{
+ Version: ipVersion,
+ Address: from.IP,
+ Gateway: from.Gateway,
+ }
+}
+
+func convertFrom02x(from types.Result, toVersion string) (types.Result, error) {
+ fromResult := from.(*types020.Result)
+ toResult := &Result{
+ CNIVersion: toVersion,
+ DNS: *fromResult.DNS.Copy(),
+ Routes: []*types.Route{},
+ }
+ if fromResult.IP4 != nil {
+ toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP4, "4"))
+ for _, fromRoute := range fromResult.IP4.Routes {
+ toResult.Routes = append(toResult.Routes, fromRoute.Copy())
+ }
+ }
+
+ if fromResult.IP6 != nil {
+ toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP6, "6"))
+ for _, fromRoute := range fromResult.IP6.Routes {
+ toResult.Routes = append(toResult.Routes, fromRoute.Copy())
+ }
+ }
+
+ return toResult, nil
+}
+
+func convertInternal(from types.Result, toVersion string) (types.Result, error) {
+ fromResult := from.(*Result)
+ toResult := &Result{
+ CNIVersion: toVersion,
+ DNS: *fromResult.DNS.Copy(),
+ Routes: []*types.Route{},
+ }
+ for _, fromIntf := range fromResult.Interfaces {
+ toResult.Interfaces = append(toResult.Interfaces, fromIntf.Copy())
+ }
+ for _, fromIPC := range fromResult.IPs {
+ toResult.IPs = append(toResult.IPs, fromIPC.Copy())
+ }
+ for _, fromRoute := range fromResult.Routes {
+ toResult.Routes = append(toResult.Routes, fromRoute.Copy())
+ }
+ return toResult, nil
+}
+
+func convertTo02x(from types.Result, toVersion string) (types.Result, error) {
+ fromResult := from.(*Result)
+ toResult := &types020.Result{
+ CNIVersion: toVersion,
+ DNS: *fromResult.DNS.Copy(),
+ }
+
+ for _, fromIP := range fromResult.IPs {
+ // Only convert the first IP address of each version as 0.2.0
+ // and earlier cannot handle multiple IP addresses
+ if fromIP.Version == "4" && toResult.IP4 == nil {
+ toResult.IP4 = &types020.IPConfig{
+ IP: fromIP.Address,
+ Gateway: fromIP.Gateway,
+ }
+ } else if fromIP.Version == "6" && toResult.IP6 == nil {
+ toResult.IP6 = &types020.IPConfig{
+ IP: fromIP.Address,
+ Gateway: fromIP.Gateway,
+ }
+ }
+ if toResult.IP4 != nil && toResult.IP6 != nil {
+ break
+ }
+ }
+
+ for _, fromRoute := range fromResult.Routes {
+ is4 := fromRoute.Dst.IP.To4() != nil
+ if is4 && toResult.IP4 != nil {
+ toResult.IP4.Routes = append(toResult.IP4.Routes, types.Route{
+ Dst: fromRoute.Dst,
+ GW: fromRoute.GW,
+ })
+ } else if !is4 && toResult.IP6 != nil {
+ toResult.IP6.Routes = append(toResult.IP6.Routes, types.Route{
+ Dst: fromRoute.Dst,
+ GW: fromRoute.GW,
+ })
+ }
+ }
+
+ // 0.2.0 and earlier require at least one IP address in the Result
+ if toResult.IP4 == nil && toResult.IP6 == nil {
+ return nil, fmt.Errorf("cannot convert: no valid IP addresses")
+ }
+
+ return toResult, nil
+}
+
+func (r *Result) Version() string {
+ return r.CNIVersion
+}
+
+func (r *Result) GetAsVersion(version string) (types.Result, error) {
+ // If the creator of the result did not set the CNIVersion, assume it
+ // should be the highest spec version implemented by this Result
+ if r.CNIVersion == "" {
+ r.CNIVersion = ImplementedSpecVersion
+ }
+ return convert.Convert(r, version)
+}
+
+func (r *Result) Print() error {
+ return r.PrintTo(os.Stdout)
+}
+
+func (r *Result) PrintTo(writer io.Writer) error {
+ data, err := json.MarshalIndent(r, "", " ")
+ if err != nil {
+ return err
+ }
+ _, err = writer.Write(data)
+ return err
+}
+
+// Interface contains values about the created interfaces
+type Interface struct {
+ Name string `json:"name"`
+ Mac string `json:"mac,omitempty"`
+ Sandbox string `json:"sandbox,omitempty"`
+}
+
+func (i *Interface) String() string {
+ return fmt.Sprintf("%+v", *i)
+}
+
+func (i *Interface) Copy() *Interface {
+ if i == nil {
+ return nil
+ }
+ newIntf := *i
+ return &newIntf
+}
+
+// Int returns a pointer to the int value passed in. Used to
+// set the IPConfig.Interface field.
+func Int(v int) *int {
+ return &v
+}
+
+// IPConfig contains values necessary to configure an IP address on an interface
+type IPConfig struct {
+ // IP version, either "4" or "6"
+ Version string
+ // Index into Result structs Interfaces list
+ Interface *int
+ Address net.IPNet
+ Gateway net.IP
+}
+
+func (i *IPConfig) String() string {
+ return fmt.Sprintf("%+v", *i)
+}
+
+func (i *IPConfig) Copy() *IPConfig {
+ if i == nil {
+ return nil
+ }
+
+ ipc := &IPConfig{
+ Version: i.Version,
+ Address: i.Address,
+ Gateway: i.Gateway,
+ }
+ if i.Interface != nil {
+ intf := *i.Interface
+ ipc.Interface = &intf
+ }
+ return ipc
+}
+
+// JSON (un)marshallable types
+type ipConfig struct {
+ Version string `json:"version"`
+ Interface *int `json:"interface,omitempty"`
+ Address types.IPNet `json:"address"`
+ Gateway net.IP `json:"gateway,omitempty"`
+}
+
+func (c *IPConfig) MarshalJSON() ([]byte, error) {
+ ipc := ipConfig{
+ Version: c.Version,
+ Interface: c.Interface,
+ Address: types.IPNet(c.Address),
+ Gateway: c.Gateway,
+ }
+
+ return json.Marshal(ipc)
+}
+
+func (c *IPConfig) UnmarshalJSON(data []byte) error {
+ ipc := ipConfig{}
+ if err := json.Unmarshal(data, &ipc); err != nil {
+ return err
+ }
+
+ c.Version = ipc.Version
+ c.Interface = ipc.Interface
+ c.Address = net.IPNet(ipc.Address)
+ c.Gateway = ipc.Gateway
+ return nil
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/100/types.go b/vendor/github.com/containernetworking/cni/pkg/types/100/types.go
new file mode 100644
index 00000000..f58b9120
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/types/100/types.go
@@ -0,0 +1,352 @@
+// Copyright 2016 CNI authors
+//
+// 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 types100
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net"
+ "os"
+
+ "github.com/containernetworking/cni/pkg/types"
+ types040 "github.com/containernetworking/cni/pkg/types/040"
+ convert "github.com/containernetworking/cni/pkg/types/internal"
+)
+
+// The types did not change between v1.0 and v1.1
+const ImplementedSpecVersion string = "1.1.0"
+
+var supportedVersions = []string{"1.0.0", "1.1.0"}
+
+// Register converters for all versions less than the implemented spec version
+func init() {
+ // Up-converters
+ convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x)
+ convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x)
+ convert.RegisterConverter("0.3.0", supportedVersions, convertFrom04x)
+ convert.RegisterConverter("0.3.1", supportedVersions, convertFrom04x)
+ convert.RegisterConverter("0.4.0", supportedVersions, convertFrom04x)
+ convert.RegisterConverter("1.0.0", []string{"1.1.0"}, convertFrom100)
+
+ // Down-converters
+ convert.RegisterConverter("1.0.0", []string{"0.3.0", "0.3.1", "0.4.0"}, convertTo04x)
+ convert.RegisterConverter("1.0.0", []string{"0.1.0", "0.2.0"}, convertTo02x)
+ convert.RegisterConverter("1.1.0", []string{"0.3.0", "0.3.1", "0.4.0"}, convertTo04x)
+ convert.RegisterConverter("1.1.0", []string{"0.1.0", "0.2.0"}, convertTo02x)
+ convert.RegisterConverter("1.1.0", []string{"1.0.0"}, convertFrom100)
+
+ // Creator
+ convert.RegisterCreator(supportedVersions, NewResult)
+}
+
+func NewResult(data []byte) (types.Result, error) {
+ result := &Result{}
+ if err := json.Unmarshal(data, result); err != nil {
+ return nil, err
+ }
+ for _, v := range supportedVersions {
+ if result.CNIVersion == v {
+ return result, nil
+ }
+ }
+ return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q",
+ supportedVersions, result.CNIVersion)
+}
+
+func GetResult(r types.Result) (*Result, error) {
+ resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion)
+ if err != nil {
+ return nil, err
+ }
+ result, ok := resultCurrent.(*Result)
+ if !ok {
+ return nil, fmt.Errorf("failed to convert result")
+ }
+ return result, nil
+}
+
+func NewResultFromResult(result types.Result) (*Result, error) {
+ newResult, err := convert.Convert(result, ImplementedSpecVersion)
+ if err != nil {
+ return nil, err
+ }
+ return newResult.(*Result), nil
+}
+
+// Result is what gets returned from the plugin (via stdout) to the caller
+type Result struct {
+ CNIVersion string `json:"cniVersion,omitempty"`
+ Interfaces []*Interface `json:"interfaces,omitempty"`
+ IPs []*IPConfig `json:"ips,omitempty"`
+ Routes []*types.Route `json:"routes,omitempty"`
+ DNS types.DNS `json:"dns,omitempty"`
+}
+
+// Note: DNS should be omit if DNS is empty but default Marshal function
+// will output empty structure hence need to write a Marshal function
+func (r *Result) MarshalJSON() ([]byte, error) {
+ // use type alias to escape recursion for json.Marshal() to MarshalJSON()
+ type fixObjType = Result
+
+ bytes, err := json.Marshal(fixObjType(*r)) //nolint:all
+ if err != nil {
+ return nil, err
+ }
+
+ fixupObj := make(map[string]interface{})
+ if err := json.Unmarshal(bytes, &fixupObj); err != nil {
+ return nil, err
+ }
+
+ if r.DNS.IsEmpty() {
+ delete(fixupObj, "dns")
+ }
+
+ return json.Marshal(fixupObj)
+}
+
+// convertFrom100 does nothing except set the version; the types are the same
+func convertFrom100(from types.Result, toVersion string) (types.Result, error) {
+ fromResult := from.(*Result)
+
+ result := &Result{
+ CNIVersion: toVersion,
+ Interfaces: fromResult.Interfaces,
+ IPs: fromResult.IPs,
+ Routes: fromResult.Routes,
+ DNS: fromResult.DNS,
+ }
+ return result, nil
+}
+
+func convertFrom02x(from types.Result, toVersion string) (types.Result, error) {
+ result040, err := convert.Convert(from, "0.4.0")
+ if err != nil {
+ return nil, err
+ }
+ result100, err := convertFrom04x(result040, toVersion)
+ if err != nil {
+ return nil, err
+ }
+ return result100, nil
+}
+
+func convertIPConfigFrom040(from *types040.IPConfig) *IPConfig {
+ to := &IPConfig{
+ Address: from.Address,
+ Gateway: from.Gateway,
+ }
+ if from.Interface != nil {
+ intf := *from.Interface
+ to.Interface = &intf
+ }
+ return to
+}
+
+func convertInterfaceFrom040(from *types040.Interface) *Interface {
+ return &Interface{
+ Name: from.Name,
+ Mac: from.Mac,
+ Sandbox: from.Sandbox,
+ }
+}
+
+func convertFrom04x(from types.Result, toVersion string) (types.Result, error) {
+ fromResult := from.(*types040.Result)
+ toResult := &Result{
+ CNIVersion: toVersion,
+ DNS: *fromResult.DNS.Copy(),
+ Routes: []*types.Route{},
+ }
+ for _, fromIntf := range fromResult.Interfaces {
+ toResult.Interfaces = append(toResult.Interfaces, convertInterfaceFrom040(fromIntf))
+ }
+ for _, fromIPC := range fromResult.IPs {
+ toResult.IPs = append(toResult.IPs, convertIPConfigFrom040(fromIPC))
+ }
+ for _, fromRoute := range fromResult.Routes {
+ toResult.Routes = append(toResult.Routes, fromRoute.Copy())
+ }
+ return toResult, nil
+}
+
+func convertIPConfigTo040(from *IPConfig) *types040.IPConfig {
+ version := "6"
+ if from.Address.IP.To4() != nil {
+ version = "4"
+ }
+ to := &types040.IPConfig{
+ Version: version,
+ Address: from.Address,
+ Gateway: from.Gateway,
+ }
+ if from.Interface != nil {
+ intf := *from.Interface
+ to.Interface = &intf
+ }
+ return to
+}
+
+func convertInterfaceTo040(from *Interface) *types040.Interface {
+ return &types040.Interface{
+ Name: from.Name,
+ Mac: from.Mac,
+ Sandbox: from.Sandbox,
+ }
+}
+
+func convertTo04x(from types.Result, toVersion string) (types.Result, error) {
+ fromResult := from.(*Result)
+ toResult := &types040.Result{
+ CNIVersion: toVersion,
+ DNS: *fromResult.DNS.Copy(),
+ Routes: []*types.Route{},
+ }
+ for _, fromIntf := range fromResult.Interfaces {
+ toResult.Interfaces = append(toResult.Interfaces, convertInterfaceTo040(fromIntf))
+ }
+ for _, fromIPC := range fromResult.IPs {
+ toResult.IPs = append(toResult.IPs, convertIPConfigTo040(fromIPC))
+ }
+ for _, fromRoute := range fromResult.Routes {
+ toResult.Routes = append(toResult.Routes, fromRoute.Copy())
+ }
+ return toResult, nil
+}
+
+func convertTo02x(from types.Result, toVersion string) (types.Result, error) {
+ // First convert to 0.4.0
+ result040, err := convertTo04x(from, "0.4.0")
+ if err != nil {
+ return nil, err
+ }
+ result02x, err := convert.Convert(result040, toVersion)
+ if err != nil {
+ return nil, err
+ }
+ return result02x, nil
+}
+
+func (r *Result) Version() string {
+ return r.CNIVersion
+}
+
+func (r *Result) GetAsVersion(version string) (types.Result, error) {
+ // If the creator of the result did not set the CNIVersion, assume it
+ // should be the highest spec version implemented by this Result
+ if r.CNIVersion == "" {
+ r.CNIVersion = ImplementedSpecVersion
+ }
+ return convert.Convert(r, version)
+}
+
+func (r *Result) Print() error {
+ return r.PrintTo(os.Stdout)
+}
+
+func (r *Result) PrintTo(writer io.Writer) error {
+ data, err := json.MarshalIndent(r, "", " ")
+ if err != nil {
+ return err
+ }
+ _, err = writer.Write(data)
+ return err
+}
+
+// Interface contains values about the created interfaces
+type Interface struct {
+ Name string `json:"name"`
+ Mac string `json:"mac,omitempty"`
+ Mtu int `json:"mtu,omitempty"`
+ Sandbox string `json:"sandbox,omitempty"`
+ SocketPath string `json:"socketPath,omitempty"`
+ PciID string `json:"pciID,omitempty"`
+}
+
+func (i *Interface) String() string {
+ return fmt.Sprintf("%+v", *i)
+}
+
+func (i *Interface) Copy() *Interface {
+ if i == nil {
+ return nil
+ }
+ newIntf := *i
+ return &newIntf
+}
+
+// Int returns a pointer to the int value passed in. Used to
+// set the IPConfig.Interface field.
+func Int(v int) *int {
+ return &v
+}
+
+// IPConfig contains values necessary to configure an IP address on an interface
+type IPConfig struct {
+ // Index into Result structs Interfaces list
+ Interface *int
+ Address net.IPNet
+ Gateway net.IP
+}
+
+func (i *IPConfig) String() string {
+ return fmt.Sprintf("%+v", *i)
+}
+
+func (i *IPConfig) Copy() *IPConfig {
+ if i == nil {
+ return nil
+ }
+
+ ipc := &IPConfig{
+ Address: i.Address,
+ Gateway: i.Gateway,
+ }
+ if i.Interface != nil {
+ intf := *i.Interface
+ ipc.Interface = &intf
+ }
+ return ipc
+}
+
+// JSON (un)marshallable types
+type ipConfig struct {
+ Interface *int `json:"interface,omitempty"`
+ Address types.IPNet `json:"address"`
+ Gateway net.IP `json:"gateway,omitempty"`
+}
+
+func (c *IPConfig) MarshalJSON() ([]byte, error) {
+ ipc := ipConfig{
+ Interface: c.Interface,
+ Address: types.IPNet(c.Address),
+ Gateway: c.Gateway,
+ }
+
+ return json.Marshal(ipc)
+}
+
+func (c *IPConfig) UnmarshalJSON(data []byte) error {
+ ipc := ipConfig{}
+ if err := json.Unmarshal(data, &ipc); err != nil {
+ return err
+ }
+
+ c.Interface = ipc.Interface
+ c.Address = net.IPNet(ipc.Address)
+ c.Gateway = ipc.Gateway
+ return nil
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/args.go b/vendor/github.com/containernetworking/cni/pkg/types/args.go
new file mode 100644
index 00000000..68a602bf
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/types/args.go
@@ -0,0 +1,122 @@
+// Copyright 2015 CNI authors
+//
+// 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 types
+
+import (
+ "encoding"
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// UnmarshallableBool typedef for builtin bool
+// because builtin type's methods can't be declared
+type UnmarshallableBool bool
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+// Returns boolean true if the string is "1" or "true" or "True"
+// Returns boolean false if the string is "0" or "false" or "False”
+func (b *UnmarshallableBool) UnmarshalText(data []byte) error {
+ s := strings.ToLower(string(data))
+ switch s {
+ case "1", "true":
+ *b = true
+ case "0", "false":
+ *b = false
+ default:
+ return fmt.Errorf("boolean unmarshal error: invalid input %s", s)
+ }
+ return nil
+}
+
+// UnmarshallableString typedef for builtin string
+type UnmarshallableString string
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+// Returns the string
+func (s *UnmarshallableString) UnmarshalText(data []byte) error {
+ *s = UnmarshallableString(data)
+ return nil
+}
+
+// CommonArgs contains the IgnoreUnknown argument
+// and must be embedded by all Arg structs
+type CommonArgs struct {
+ IgnoreUnknown UnmarshallableBool `json:"ignoreunknown,omitempty"`
+}
+
+// GetKeyField is a helper function to receive Values
+// Values that represent a pointer to a struct
+func GetKeyField(keyString string, v reflect.Value) reflect.Value {
+ return v.Elem().FieldByName(keyString)
+}
+
+// UnmarshalableArgsError is used to indicate error unmarshalling args
+// from the args-string in the form "K=V;K2=V2;..."
+type UnmarshalableArgsError struct {
+ error
+}
+
+// LoadArgs parses args from a string in the form "K=V;K2=V2;..."
+func LoadArgs(args string, container interface{}) error {
+ if args == "" {
+ return nil
+ }
+
+ containerValue := reflect.ValueOf(container)
+
+ pairs := strings.Split(args, ";")
+ unknownArgs := []string{}
+ for _, pair := range pairs {
+ kv := strings.Split(pair, "=")
+ if len(kv) != 2 {
+ return fmt.Errorf("ARGS: invalid pair %q", pair)
+ }
+ keyString := kv[0]
+ valueString := kv[1]
+ keyField := GetKeyField(keyString, containerValue)
+ if !keyField.IsValid() {
+ unknownArgs = append(unknownArgs, pair)
+ continue
+ }
+
+ var keyFieldInterface interface{}
+ switch {
+ case keyField.Kind() == reflect.Ptr:
+ keyField.Set(reflect.New(keyField.Type().Elem()))
+ keyFieldInterface = keyField.Interface()
+ case keyField.CanAddr() && keyField.Addr().CanInterface():
+ keyFieldInterface = keyField.Addr().Interface()
+ default:
+ return UnmarshalableArgsError{fmt.Errorf("field '%s' has no valid interface", keyString)}
+ }
+ u, ok := keyFieldInterface.(encoding.TextUnmarshaler)
+ if !ok {
+ return UnmarshalableArgsError{fmt.Errorf(
+ "ARGS: cannot unmarshal into field '%s' - type '%s' does not implement encoding.TextUnmarshaler",
+ keyString, reflect.TypeOf(keyFieldInterface))}
+ }
+ err := u.UnmarshalText([]byte(valueString))
+ if err != nil {
+ return fmt.Errorf("ARGS: error parsing value of pair %q: %w", pair, err)
+ }
+ }
+
+ isIgnoreUnknown := GetKeyField("IgnoreUnknown", containerValue).Bool()
+ if len(unknownArgs) > 0 && !isIgnoreUnknown {
+ return fmt.Errorf("ARGS: unknown args %q", unknownArgs)
+ }
+ return nil
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/create/create.go b/vendor/github.com/containernetworking/cni/pkg/types/create/create.go
new file mode 100644
index 00000000..452cb622
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/types/create/create.go
@@ -0,0 +1,59 @@
+// Copyright 2016 CNI authors
+//
+// 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 create
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/containernetworking/cni/pkg/types"
+ _ "github.com/containernetworking/cni/pkg/types/020"
+ _ "github.com/containernetworking/cni/pkg/types/040"
+ _ "github.com/containernetworking/cni/pkg/types/100"
+ convert "github.com/containernetworking/cni/pkg/types/internal"
+)
+
+// DecodeVersion returns the CNI version from CNI configuration or result JSON,
+// or an error if the operation could not be performed.
+func DecodeVersion(jsonBytes []byte) (string, error) {
+ var conf struct {
+ CNIVersion string `json:"cniVersion"`
+ }
+ err := json.Unmarshal(jsonBytes, &conf)
+ if err != nil {
+ return "", fmt.Errorf("decoding version from network config: %w", err)
+ }
+ if conf.CNIVersion == "" {
+ return "0.1.0", nil
+ }
+ return conf.CNIVersion, nil
+}
+
+// Create creates a CNI Result using the given JSON with the expected
+// version, or an error if the creation could not be performed
+func Create(version string, bytes []byte) (types.Result, error) {
+ return convert.Create(version, bytes)
+}
+
+// CreateFromBytes creates a CNI Result from the given JSON, automatically
+// detecting the CNI spec version of the result. An error is returned if the
+// operation could not be performed.
+func CreateFromBytes(bytes []byte) (types.Result, error) {
+ version, err := DecodeVersion(bytes)
+ if err != nil {
+ return nil, err
+ }
+ return convert.Create(version, bytes)
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go b/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go
new file mode 100644
index 00000000..bdbe4b0a
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go
@@ -0,0 +1,92 @@
+// Copyright 2016 CNI authors
+//
+// 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 convert
+
+import (
+ "fmt"
+
+ "github.com/containernetworking/cni/pkg/types"
+)
+
+// ConvertFn should convert from the given arbitrary Result type into a
+// Result implementing CNI specification version passed in toVersion.
+// The function is guaranteed to be passed a Result type matching the
+// fromVersion it was registered with, and is guaranteed to be
+// passed a toVersion matching one of the toVersions it was registered with.
+type ConvertFn func(from types.Result, toVersion string) (types.Result, error)
+
+type converter struct {
+ // fromVersion is the CNI Result spec version that convertFn accepts
+ fromVersion string
+ // toVersions is a list of versions that convertFn can convert to
+ toVersions []string
+ convertFn ConvertFn
+}
+
+var converters []*converter
+
+func findConverter(fromVersion, toVersion string) *converter {
+ for _, c := range converters {
+ if c.fromVersion == fromVersion {
+ for _, v := range c.toVersions {
+ if v == toVersion {
+ return c
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// Convert converts a CNI Result to the requested CNI specification version,
+// or returns an error if the conversion could not be performed or failed
+func Convert(from types.Result, toVersion string) (types.Result, error) {
+ if toVersion == "" {
+ toVersion = "0.1.0"
+ }
+
+ fromVersion := from.Version()
+
+ // Shortcut for same version
+ if fromVersion == toVersion {
+ return from, nil
+ }
+
+ // Otherwise find the right converter
+ c := findConverter(fromVersion, toVersion)
+ if c == nil {
+ return nil, fmt.Errorf("no converter for CNI result version %s to %s",
+ fromVersion, toVersion)
+ }
+ return c.convertFn(from, toVersion)
+}
+
+// RegisterConverter registers a CNI Result converter. SHOULD NOT BE CALLED
+// EXCEPT FROM CNI ITSELF.
+func RegisterConverter(fromVersion string, toVersions []string, convertFn ConvertFn) {
+ // Make sure there is no converter already registered for these
+ // from and to versions
+ for _, v := range toVersions {
+ if findConverter(fromVersion, v) != nil {
+ panic(fmt.Sprintf("converter already registered for %s to %s",
+ fromVersion, v))
+ }
+ }
+ converters = append(converters, &converter{
+ fromVersion: fromVersion,
+ toVersions: toVersions,
+ convertFn: convertFn,
+ })
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go b/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go
new file mode 100644
index 00000000..96363091
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go
@@ -0,0 +1,66 @@
+// Copyright 2016 CNI authors
+//
+// 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 convert
+
+import (
+ "fmt"
+
+ "github.com/containernetworking/cni/pkg/types"
+)
+
+type ResultFactoryFunc func([]byte) (types.Result, error)
+
+type creator struct {
+ // CNI Result spec versions that createFn can create a Result for
+ versions []string
+ createFn ResultFactoryFunc
+}
+
+var creators []*creator
+
+func findCreator(version string) *creator {
+ for _, c := range creators {
+ for _, v := range c.versions {
+ if v == version {
+ return c
+ }
+ }
+ }
+ return nil
+}
+
+// Create creates a CNI Result using the given JSON, or an error if the creation
+// could not be performed
+func Create(version string, bytes []byte) (types.Result, error) {
+ if c := findCreator(version); c != nil {
+ return c.createFn(bytes)
+ }
+ return nil, fmt.Errorf("unsupported CNI result version %q", version)
+}
+
+// RegisterCreator registers a CNI Result creator. SHOULD NOT BE CALLED
+// EXCEPT FROM CNI ITSELF.
+func RegisterCreator(versions []string, createFn ResultFactoryFunc) {
+ // Make sure there is no creator already registered for these versions
+ for _, v := range versions {
+ if findCreator(v) != nil {
+ panic(fmt.Sprintf("creator already registered for %s", v))
+ }
+ }
+ creators = append(creators, &creator{
+ versions: versions,
+ createFn: createFn,
+ })
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/types.go b/vendor/github.com/containernetworking/cni/pkg/types/types.go
new file mode 100644
index 00000000..f4b3ce35
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/types/types.go
@@ -0,0 +1,325 @@
+// Copyright 2015 CNI authors
+//
+// 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 types
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net"
+ "os"
+)
+
+// like net.IPNet but adds JSON marshalling and unmarshalling
+type IPNet net.IPNet
+
+// ParseCIDR takes a string like "10.2.3.1/24" and
+// return IPNet with "10.2.3.1" and /24 mask
+func ParseCIDR(s string) (*net.IPNet, error) {
+ ip, ipn, err := net.ParseCIDR(s)
+ if err != nil {
+ return nil, err
+ }
+
+ ipn.IP = ip
+ return ipn, nil
+}
+
+func (n IPNet) MarshalJSON() ([]byte, error) {
+ return json.Marshal((*net.IPNet)(&n).String())
+}
+
+func (n *IPNet) UnmarshalJSON(data []byte) error {
+ var s string
+ if err := json.Unmarshal(data, &s); err != nil {
+ return err
+ }
+
+ tmp, err := ParseCIDR(s)
+ if err != nil {
+ return err
+ }
+
+ *n = IPNet(*tmp)
+ return nil
+}
+
+// Use PluginConf instead of NetConf, the NetConf
+// backwards-compat alias will be removed in a future release.
+type NetConf = PluginConf
+
+// PluginConf describes a plugin configuration for a specific network.
+type PluginConf struct {
+ CNIVersion string `json:"cniVersion,omitempty"`
+
+ Name string `json:"name,omitempty"`
+ Type string `json:"type,omitempty"`
+ Capabilities map[string]bool `json:"capabilities,omitempty"`
+ IPAM IPAM `json:"ipam,omitempty"`
+ DNS DNS `json:"dns,omitempty"`
+
+ RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
+ PrevResult Result `json:"-"`
+
+ // ValidAttachments is only supplied when executing a GC operation
+ ValidAttachments []GCAttachment `json:"cni.dev/valid-attachments,omitempty"`
+}
+
+// GCAttachment is the parameters to a GC call -- namely,
+// the container ID and ifname pair that represents a
+// still-valid attachment.
+type GCAttachment struct {
+ ContainerID string `json:"containerID"`
+ IfName string `json:"ifname"`
+}
+
+// Note: DNS should be omit if DNS is empty but default Marshal function
+// will output empty structure hence need to write a Marshal function
+func (n *PluginConf) MarshalJSON() ([]byte, error) {
+ bytes, err := json.Marshal(*n)
+ if err != nil {
+ return nil, err
+ }
+
+ fixupObj := make(map[string]interface{})
+ if err := json.Unmarshal(bytes, &fixupObj); err != nil {
+ return nil, err
+ }
+
+ if n.DNS.IsEmpty() {
+ delete(fixupObj, "dns")
+ }
+
+ return json.Marshal(fixupObj)
+}
+
+type IPAM struct {
+ Type string `json:"type,omitempty"`
+}
+
+// IsEmpty returns true if IPAM structure has no value, otherwise return false
+func (i *IPAM) IsEmpty() bool {
+ return i.Type == ""
+}
+
+// NetConfList describes an ordered list of networks.
+type NetConfList struct {
+ CNIVersion string `json:"cniVersion,omitempty"`
+
+ Name string `json:"name,omitempty"`
+ DisableCheck bool `json:"disableCheck,omitempty"`
+ DisableGC bool `json:"disableGC,omitempty"`
+ Plugins []*PluginConf `json:"plugins,omitempty"`
+}
+
+// Result is an interface that provides the result of plugin execution
+type Result interface {
+ // The highest CNI specification result version the result supports
+ // without having to convert
+ Version() string
+
+ // Returns the result converted into the requested CNI specification
+ // result version, or an error if conversion failed
+ GetAsVersion(version string) (Result, error)
+
+ // Prints the result in JSON format to stdout
+ Print() error
+
+ // Prints the result in JSON format to provided writer
+ PrintTo(writer io.Writer) error
+}
+
+func PrintResult(result Result, version string) error {
+ newResult, err := result.GetAsVersion(version)
+ if err != nil {
+ return err
+ }
+ return newResult.Print()
+}
+
+// DNS contains values interesting for DNS resolvers
+type DNS struct {
+ Nameservers []string `json:"nameservers,omitempty"`
+ Domain string `json:"domain,omitempty"`
+ Search []string `json:"search,omitempty"`
+ Options []string `json:"options,omitempty"`
+}
+
+// IsEmpty returns true if DNS structure has no value, otherwise return false
+func (d *DNS) IsEmpty() bool {
+ if len(d.Nameservers) == 0 && d.Domain == "" && len(d.Search) == 0 && len(d.Options) == 0 {
+ return true
+ }
+ return false
+}
+
+func (d *DNS) Copy() *DNS {
+ if d == nil {
+ return nil
+ }
+
+ to := &DNS{Domain: d.Domain}
+ to.Nameservers = append(to.Nameservers, d.Nameservers...)
+ to.Search = append(to.Search, d.Search...)
+ to.Options = append(to.Options, d.Options...)
+ return to
+}
+
+type Route struct {
+ Dst net.IPNet
+ GW net.IP
+ MTU int
+ AdvMSS int
+ Priority int
+ Table *int
+ Scope *int
+}
+
+func (r *Route) String() string {
+ table := ""
+ if r.Table != nil {
+ table = fmt.Sprintf("%d", *r.Table)
+ }
+
+ scope := ""
+ if r.Scope != nil {
+ scope = fmt.Sprintf("%d", *r.Scope)
+ }
+
+ return fmt.Sprintf("{Dst:%+v GW:%v MTU:%d AdvMSS:%d Priority:%d Table:%s Scope:%s}", r.Dst, r.GW, r.MTU, r.AdvMSS, r.Priority, table, scope)
+}
+
+func (r *Route) Copy() *Route {
+ if r == nil {
+ return nil
+ }
+
+ route := &Route{
+ Dst: r.Dst,
+ GW: r.GW,
+ MTU: r.MTU,
+ AdvMSS: r.AdvMSS,
+ Priority: r.Priority,
+ Scope: r.Scope,
+ }
+
+ if r.Table != nil {
+ table := *r.Table
+ route.Table = &table
+ }
+
+ if r.Scope != nil {
+ scope := *r.Scope
+ route.Scope = &scope
+ }
+
+ return route
+}
+
+// Well known error codes
+// see https://github.com/containernetworking/cni/blob/main/SPEC.md#well-known-error-codes
+const (
+ ErrUnknown uint = iota // 0
+ ErrIncompatibleCNIVersion // 1
+ ErrUnsupportedField // 2
+ ErrUnknownContainer // 3
+ ErrInvalidEnvironmentVariables // 4
+ ErrIOFailure // 5
+ ErrDecodingFailure // 6
+ ErrInvalidNetworkConfig // 7
+ ErrInvalidNetNS // 8
+ ErrTryAgainLater uint = 11
+ ErrInternal uint = 999
+)
+
+type Error struct {
+ Code uint `json:"code"`
+ Msg string `json:"msg"`
+ Details string `json:"details,omitempty"`
+}
+
+func NewError(code uint, msg, details string) *Error {
+ return &Error{
+ Code: code,
+ Msg: msg,
+ Details: details,
+ }
+}
+
+func (e *Error) Error() string {
+ details := ""
+ if e.Details != "" {
+ details = fmt.Sprintf("; %v", e.Details)
+ }
+ return fmt.Sprintf("%v%v", e.Msg, details)
+}
+
+func (e *Error) Print() error {
+ return prettyPrint(e)
+}
+
+// net.IPNet is not JSON (un)marshallable so this duality is needed
+// for our custom IPNet type
+
+// JSON (un)marshallable types
+type route struct {
+ Dst IPNet `json:"dst"`
+ GW net.IP `json:"gw,omitempty"`
+ MTU int `json:"mtu,omitempty"`
+ AdvMSS int `json:"advmss,omitempty"`
+ Priority int `json:"priority,omitempty"`
+ Table *int `json:"table,omitempty"`
+ Scope *int `json:"scope,omitempty"`
+}
+
+func (r *Route) UnmarshalJSON(data []byte) error {
+ rt := route{}
+ if err := json.Unmarshal(data, &rt); err != nil {
+ return err
+ }
+
+ r.Dst = net.IPNet(rt.Dst)
+ r.GW = rt.GW
+ r.MTU = rt.MTU
+ r.AdvMSS = rt.AdvMSS
+ r.Priority = rt.Priority
+ r.Table = rt.Table
+ r.Scope = rt.Scope
+
+ return nil
+}
+
+func (r Route) MarshalJSON() ([]byte, error) {
+ rt := route{
+ Dst: IPNet(r.Dst),
+ GW: r.GW,
+ MTU: r.MTU,
+ AdvMSS: r.AdvMSS,
+ Priority: r.Priority,
+ Table: r.Table,
+ Scope: r.Scope,
+ }
+
+ return json.Marshal(rt)
+}
+
+func prettyPrint(obj interface{}) error {
+ data, err := json.MarshalIndent(obj, "", " ")
+ if err != nil {
+ return err
+ }
+ _, err = os.Stdout.Write(data)
+ return err
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/utils/utils.go b/vendor/github.com/containernetworking/cni/pkg/utils/utils.go
new file mode 100644
index 00000000..1981d255
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/utils/utils.go
@@ -0,0 +1,82 @@
+// Copyright 2019 CNI authors
+//
+// 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 utils
+
+import (
+ "bytes"
+ "fmt"
+ "regexp"
+ "unicode"
+
+ "github.com/containernetworking/cni/pkg/types"
+)
+
+const (
+ // cniValidNameChars is the regexp used to validate valid characters in
+ // containerID and networkName
+ cniValidNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.\-]`
+
+ // maxInterfaceNameLength is the length max of a valid interface name
+ maxInterfaceNameLength = 15
+)
+
+var cniReg = regexp.MustCompile(`^` + cniValidNameChars + `*$`)
+
+// ValidateContainerID will validate that the supplied containerID is not empty does not contain invalid characters
+func ValidateContainerID(containerID string) *types.Error {
+ if containerID == "" {
+ return types.NewError(types.ErrUnknownContainer, "missing containerID", "")
+ }
+ if !cniReg.MatchString(containerID) {
+ return types.NewError(types.ErrInvalidEnvironmentVariables, "invalid characters in containerID", containerID)
+ }
+ return nil
+}
+
+// ValidateNetworkName will validate that the supplied networkName does not contain invalid characters
+func ValidateNetworkName(networkName string) *types.Error {
+ if networkName == "" {
+ return types.NewError(types.ErrInvalidNetworkConfig, "missing network name:", "")
+ }
+ if !cniReg.MatchString(networkName) {
+ return types.NewError(types.ErrInvalidNetworkConfig, "invalid characters found in network name", networkName)
+ }
+ return nil
+}
+
+// ValidateInterfaceName will validate the interface name based on the four rules below
+// 1. The name must not be empty
+// 2. The name must be less than 16 characters
+// 3. The name must not be "." or ".."
+// 4. The name must not contain / or : or any whitespace characters
+// ref to https://github.com/torvalds/linux/blob/master/net/core/dev.c#L1024
+func ValidateInterfaceName(ifName string) *types.Error {
+ if len(ifName) == 0 {
+ return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is empty", "")
+ }
+ if len(ifName) > maxInterfaceNameLength {
+ return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is too long", fmt.Sprintf("interface name should be less than %d characters", maxInterfaceNameLength+1))
+ }
+ if ifName == "." || ifName == ".." {
+ return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is . or ..", "")
+ }
+ for _, r := range bytes.Runes([]byte(ifName)) {
+ if r == '/' || r == ':' || unicode.IsSpace(r) {
+ return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name contains / or : or whitespace characters", "")
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/version/conf.go b/vendor/github.com/containernetworking/cni/pkg/version/conf.go
new file mode 100644
index 00000000..808c33b8
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/version/conf.go
@@ -0,0 +1,26 @@
+// Copyright 2016 CNI authors
+//
+// 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 version
+
+import (
+ "github.com/containernetworking/cni/pkg/types/create"
+)
+
+// ConfigDecoder can decode the CNI version available in network config data
+type ConfigDecoder struct{}
+
+func (*ConfigDecoder) Decode(jsonBytes []byte) (string, error) {
+ return create.DecodeVersion(jsonBytes)
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/version/plugin.go b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go
new file mode 100644
index 00000000..e3bd375b
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go
@@ -0,0 +1,168 @@
+// Copyright 2016 CNI authors
+//
+// 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 version
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+)
+
+// PluginInfo reports information about CNI versioning
+type PluginInfo interface {
+ // SupportedVersions returns one or more CNI spec versions that the plugin
+ // supports. If input is provided in one of these versions, then the plugin
+ // promises to use the same CNI version in its response
+ SupportedVersions() []string
+
+ // Encode writes this CNI version information as JSON to the given Writer
+ Encode(io.Writer) error
+}
+
+type pluginInfo struct {
+ CNIVersion_ string `json:"cniVersion"`
+ SupportedVersions_ []string `json:"supportedVersions,omitempty"`
+}
+
+// pluginInfo implements the PluginInfo interface
+var _ PluginInfo = &pluginInfo{}
+
+func (p *pluginInfo) Encode(w io.Writer) error {
+ return json.NewEncoder(w).Encode(p)
+}
+
+func (p *pluginInfo) SupportedVersions() []string {
+ return p.SupportedVersions_
+}
+
+// PluginSupports returns a new PluginInfo that will report the given versions
+// as supported
+func PluginSupports(supportedVersions ...string) PluginInfo {
+ if len(supportedVersions) < 1 {
+ panic("programmer error: you must support at least one version")
+ }
+ return &pluginInfo{
+ CNIVersion_: Current(),
+ SupportedVersions_: supportedVersions,
+ }
+}
+
+// PluginDecoder can decode the response returned by a plugin's VERSION command
+type PluginDecoder struct{}
+
+func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) {
+ var info pluginInfo
+ err := json.Unmarshal(jsonBytes, &info)
+ if err != nil {
+ return nil, fmt.Errorf("decoding version info: %w", err)
+ }
+ if info.CNIVersion_ == "" {
+ return nil, fmt.Errorf("decoding version info: missing field cniVersion")
+ }
+ if len(info.SupportedVersions_) == 0 {
+ if info.CNIVersion_ == "0.2.0" {
+ return PluginSupports("0.1.0", "0.2.0"), nil
+ }
+ return nil, fmt.Errorf("decoding version info: missing field supportedVersions")
+ }
+ return &info, nil
+}
+
+// ParseVersion parses a version string like "3.0.1" or "0.4.5" into major,
+// minor, and micro numbers or returns an error
+func ParseVersion(version string) (int, int, int, error) {
+ var major, minor, micro int
+ if version == "" { // special case: no version declared == v0.1.0
+ return 0, 1, 0, nil
+ }
+
+ parts := strings.Split(version, ".")
+ if len(parts) >= 4 {
+ return -1, -1, -1, fmt.Errorf("invalid version %q: too many parts", version)
+ }
+
+ major, err := strconv.Atoi(parts[0])
+ if err != nil {
+ return -1, -1, -1, fmt.Errorf("failed to convert major version part %q: %w", parts[0], err)
+ }
+
+ if len(parts) >= 2 {
+ minor, err = strconv.Atoi(parts[1])
+ if err != nil {
+ return -1, -1, -1, fmt.Errorf("failed to convert minor version part %q: %w", parts[1], err)
+ }
+ }
+
+ if len(parts) >= 3 {
+ micro, err = strconv.Atoi(parts[2])
+ if err != nil {
+ return -1, -1, -1, fmt.Errorf("failed to convert micro version part %q: %w", parts[2], err)
+ }
+ }
+
+ return major, minor, micro, nil
+}
+
+// GreaterThanOrEqualTo takes two string versions, parses them into major/minor/micro
+// numbers, and compares them to determine whether the first version is greater
+// than or equal to the second
+func GreaterThanOrEqualTo(version, otherVersion string) (bool, error) {
+ firstMajor, firstMinor, firstMicro, err := ParseVersion(version)
+ if err != nil {
+ return false, err
+ }
+
+ secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion)
+ if err != nil {
+ return false, err
+ }
+
+ if firstMajor > secondMajor {
+ return true, nil
+ } else if firstMajor == secondMajor {
+ if firstMinor > secondMinor {
+ return true, nil
+ } else if firstMinor == secondMinor && firstMicro >= secondMicro {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+// GreaterThan returns true if the first version is greater than the second
+func GreaterThan(version, otherVersion string) (bool, error) {
+ firstMajor, firstMinor, firstMicro, err := ParseVersion(version)
+ if err != nil {
+ return false, err
+ }
+
+ secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion)
+ if err != nil {
+ return false, err
+ }
+
+ if firstMajor > secondMajor {
+ return true, nil
+ } else if firstMajor == secondMajor {
+ if firstMinor > secondMinor {
+ return true, nil
+ } else if firstMinor == secondMinor && firstMicro > secondMicro {
+ return true, nil
+ }
+ }
+ return false, nil
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go b/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go
new file mode 100644
index 00000000..25c3810b
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go
@@ -0,0 +1,49 @@
+// Copyright 2016 CNI authors
+//
+// 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 version
+
+import "fmt"
+
+type ErrorIncompatible struct {
+ Config string
+ Supported []string
+}
+
+func (e *ErrorIncompatible) Details() string {
+ return fmt.Sprintf("config is %q, plugin supports %q", e.Config, e.Supported)
+}
+
+func (e *ErrorIncompatible) Error() string {
+ return fmt.Sprintf("incompatible CNI versions: %s", e.Details())
+}
+
+type Reconciler struct{}
+
+func (r *Reconciler) Check(configVersion string, pluginInfo PluginInfo) *ErrorIncompatible {
+ return r.CheckRaw(configVersion, pluginInfo.SupportedVersions())
+}
+
+func (*Reconciler) CheckRaw(configVersion string, supportedVersions []string) *ErrorIncompatible {
+ for _, supportedVersion := range supportedVersions {
+ if configVersion == supportedVersion {
+ return nil
+ }
+ }
+
+ return &ErrorIncompatible{
+ Config: configVersion,
+ Supported: supportedVersions,
+ }
+}
diff --git a/vendor/github.com/containernetworking/cni/pkg/version/version.go b/vendor/github.com/containernetworking/cni/pkg/version/version.go
new file mode 100644
index 00000000..cfb6a12f
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/version/version.go
@@ -0,0 +1,90 @@
+// Copyright 2016 CNI authors
+//
+// 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 version
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/create"
+)
+
+// Current reports the version of the CNI spec implemented by this library
+func Current() string {
+ return "1.1.0"
+}
+
+// Legacy PluginInfo describes a plugin that is backwards compatible with the
+// CNI spec version 0.1.0. In particular, a runtime compiled against the 0.1.0
+// library ought to work correctly with a plugin that reports support for
+// Legacy versions.
+//
+// Any future CNI spec versions which meet this definition should be added to
+// this list.
+var (
+ Legacy = PluginSupports("0.1.0", "0.2.0")
+ All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0", "1.1.0")
+)
+
+// VersionsFrom returns a list of versions starting from min, inclusive
+func VersionsStartingFrom(min string) PluginInfo {
+ out := []string{}
+ // cheat, just assume ordered
+ ok := false
+ for _, v := range All.SupportedVersions() {
+ if !ok && v == min {
+ ok = true
+ }
+ if ok {
+ out = append(out, v)
+ }
+ }
+ return PluginSupports(out...)
+}
+
+// Finds a Result object matching the requested version (if any) and asks
+// that object to parse the plugin result, returning an error if parsing failed.
+func NewResult(version string, resultBytes []byte) (types.Result, error) {
+ return create.Create(version, resultBytes)
+}
+
+// ParsePrevResult parses a prevResult in a NetConf structure and sets
+// the NetConf's PrevResult member to the parsed Result object.
+func ParsePrevResult(conf *types.PluginConf) error {
+ if conf.RawPrevResult == nil {
+ return nil
+ }
+
+ // Prior to 1.0.0, Result types may not marshal a CNIVersion. Since the
+ // result version must match the config version, if the Result's version
+ // is empty, inject the config version.
+ if ver, ok := conf.RawPrevResult["CNIVersion"]; !ok || ver == "" {
+ conf.RawPrevResult["CNIVersion"] = conf.CNIVersion
+ }
+
+ resultBytes, err := json.Marshal(conf.RawPrevResult)
+ if err != nil {
+ return fmt.Errorf("could not serialize prevResult: %w", err)
+ }
+
+ conf.RawPrevResult = nil
+ conf.PrevResult, err = create.Create(conf.CNIVersion, resultBytes)
+ if err != nil {
+ return fmt.Errorf("could not parse prevResult: %w", err)
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/containernetworking/plugins/LICENSE b/vendor/github.com/containernetworking/plugins/LICENSE
new file mode 100644
index 00000000..8dada3ed
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ 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.
diff --git a/vendor/github.com/containernetworking/plugins/pkg/ns/README.md b/vendor/github.com/containernetworking/plugins/pkg/ns/README.md
new file mode 100644
index 00000000..e5fef2db
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/pkg/ns/README.md
@@ -0,0 +1,41 @@
+### Namespaces, Threads, and Go
+On Linux each OS thread can have a different network namespace. Go's thread scheduling model switches goroutines between OS threads based on OS thread load and whether the goroutine would block other goroutines. This can result in a goroutine switching network namespaces without notice and lead to errors in your code.
+
+### Namespace Switching
+Switching namespaces with the `ns.Set()` method is not recommended without additional strategies to prevent unexpected namespace changes when your goroutines switch OS threads.
+
+Go provides the `runtime.LockOSThread()` function to ensure a specific goroutine executes on its current OS thread and prevents any other goroutine from running in that thread until the locked one exits. Careful usage of `LockOSThread()` and goroutines can provide good control over which network namespace a given goroutine executes in.
+
+For example, you cannot rely on the `ns.Set()` namespace being the current namespace after the `Set()` call unless you do two things. First, the goroutine calling `Set()` must have previously called `LockOSThread()`. Second, you must ensure `runtime.UnlockOSThread()` is not called somewhere in-between. You also cannot rely on the initial network namespace remaining the current network namespace if any other code in your program switches namespaces, unless you have already called `LockOSThread()` in that goroutine. Note that `LockOSThread()` prevents the Go scheduler from optimally scheduling goroutines for best performance, so `LockOSThread()` should only be used in small, isolated goroutines that release the lock quickly.
+
+### Do() The Recommended Thing
+The `ns.Do()` method provides **partial** control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace (including the root namespace) should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example:
+
+```go
+err = targetNs.Do(func(hostNs ns.NetNS) error {
+ linkAttrs := netlink.NewLinkAttrs()
+ linkAttrs.Name = "dummy0"
+ dummy := &netlink.Dummy{
+ LinkAttrs: linkAttrs,
+ }
+ return netlink.LinkAdd(dummy)
+})
+```
+
+Note this requirement to wrap every network call is very onerous - any libraries you call might call out to network services such as DNS, and all such calls need to be protected after you call `ns.Do()`. All goroutines spawned from within the `ns.Do` will not inherit the new namespace. The CNI plugins all exit very soon after calling `ns.Do()` which helps to minimize the problem.
+
+When a new thread is spawned in Linux, it inherits the namespace of its parent. In versions of go **prior to 1.10**, if the runtime spawns a new OS thread, it picks the parent randomly. If the chosen parent thread has been moved to a new namespace (even temporarily), the new OS thread will be permanently "stuck in the wrong namespace", and goroutines will non-deterministically switch namespaces as they are rescheduled.
+
+In short, **there was no safe way to change network namespaces, even temporarily, from within a long-lived, multithreaded Go process**. If you wish to do this, you must use go 1.10 or greater.
+
+
+### Creating network namespaces
+Earlier versions of this library managed namespace creation, but as CNI does not actually utilize this feature (and it was essentially unmaintained), it was removed. If you're writing a container runtime, you should implement namespace management yourself. However, there are some gotchas when doing so, especially around handling `/var/run/netns`. A reasonably correct reference implementation, borrowed from `rkt`, can be found in `pkg/testutils/netns_linux.go` if you're in need of a source of inspiration.
+
+
+### Further Reading
+ - https://github.com/golang/go/wiki/LockOSThread
+ - http://morsmachine.dk/go-scheduler
+ - https://github.com/containernetworking/cni/issues/262
+ - https://golang.org/pkg/runtime/
+ - https://www.weave.works/blog/linux-namespaces-and-go-don-t-mix
diff --git a/vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go
new file mode 100644
index 00000000..5a6aaa33
--- /dev/null
+++ b/vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go
@@ -0,0 +1,286 @@
+// Copyright 2015-2017 CNI authors
+//
+// 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 ns
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+ "sync"
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+// Returns an object representing the current OS thread's network namespace
+func GetCurrentNS() (NetNS, error) {
+ // Lock the thread in case other goroutine executes in it and changes its
+ // network namespace after getCurrentThreadNetNSPath(), otherwise it might
+ // return an unexpected network namespace.
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+ return getCurrentNSNoLock()
+}
+
+func getCurrentNSNoLock() (NetNS, error) {
+ return GetNS(getCurrentThreadNetNSPath())
+}
+
+func getCurrentThreadNetNSPath() string {
+ // /proc/self/ns/net returns the namespace of the main thread, not
+ // of whatever thread this goroutine is running on. Make sure we
+ // use the thread's net namespace since the thread is switching around
+ return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
+}
+
+func (ns *netNS) Close() error {
+ if err := ns.errorIfClosed(); err != nil {
+ return err
+ }
+
+ if err := ns.file.Close(); err != nil {
+ return fmt.Errorf("Failed to close %q: %v", ns.file.Name(), err)
+ }
+ ns.closed = true
+
+ return nil
+}
+
+func (ns *netNS) Set() error {
+ if err := ns.errorIfClosed(); err != nil {
+ return err
+ }
+
+ if err := unix.Setns(int(ns.Fd()), unix.CLONE_NEWNET); err != nil {
+ return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err)
+ }
+
+ return nil
+}
+
+type NetNS interface {
+ // Executes the passed closure in this object's network namespace,
+ // attempting to restore the original namespace before returning.
+ // However, since each OS thread can have a different network namespace,
+ // and Go's thread scheduling is highly variable, callers cannot
+ // guarantee any specific namespace is set unless operations that
+ // require that namespace are wrapped with Do(). Also, no code called
+ // from Do() should call runtime.UnlockOSThread(), or the risk
+ // of executing code in an incorrect namespace will be greater. See
+ // https://github.com/golang/go/wiki/LockOSThread for further details.
+ Do(toRun func(NetNS) error) error
+
+ // Sets the current network namespace to this object's network namespace.
+ // Note that since Go's thread scheduling is highly variable, callers
+ // cannot guarantee the requested namespace will be the current namespace
+ // after this function is called; to ensure this wrap operations that
+ // require the namespace with Do() instead.
+ Set() error
+
+ // Returns the filesystem path representing this object's network namespace
+ Path() string
+
+ // Returns a file descriptor representing this object's network namespace
+ Fd() uintptr
+
+ // Cleans up this instance of the network namespace; if this instance
+ // is the last user the namespace will be destroyed
+ Close() error
+}
+
+type netNS struct {
+ file *os.File
+ closed bool
+}
+
+// netNS implements the NetNS interface
+var _ NetNS = &netNS{}
+
+const (
+ // https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h
+ NSFS_MAGIC = unix.NSFS_MAGIC
+ PROCFS_MAGIC = unix.PROC_SUPER_MAGIC
+)
+
+type NSPathNotExistErr struct{ msg string }
+
+func (e NSPathNotExistErr) Error() string { return e.msg }
+
+type NSPathNotNSErr struct{ msg string }
+
+func (e NSPathNotNSErr) Error() string { return e.msg }
+
+func IsNSorErr(nspath string) error {
+ stat := syscall.Statfs_t{}
+ if err := syscall.Statfs(nspath, &stat); err != nil {
+ if os.IsNotExist(err) {
+ err = NSPathNotExistErr{msg: fmt.Sprintf("failed to Statfs %q: %v", nspath, err)}
+ } else {
+ err = fmt.Errorf("failed to Statfs %q: %v", nspath, err)
+ }
+ return err
+ }
+
+ switch stat.Type {
+ case PROCFS_MAGIC, NSFS_MAGIC:
+ return nil
+ default:
+ return NSPathNotNSErr{msg: fmt.Sprintf("unknown FS magic on %q: %x", nspath, stat.Type)}
+ }
+}
+
+// Returns an object representing the namespace referred to by @path
+func GetNS(nspath string) (NetNS, error) {
+ err := IsNSorErr(nspath)
+ if err != nil {
+ return nil, err
+ }
+
+ fd, err := os.Open(nspath)
+ if err != nil {
+ return nil, err
+ }
+
+ return &netNS{file: fd}, nil
+}
+
+// Returns a new empty NetNS.
+// Calling Close() let the kernel garbage collect the network namespace.
+func TempNetNS() (NetNS, error) {
+ var tempNS NetNS
+ var err error
+ var wg sync.WaitGroup
+ wg.Add(1)
+
+ // Create the new namespace in a new goroutine so that if we later fail
+ // to switch the namespace back to the original one, we can safely
+ // leave the thread locked to die without a risk of the current thread
+ // left lingering with incorrect namespace.
+ go func() {
+ defer wg.Done()
+ runtime.LockOSThread()
+
+ var threadNS NetNS
+ // save a handle to current network namespace
+ threadNS, err = getCurrentNSNoLock()
+ if err != nil {
+ err = fmt.Errorf("failed to open current namespace: %v", err)
+ return
+ }
+ defer threadNS.Close()
+
+ // create the temporary network namespace
+ err = unix.Unshare(unix.CLONE_NEWNET)
+ if err != nil {
+ return
+ }
+
+ // get a handle to the temporary network namespace
+ tempNS, err = getCurrentNSNoLock()
+
+ err2 := threadNS.Set()
+ if err2 == nil {
+ // Unlock the current thread only when we successfully switched back
+ // to the original namespace; otherwise leave the thread locked which
+ // will force the runtime to scrap the current thread, that is maybe
+ // not as optimal but at least always safe to do.
+ runtime.UnlockOSThread()
+ }
+ }()
+
+ wg.Wait()
+ return tempNS, err
+}
+
+func (ns *netNS) Path() string {
+ return ns.file.Name()
+}
+
+func (ns *netNS) Fd() uintptr {
+ return ns.file.Fd()
+}
+
+func (ns *netNS) errorIfClosed() error {
+ if ns.closed {
+ return fmt.Errorf("%q has already been closed", ns.file.Name())
+ }
+ return nil
+}
+
+func (ns *netNS) Do(toRun func(NetNS) error) error {
+ if err := ns.errorIfClosed(); err != nil {
+ return err
+ }
+
+ containedCall := func(hostNS NetNS) error {
+ threadNS, err := getCurrentNSNoLock()
+ if err != nil {
+ return fmt.Errorf("failed to open current netns: %v", err)
+ }
+ defer threadNS.Close()
+
+ // switch to target namespace
+ if err = ns.Set(); err != nil {
+ return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err)
+ }
+ defer func() {
+ err := threadNS.Set() // switch back
+ if err == nil {
+ // Unlock the current thread only when we successfully switched back
+ // to the original namespace; otherwise leave the thread locked which
+ // will force the runtime to scrap the current thread, that is maybe
+ // not as optimal but at least always safe to do.
+ runtime.UnlockOSThread()
+ }
+ }()
+
+ return toRun(hostNS)
+ }
+
+ // save a handle to current network namespace
+ hostNS, err := GetCurrentNS()
+ if err != nil {
+ return fmt.Errorf("Failed to open current namespace: %v", err)
+ }
+ defer hostNS.Close()
+
+ var wg sync.WaitGroup
+ wg.Add(1)
+
+ // Start the callback in a new green thread so that if we later fail
+ // to switch the namespace back to the original one, we can safely
+ // leave the thread locked to die without a risk of the current thread
+ // left lingering with incorrect namespace.
+ var innerError error
+ go func() {
+ defer wg.Done()
+ runtime.LockOSThread()
+ innerError = containedCall(hostNS)
+ }()
+ wg.Wait()
+
+ return innerError
+}
+
+// WithNetNSPath executes the passed closure under the given network
+// namespace, restoring the original namespace afterwards.
+func WithNetNSPath(nspath string, toRun func(NetNS) error) error {
+ ns, err := GetNS(nspath)
+ if err != nil {
+ return err
+ }
+ defer ns.Close()
+ return ns.Do(toRun)
+}
diff --git a/vendor/github.com/containers/buildah/.cirrus.yml b/vendor/github.com/containers/buildah/.cirrus.yml
new file mode 100644
index 00000000..66b95024
--- /dev/null
+++ b/vendor/github.com/containers/buildah/.cirrus.yml
@@ -0,0 +1,456 @@
+---
+
+# Main collection of env. vars to set for all tasks and scripts.
+env:
+ ####
+ #### Global variables used for all tasks
+ ####
+ # Name of the ultimate destination branch for this CI run, PR or post-merge.
+ DEST_BRANCH: "main"
+ GOPATH: "/var/tmp/go"
+ GOSRC: "${GOPATH}/src/github.com/containers/buildah"
+ GOCACHE: "/tmp/go-build"
+ # Overrides default location (/tmp/cirrus) for repo clone
+ CIRRUS_WORKING_DIR: "${GOSRC}"
+ # Shell used to execute all script commands
+ CIRRUS_SHELL: "/bin/bash"
+ # Automation script path relative to $CIRRUS_WORKING_DIR)
+ SCRIPT_BASE: "./contrib/cirrus"
+ # No need to go crazy, but grab enough to cover most PRs
+ CIRRUS_CLONE_DEPTH: 50
+ # Unless set by in_podman.sh, default to operating outside of a podman container
+ IN_PODMAN: 'false'
+ # root or rootless
+ PRIV_NAME: root
+ # default "mention the $BUILDAH_RUNTIME in the task alias, with initial whitespace" value
+ RUNTIME_N: ""
+
+ ####
+ #### Cache-image names to test with
+ ####
+ # GCE project where images live
+ IMAGE_PROJECT: "libpod-218412"
+ FEDORA_NAME: "fedora-42"
+ PRIOR_FEDORA_NAME: "fedora-41"
+ RAWHIDE_NAME: "rawhide"
+ DEBIAN_NAME: "debian-13"
+
+ # Image identifiers
+ IMAGE_SUFFIX: "c20250910t092246z-f42f41d13"
+ FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
+ PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}"
+ RAWHIDE_CACHE_IMAGE_NAME: "rawhide-${IMAGE_SUFFIX}" # Used temporarily for rust-podman-sequoia. After that RPM is available in stable Fedora releases, we can stop testing against Rawhide again.
+ DEBIAN_CACHE_IMAGE_NAME: "debian-${IMAGE_SUFFIX}"
+
+ IN_PODMAN_IMAGE: "quay.io/libpod/fedora_podman:${IMAGE_SUFFIX}"
+
+ ####
+ #### Command variables to help avoid duplication
+ ####
+ # Command to prefix every output line with a timestamp
+ # (can't do inline awk script, Cirrus-CI or YAML mangles quoting)
+ _TIMESTAMP: 'awk -f ${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/timestamp.awk'
+
+gcp_credentials: ENCRYPTED[ae0bf7370f0b6e446bc61d0865a2c55d3e166b3fab9466eb0393e38e1c66a31ca4c71ddc7e0139d47d075c36dd6d3fd7]
+
+# Default timeout for each task
+timeout_in: 30m
+
+# Default VM to use unless set or modified by task
+gce_instance: &standardvm
+ image_project: "${IMAGE_PROJECT}"
+ zone: "us-central1-c" # Required by Cirrus for the time being
+ cpu: 2
+ memory: "4G"
+ disk: 200 # Gigabytes, do not set less than 200 per obscure GCE docs re: I/O performance
+ image_name: "${FEDORA_CACHE_IMAGE_NAME}"
+
+
+# Update metadata on VM images referenced by this repository state
+meta_task:
+ name: "VM img. keepalive"
+ alias: meta
+
+ container:
+ image: "quay.io/libpod/imgts:latest"
+ cpu: 1
+ memory: "1G"
+
+ env:
+ # Space-separated list of images used by this repository state
+ IMGNAMES: |-
+ ${FEDORA_CACHE_IMAGE_NAME}
+ ${PRIOR_FEDORA_CACHE_IMAGE_NAME}
+ ${RAWHIDE_CACHE_IMAGE_NAME}
+ ${DEBIAN_CACHE_IMAGE_NAME}
+ build-push-${IMAGE_SUFFIX}
+ BUILDID: "${CIRRUS_BUILD_ID}"
+ REPOREF: "${CIRRUS_CHANGE_IN_REPO}"
+ GCPJSON: ENCRYPTED[d3614d6f5cc0e66be89d4252b3365fd84f14eee0259d4eb47e25fc0bc2842c7937f5ee8c882b7e547b4c5ec4b6733b14]
+ GCPNAME: ENCRYPTED[8509e6a681b859479ce6aa275bd3c4ac82de5beec6df6057925afc4cd85b7ef2e879066ae8baaa2d453b82958e434578]
+ GCPPROJECT: ENCRYPTED[cc09b62d0ec6746a3df685e663ad25d9d5af95ef5fd843c96f3d0ec9d7f065dc63216b9c685c9f43a776a1d403991494]
+
+ clone_script: 'true'
+ script: '/usr/local/bin/entrypoint.sh'
+
+
+smoke_task:
+ alias: 'smoke'
+ name: "Smoke Test"
+
+ gce_instance:
+ memory: "12G"
+ cpu: 8
+
+ # Don't bother running on branches (including cron), or for tags.
+ skip: $CIRRUS_PR == ''
+
+ timeout_in: 10m
+
+ setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}'
+ build_script: '${SCRIPT_BASE}/build.sh |& ${_TIMESTAMP}'
+ validate_test_script: '${SCRIPT_BASE}/test.sh validate |& ${_TIMESTAMP}'
+
+ binary_artifacts:
+ path: ./bin/*
+
+# Check that all included go modules from other sources match
+# # what is expected in `vendor/modules.txt` vs `go.mod`.
+vendor_task:
+ name: "Test Vendoring"
+ alias: vendor
+
+ env:
+ CIRRUS_WORKING_DIR: "/var/tmp/go/src/github.com/containers/buildah"
+ GOPATH: "/var/tmp/go"
+ GOSRC: "/var/tmp/go/src/github.com/containers/buildah"
+
+ # Runs within Cirrus's "community cluster"
+ container:
+ image: docker.io/library/golang:1.24.2
+ cpu: 1
+ memory: 1
+
+ timeout_in: 5m
+
+ vendor_script:
+ - './hack/check_vendor_toolchain.sh Try updating the image used by the vendor_task in .cirrus.yml.'
+ - 'make vendor'
+ - './hack/tree_status.sh'
+
+
+# Confirm cross-compile ALL architectures on a Mac OS-X VM.
+cross_build_task:
+ name: "Cross Compile"
+ gce_instance:
+ cpu: 8
+ memory: "24G"
+ alias: cross_build
+ skip: >-
+ $CIRRUS_CHANGE_TITLE =~ '.*CI:DOCS.*'
+ env:
+ HOME: /root
+ script:
+ - go version
+ - make -j cross CGO_ENABLED=0
+ binary_artifacts:
+ path: ./bin/*
+
+
+unit_task:
+ name: 'Unit tests w/ $STORAGE_DRIVER'
+ gce_instance:
+ cpu: 4
+ alias: unit
+ skip: ¬_build_docs >-
+ $CIRRUS_CHANGE_TITLE =~ '.*CI:DOCS.*' ||
+ $CIRRUS_CHANGE_TITLE =~ '.*CI:BUILD.*'
+ depends_on: &smoke_vendor
+ - smoke
+ - vendor
+
+ matrix:
+ - env:
+ STORAGE_DRIVER: 'vfs'
+ - env:
+ STORAGE_DRIVER: 'overlay'
+
+ setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}'
+ unit_test_script: '${SCRIPT_BASE}/test.sh unit |& ${_TIMESTAMP}'
+
+
+conformance_task:
+ name: 'Debian Conformance w/ $STORAGE_DRIVER'
+ alias: conformance
+ skip: *not_build_docs
+ depends_on: *smoke_vendor
+
+ gce_instance:
+ cpu: 4
+ image_name: "${DEBIAN_CACHE_IMAGE_NAME}"
+
+ matrix:
+ - env:
+ STORAGE_DRIVER: 'vfs'
+ TMPDIR: '/var/tmp'
+ - env:
+ STORAGE_DRIVER: 'overlay'
+
+ setup_script: '${SCRIPT_BASE}/setup.sh conformance |& ${_TIMESTAMP}'
+ conformance_test_script: '${SCRIPT_BASE}/test.sh conformance |& ${_TIMESTAMP}'
+
+
+integration_task:
+ name: "Integration $DISTRO_NV$RUNTIME_N w/ $STORAGE_DRIVER"
+ alias: integration
+ skip: *not_build_docs
+ depends_on: *smoke_vendor
+
+ matrix:
+ # VFS
+ - env:
+ DISTRO_NV: "${FEDORA_NAME}"
+ IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'vfs'
+ BUILDAH_RUNTIME: crun
+ RUNTIME_N: " using crun"
+ - env:
+ DISTRO_NV: "${PRIOR_FEDORA_NAME}"
+ IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'vfs'
+ BUILDAH_RUNTIME: crun
+ RUNTIME_N: " using crun"
+ - env:
+ DISTRO_NV: "${DEBIAN_NAME}"
+ IMAGE_NAME: "${DEBIAN_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'vfs'
+ # OVERLAY
+ - env:
+ DISTRO_NV: "${FEDORA_NAME}"
+ IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'overlay'
+ BUILDAH_RUNTIME: crun
+ RUNTIME_N: " using crun"
+ - env:
+ DISTRO_NV: "${PRIOR_FEDORA_NAME}"
+ IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'overlay'
+ BUILDAH_RUNTIME: crun
+ RUNTIME_N: " using crun"
+ - env:
+ DISTRO_NV: "${DEBIAN_NAME}"
+ IMAGE_NAME: "${DEBIAN_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'overlay'
+ - env:
+ DISTRO_NV: "${RAWHIDE_NAME}"
+ IMAGE_NAME: "${RAWHIDE_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'overlay'
+ TEST_BUILD_TAGS: 'containers_image_sequoia'
+
+ gce_instance: &integration_gce_instance
+ image_name: "$IMAGE_NAME"
+ cpu: 8
+ memory: "8G"
+
+ # Separate scripts for separate outputs, makes debugging easier.
+ setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}'
+ build_script: '${SCRIPT_BASE}/build.sh |& ${_TIMESTAMP}'
+ integration_test_script: '${SCRIPT_BASE}/test.sh integration |& ${_TIMESTAMP}'
+
+ binary_artifacts:
+ path: ./bin/*
+
+ always: &standardlogs
+ audit_log_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh audit'
+ df_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh df'
+ journal_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh journal'
+ podman_system_info_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh podman'
+ buildah_version_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh buildah_version'
+ buildah_info_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh buildah_info'
+ package_versions_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh packages'
+ golang_version_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh golang'
+
+non_blocking_integration_task:
+ name: "Integration $DISTRO_NV$RUNTIME_N w/ $STORAGE_DRIVER (non-blocking)"
+ alias: non_blocking_integration
+ skip: *not_build_docs
+ depends_on: *smoke_vendor
+
+ matrix:
+ # VFS
+ - env:
+ DISTRO_NV: "${FEDORA_NAME}"
+ IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'vfs'
+ BUILDAH_RUNTIME: runc
+ RUNTIME_N: " using runc"
+ - env:
+ DISTRO_NV: "${PRIOR_FEDORA_NAME}"
+ IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'vfs'
+ BUILDAH_RUNTIME: runc
+ RUNTIME_N: " using runc"
+ # OVERLAY
+ - env:
+ DISTRO_NV: "${FEDORA_NAME}"
+ IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'overlay'
+ BUILDAH_RUNTIME: runc
+ RUNTIME_N: " using runc"
+ - env:
+ DISTRO_NV: "${PRIOR_FEDORA_NAME}"
+ IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'overlay'
+ BUILDAH_RUNTIME: runc
+ RUNTIME_N: " using runc"
+
+ gce_instance:
+ <<: *integration_gce_instance
+
+ # Separate scripts for separate outputs, makes debugging easier.
+ setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}'
+ build_script: '${SCRIPT_BASE}/build.sh |& ${_TIMESTAMP}'
+ integration_test_script: '${SCRIPT_BASE}/test.sh integration |& ${_TIMESTAMP}'
+
+ binary_artifacts:
+ path: ./bin/*
+
+ always: *standardlogs
+
+integration_rootless_task:
+ name: "Integration rootless $DISTRO_NV$RUNTIME_N w/ $STORAGE_DRIVER"
+ alias: integration_rootless
+ skip: *not_build_docs
+ depends_on: *smoke_vendor
+
+ matrix:
+ # Running rootless tests on overlay
+ # OVERLAY
+ - env:
+ DISTRO_NV: "${FEDORA_NAME}"
+ IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'overlay'
+ PRIV_NAME: rootless
+ BUILDAH_RUNTIME: crun
+ RUNTIME_N: " using crun"
+ - env:
+ DISTRO_NV: "${PRIOR_FEDORA_NAME}"
+ IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'overlay'
+ PRIV_NAME: rootless
+ BUILDAH_RUNTIME: crun
+ RUNTIME_N: " using crun"
+ - env:
+ DISTRO_NV: "${DEBIAN_NAME}"
+ IMAGE_NAME: "${DEBIAN_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'overlay'
+ PRIV_NAME: rootless
+
+ gce_instance:
+ <<: *integration_gce_instance
+
+ # Separate scripts for separate outputs, makes debugging easier.
+ setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}'
+ build_script: '${SCRIPT_BASE}/build.sh |& ${_TIMESTAMP}'
+ integration_test_script: '${SCRIPT_BASE}/test.sh integration |& ${_TIMESTAMP}'
+
+ binary_artifacts:
+ path: ./bin/*
+
+ always:
+ <<: *standardlogs
+
+non_blocking_integration_rootless_task:
+ name: "Integration rootless $DISTRO_NV$RUNTIME_N w/ $STORAGE_DRIVER (non-blocking)"
+ alias: non_blocking_integration_rootless
+ skip: *not_build_docs
+ depends_on: *smoke_vendor
+
+ matrix:
+ - env:
+ DISTRO_NV: "${FEDORA_NAME}"
+ IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'overlay'
+ PRIV_NAME: rootless
+ BUILDAH_RUNTIME: runc
+ RUNTIME_N: " using runc"
+ - env:
+ DISTRO_NV: "${PRIOR_FEDORA_NAME}"
+ IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}"
+ STORAGE_DRIVER: 'overlay'
+ PRIV_NAME: rootless
+ BUILDAH_RUNTIME: runc
+ RUNTIME_N: " using runc"
+
+ gce_instance:
+ <<: *integration_gce_instance
+
+ # Separate scripts for separate outputs, makes debugging easier.
+ setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}'
+ build_script: '${SCRIPT_BASE}/build.sh |& ${_TIMESTAMP}'
+ integration_test_script: '${SCRIPT_BASE}/test.sh integration |& ${_TIMESTAMP}'
+
+ binary_artifacts:
+ path: ./bin/*
+
+ always:
+ <<: *standardlogs
+
+in_podman_task:
+ name: "Containerized Integration"
+ alias: in_podman
+ skip: *not_build_docs
+ depends_on: *smoke_vendor
+
+ gce_instance:
+ cpu: 8
+ memory: "8G"
+
+ env:
+ # This is key, cause the scripts to re-execute themselves inside a container.
+ IN_PODMAN: 'true'
+ BUILDAH_ISOLATION: 'chroot'
+ STORAGE_DRIVER: 'vfs'
+
+ # Separate scripts for separate outputs, makes debugging easier.
+ setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}'
+ build_script: '${SCRIPT_BASE}/build.sh |& ${_TIMESTAMP}'
+ integration_test_script: '${SCRIPT_BASE}/test.sh integration |& ${_TIMESTAMP}'
+
+ binary_artifacts:
+ path: ./bin/*
+
+ always:
+ <<: *standardlogs
+
+
+# Status aggregator for all tests. This task simply ensures a defined
+# set of tasks all passed, and allows confirming that based on the status
+# of this task.
+success_task:
+ # N/B: The prow merge-bot (tide) is sensitized to this exact name, DO NOT CHANGE IT.
+ # Ref: https://github.com/openshift/release/pull/48909
+ name: "Total Success"
+ alias: success
+
+ depends_on:
+ - meta
+ - smoke
+ - unit
+ - conformance
+ - vendor
+ - cross_build
+ - integration
+ - integration_rootless
+ - in_podman
+
+ container:
+ image: "quay.io/libpod/alpine:latest"
+ cpu: 1
+ memory: 1
+
+ env:
+ CIRRUS_SHELL: direct # execute command directly
+
+ clone_script: mkdir -p $CIRRUS_WORKING_DIR
+ script: /bin/true
diff --git a/vendor/github.com/containers/buildah/.codespellrc b/vendor/github.com/containers/buildah/.codespellrc
new file mode 100644
index 00000000..64a29fe6
--- /dev/null
+++ b/vendor/github.com/containers/buildah/.codespellrc
@@ -0,0 +1,3 @@
+[codespell]
+skip = ./vendor,./.git,./go.sum,./docs/*.1,./docker/AUTHORS,./CHANGELOG.md,./changelog.txt,./tests/tools/vendor,./tests/tools/go.mod,./tests/tools/go.sum
+ignore-words-list = fo,passt,secon,erro
diff --git a/vendor/github.com/containers/buildah/.gitignore b/vendor/github.com/containers/buildah/.gitignore
new file mode 100644
index 00000000..6884c670
--- /dev/null
+++ b/vendor/github.com/containers/buildah/.gitignore
@@ -0,0 +1,14 @@
+docs/buildah*.1
+docs/*.5
+/bin
+/buildah
+/imgtype
+/build/
+/tests/tools/build
+Dockerfile*
+!/tests/bud/*/Dockerfile*
+!/tests/conformance/**/Dockerfile*
+*.swp
+/result/
+internal/mkcw/embed/entrypoint_amd64.o
+internal/mkcw/embed/entrypoint_amd64
diff --git a/vendor/github.com/containers/buildah/.golangci.yml b/vendor/github.com/containers/buildah/.golangci.yml
new file mode 100644
index 00000000..c4d2eaed
--- /dev/null
+++ b/vendor/github.com/containers/buildah/.golangci.yml
@@ -0,0 +1,29 @@
+version: "2"
+
+run:
+ build-tags:
+ - apparmor
+ - seccomp
+ - selinux
+
+formatters:
+ enable:
+ - gofumpt
+
+linters:
+ enable:
+ - nolintlint
+ - revive
+ - unconvert
+ - unparam
+ - unused
+ - whitespace
+ exclusions:
+ presets:
+ - comments
+ - std-error-handling
+ settings:
+ staticcheck:
+ checks:
+ - all
+ - -QF1008 # https://staticcheck.dev/docs/checks/#QF1008 Omit embedded fields from selector expression.
diff --git a/vendor/github.com/containers/buildah/.packit.yaml b/vendor/github.com/containers/buildah/.packit.yaml
new file mode 100644
index 00000000..032e9e13
--- /dev/null
+++ b/vendor/github.com/containers/buildah/.packit.yaml
@@ -0,0 +1,149 @@
+---
+# See the documentation for more information:
+# https://packit.dev/docs/configuration/
+
+downstream_package_name: buildah
+upstream_tag_template: v{version}
+
+# These files get synced from upstream to downstream (Fedora / CentOS Stream) on every
+# propose-downstream job. This is done so tests maintained upstream can be run
+# downstream in Zuul CI and Bodhi.
+# Ref: https://packit.dev/docs/configuration#files_to_sync
+files_to_sync:
+ - src: rpm/gating.yaml
+ dest: gating.yaml
+ delete: true
+ - src: plans/
+ dest: plans/
+ delete: true
+ mkpath: true
+ - src: tests/tmt/
+ dest: tests/tmt/
+ delete: true
+ mkpath: true
+ - src: .fmf/
+ dest: .fmf/
+ delete: true
+ - .packit.yaml
+
+packages:
+ buildah-fedora:
+ pkg_tool: fedpkg
+ specfile_path: rpm/buildah.spec
+ buildah-centos:
+ pkg_tool: centpkg
+ specfile_path: rpm/buildah.spec
+ buildah-eln:
+ specfile_path: rpm/buildah.spec
+
+srpm_build_deps:
+ - make
+
+jobs:
+ - job: copr_build
+ trigger: pull_request
+ packages: [buildah-fedora]
+ notifications: &copr_build_failure_notification
+ failure_comment:
+ message: "Ephemeral COPR build failed. @containers/packit-build please check."
+ # Fedora aliases documentation: https://packit.dev/docs/configuration#aliases
+ # python3-fedora-distro-aliases provides `resolve-fedora-aliases` command
+ targets: &fedora_copr_targets
+ - fedora-all-x86_64
+ - fedora-all-aarch64
+ enable_net: true
+ # Disable osh diff scan until Go support is available
+ # Ref: https://github.com/openscanhub/known-false-positives/pull/30#issuecomment-2858698495
+ osh_diff_scan_after_copr_build: false
+
+ # Ignore until golang is updated in distro buildroot to 1.23.3+
+ - job: copr_build
+ trigger: ignore
+ packages: [buildah-eln]
+ notifications: *copr_build_failure_notification
+ targets:
+ fedora-eln-x86_64:
+ additional_repos:
+ - "https://kojipkgs.fedoraproject.org/repos/eln-build/latest/x86_64/"
+ fedora-eln-aarch64:
+ additional_repos:
+ - "https://kojipkgs.fedoraproject.org/repos/eln-build/latest/aarch64/"
+ enable_net: true
+
+ # Ignore until golang is updated in distro buildroot to 1.23.3+
+ - job: copr_build
+ trigger: ignore
+ packages: [buildah-centos]
+ notifications: *copr_build_failure_notification
+ targets: ¢os_copr_targets
+ - centos-stream-9-x86_64
+ - centos-stream-9-aarch64
+ - centos-stream-10-x86_64
+ - centos-stream-10-aarch64
+ enable_net: true
+
+ # Run on commit to main branch
+ - job: copr_build
+ trigger: commit
+ packages: [buildah-fedora]
+ notifications:
+ failure_comment:
+ message: "podman-next COPR build failed. @containers/packit-build please check."
+ branch: main
+ owner: rhcontainerbot
+ project: podman-next
+ enable_net: true
+
+ # Tests on Fedora for main branch PRs
+ - job: tests
+ trigger: pull_request
+ packages: [buildah-fedora]
+ targets:
+ - fedora-all-x86_64
+ tf_extra_params:
+ environments:
+ - artifacts:
+ - type: repository-file
+ id: https://copr.fedorainfracloud.org/coprs/rhcontainerbot/podman-next/repo/fedora-$releasever/rhcontainerbot-podman-next-fedora-$releasever.repo
+
+ # Ignore until golang is updated in distro buildroot to 1.23.3+
+ # Tests on CentOS Stream for main branch PRs
+ - job: tests
+ trigger: ignore
+ packages: [buildah-centos]
+ targets:
+ - centos-stream-9-x86_64
+ - centos-stream-10-x86_64
+ tf_extra_params:
+ environments:
+ - artifacts:
+ - type: repository-file
+ id: https://copr.fedorainfracloud.org/coprs/rhcontainerbot/podman-next/repo/centos-stream-$releasever/rhcontainerbot-podman-next-centos-stream-$releasever.repo
+
+ # Sync to Fedora
+ - job: propose_downstream
+ trigger: release
+ packages: [buildah-fedora]
+ update_release: false
+ dist_git_branches: &fedora_targets
+ - fedora-all
+
+ # Sync to CentOS Stream
+ - job: propose_downstream
+ trigger: release
+ packages: [buildah-centos]
+ update_release: false
+ dist_git_branches:
+ - c10s
+
+ # Fedora Koji build
+ - job: koji_build
+ trigger: commit
+ packages: [buildah-fedora]
+ sidetag_group: podman-releases
+ # Dependents are not rpm dependencies, but the package whose bodhi update
+ # should include this package.
+ # Ref: https://packit.dev/docs/fedora-releases-guide/releasing-multiple-packages
+ dependents:
+ - podman
+ dist_git_branches: *fedora_targets
diff --git a/vendor/github.com/containers/buildah/CHANGELOG.md b/vendor/github.com/containers/buildah/CHANGELOG.md
new file mode 100644
index 00000000..207d47bd
--- /dev/null
+++ b/vendor/github.com/containers/buildah/CHANGELOG.md
@@ -0,0 +1,3833 @@
+
+
+# Changelog
+
+## v1.42.2 (2025-12-02)
+
+ [release-1.42] Bump runc to v1.3.4
+
+## v1.42.1 (2025-11-06)
+
+ [release-1.42] bump runc to v1.3.3 - CVE-2025-52881
+ RPM: build with sequoia on F43+
+
+## v1.42.0 (2025-10-17)
+
+ Bump to storage v1.61.0, image v5.38.0, common v0.66.0
+ fix(deps): update module github.com/openshift/imagebuilder to v1.2.19
+ fix(deps): update module github.com/openshift/imagebuilder to v1.2.18
+ copier: ignore user.overlay.* xattrs
+ commit: always return the config digest as the image ID
+ fix(deps): update module golang.org/x/crypto to v0.43.0
+ fix(deps): update module golang.org/x/sys to v0.37.0
+ fix(deps): update module github.com/docker/docker to v28.5.1+incompatible
+ fix(deps): update module github.com/moby/buildkit to v0.25.1
+ fix(deps): update module github.com/opencontainers/runc to v1.3.2
+ fix(deps): update module github.com/docker/docker to v28.5.0+incompatible
+ fix(deps): update module github.com/moby/buildkit to v0.25.0
+ fix(deps): update github.com/containers/luksy digest to 2cf5bc9
+ Make some test files different from each other
+ Revert "fix(deps): update module github.com/cyphar/filepath-securejoin to v0.5.0"
+ Also run integration tests with the Sequoia backend
+ Allow users to build against podman-sequoia in non-default locations
+ fix(deps): update module github.com/cyphar/filepath-securejoin to v0.5.0
+ .cirrus.yml: Test Vendoring bump golang
+ vendor: bump go.podman.io/{common,image,storage} to main
+ fix(deps): update module golang.org/x/crypto to v0.42.0
+ fix(deps): update module github.com/docker/docker to v28.4.0+incompatible
+ fix(deps): update module github.com/moby/buildkit to v0.24.0
+ fix(deps): update module github.com/spf13/pflag to v1.0.10
+ fix(deps): update module github.com/fsouza/go-dockerclient to v1.12.2
+ fix(deps): update module github.com/opencontainers/runc to v1.3.1
+ fix(deps): update module github.com/opencontainers/cgroups to v0.0.5
+ fix(deps): update module golang.org/x/sync to v0.17.0
+ tests/run.bats: "run masks" test: accept "unreadable" masked directories
+ Run: create parent directories of mount targets with mode 0755
+ tests/run.bats: "run masks" test: accept "unreadable" masked directories
+ New VM images
+ Suppress a linter warning
+ modernize: JSON doesn't do "omitempty" structs, so stop asking
+ modernize: use maps.Copy() instead of iterating over a map to copy it
+ modernize: use strings.CutPrefix/SplitSeq/FieldsSeq
+ Update expected/minimum version of Go to 1.24
+ chroot: use $PATH when finding commands
+ [skip-ci] Update actions/stale action to v10
+ Update module github.com/ulikunitz/xz to v0.5.15 [SECURITY]
+ Update go.sum
+ New VM images
+ Update module github.com/openshift/imagebuilder to v1
+ Update module github.com/spf13/cobra to v1.10.1
+ Switch common, storage and image to monorepo.
+ Update module github.com/stretchr/testify to v1.11.1
+ Update module go.etcd.io/bbolt to v1.4.3
+ Handle tagged+digested references when processing --all-platforms
+ Update module github.com/stretchr/testify to v1.11.0
+ Add --transient-store global option
+ Support "--imagestore" global flags
+ Commit: don't depend on MountImage(), because .imagestore
+ Adding mohanboddu as community manager to MAINTAINERS.md
+ Rework how we decide what to filter out of layer diffs
+ Note that we have to build `true` first for the sake of its tests
+ copier.Stat(): return owner UID and GID if available
+ copier.Get(): ensure that directory entries end in "/"
+ copier.Get(): strip user and group names from entries
+ imagebuildah.Executor/StageExecutor: check numeric --from= values
+ Losen the dependency on go-connections/tlsconfig
+ fix(deps): update module golang.org/x/crypto to v0.41.0
+ fix(deps): update module golang.org/x/term to v0.34.0
+ fix(deps): update module github.com/docker/go-connections to v0.6.0
+ fix(deps): update module golang.org/x/sys to v0.35.0
+ copy: assume a destination with basename "." is a directory
+ generatePathChecksum: ignore ModTime, AccessTime and ChangeTime
+ fix(deps): update module github.com/seccomp/libseccomp-golang to v0.11.1
+ fix(deps): update module github.com/containers/common to v0.64.1
+ History should note unset-label, timestamp, and rewrite-timestamp
+ pkg/cli.GenBuildOptions(): don't hardwire optional bools
+ fix(deps): update module github.com/containers/image/v5 to v5.36.1
+ imagebuildah.StageExecutor.Execute: commit more "no instructions" cases
+ fix(deps): update module github.com/containers/storage to v1.59.1
+ Only suppress "noted" items when not squashing
+ Reap stray processes
+ fix(deps): update github.com/containers/luksy digest to 8fccf78
+ fix(deps): update module github.com/docker/docker to v28.3.3+incompatible
+ Restore the default meaning of `--pull` (should be `always`).
+ Test that pulled up parent directories are excluded at commit
+ Exclude pulled up parent directories at commit-time
+ copier.Ensure(): also return parent directories
+ copier.MkdirOptions: add ModTimeNew
+ fix(deps): update module github.com/containers/common to v0.64.0
+ Bump to Buildah v1.42.0-dev
+ fix(deps): update module github.com/spf13/pflag to v1.0.7
+ CI: make runc tests non-blocking
+ build,add: add support for corporate proxies
+
+## v1.41.0 (2025-07-16)
+
+ Bump to c/storage v1.59.0, c/image v5.36.0, ... c/common v0.64.0
+ stage_executor: check platform of cache candidates
+ fix(deps): update module golang.org/x/crypto to v0.40.0
+ fix(deps): update module golang.org/x/term to v0.33.0
+ fix(deps): update module golang.org/x/sync to v0.16.0
+ fix(deps): update module github.com/docker/docker to v28.3.2+incompatible
+ ADD/COPY --link support added
+ RPM/TMT: account for passwd binary moving to tests
+ buildah: move passwd command to tests
+ Update "bud with --cpu-shares" test, and rename it
+ Remove BUILDTAG btrfs_noversion as no longer effective
+ fix(deps): update module github.com/docker/docker to v28.3.1+incompatible
+ fix(deps): update module github.com/moby/buildkit to v0.23.2
+ fix(deps): update github.com/containers/luksy digest to bc60f96
+ chore(typos): fix typos
+ vendor: update c/{common,image,storage} to main
+ chore(deps): update module github.com/go-viper/mapstructure/v2 to v2.3.0 [security]
+ fix(deps): update module go.etcd.io/bbolt to v1.4.2
+ Update Neil Smith's GitHub username in MAINTAINERS.md
+ Accept SOURCE_DATE_EPOCH as a build-arg
+ fix(deps): update module github.com/docker/docker to v28.3.0+incompatible
+ Add conditional release-checking system test
+ info,inspect: use the "formats" package to get some builtins
+ Use containers/common's formats package instead of our own
+ build, commit: set the OCI ...created annotation on OCI images
+ commit: exclude parents of mount targets, too
+ run: clean up parents of mount targets, too
+ tarFilterer: always flush after writing
+ Builder: drop the TempVolumes field
+ Update module github.com/moby/buildkit to v0.23.1
+ Update module github.com/opencontainers/cgroups to v0.0.3
+ Add CommitOptions.OmitLayerHistoryEntry, for skipping the new bits
+ Update module github.com/fsouza/go-dockerclient to v1.12.1
+ conformance: use mirrored frontend and base images
+ commit-with-extra-files test: use $TEST_SCRATCH_DIR
+ fix(deps): update module github.com/moby/buildkit to v0.23.0
+ "root fs only mounted once" test: accept root with only the rw option
+ Run with --device /dev/fuse and not just -v /dev/fuse:/dev/fuse
+ CI: pass $BUILDAH_RUNTIME through to in-container test runs
+ CI: ensure rootless groups aren't duplicates
+ build: add support for --inherit-annotations
+ CI: give the rootless test user some supplemental groups
+ bud,run: runc does not support keep-groups
+ Fix lint issue in TestCommitCompression
+ Add a unit test for compression types in OCI images
+ Support zstd compression in image commit
+ fix(deps): update module go.etcd.io/bbolt to v1.4.1
+ rpm: build rpm with libsqlite3 tag
+ Makefile: use libsqlite3 build when possible
+ commit,build: --source-date-epoch/--timestamp omit identity label
+ docs: add --setopt "*.countme=false" to dnf examples
+ Builder.sbomScan(): don't break non-root scanners
+ build: --source-date-epoch/--timestamp use static hostname/cid
+ fix(deps): update module golang.org/x/crypto to v0.39.0
+ fix(deps): update module golang.org/x/sync to v0.15.0
+ build: add --source-date-epoch and --rewrite-timestamp flags
+ build,config: add support for --unsetannotation
+ commit: add --source-date-epoch and --rewrite-timestamp flags
+ fix(deps): update module github.com/openshift/imagebuilder to v1.2.16
+ vendor latest c/{common,image,storage}
+ Tweak our handling of variant values, again
+ Don't BuildRequires: ostree-devel
+ parse, validateExtraHost: honor Hostgateway in format
+ remove static nix build
+ Ensure extendedGlob returns paths in lexical order
+ CI: run integration tests on Fedora with both crun and runc
+ buildah-build(1): clarify that --cgroup-parent affects RUN instructions
+ runUsingRuntime: use named constants for runtime states
+ Add a dummy "runtime" that just dumps its config file
+ run: handle relabeling bind mounts ourselves
+ fix link to Maintainers file
+ Update to avoid deprecated types
+ fix(deps): update module github.com/docker/docker to v28.2.0+incompatible
+ [skip-ci] Packit: cleanup redundant targets and unused anchors
+ [skip-ci] Packit: set fedora-all after F40 EOL
+ Use Fedora 42 instead of 41 in that one conformance test
+ [CI:DOCS] README.md: add openssf passing badge
+ fix(deps): update module github.com/moby/buildkit to v0.22.0
+ copier: add Ensure and ConditionalRemove
+ [CI:DOCS] update a couple of lists in the build man page
+ build: allow --output to be specified multiple times
+ add: add a new --timestamp flag
+ tests/helpers.bash: add some helpers for parsing images
+ pkg/parse.GetBuildOutput(): use strings.Cut()
+ [skip-ci] Packit: Disable osh_diff_scan
+ internal/util.SetHas(): handle maps of [generic]generic
+ Refactor NewImageSource to add a manifest type abstraction (#5743)
+ [skip-ci] Packit: Ignore ELN and CentOS Stream jobs
+ imagebuildah: select most recent layer for cache
+ [CI:DOCS] Add CNCF roadmap, touchup other CNCF files
+ fix(deps): update module golang.org/x/crypto to v0.38.0
+ Fix typo in comment (#6167)
+ Support label_users in buildah
+ fix(deps): update module golang.org/x/sync to v0.14.0
+ fix(deps): update github.com/containers/luksy digest to 4bb4c3f
+ test/serve: fix a descriptor leak, add preliminary directory support
+ fix(deps): update module github.com/opencontainers/cgroups to v0.0.2
+ fix(deps): update module github.com/moby/buildkit to v0.21.1
+ Update to avoid deprecated types
+ fix(deps): update module github.com/opencontainers/runc to v1.3.0
+ Only filter if containerImageRef.created != nil
+ Drop superfluous cast
+ Remove UID/GID scrubbing.
+ fix(deps): update module github.com/seccomp/libseccomp-golang to v0.11.0
+ cirrus: turn prior fedora testing back on
+ chore(deps): update dependency containers/automation_images to v20250422
+ fix(deps): update module github.com/docker/docker to v28.1.1+incompatible
+ Bump to Buildah v1.41.0-dev
+ CI vendor_task: pin to go 1.23.3 for now
+ fix(deps): update module github.com/containers/common to v0.63.0
+
+## v1.40.0 (2025-04-17)
+
+ Bump c/storage to v1.58.0, c/image v5.35.0, c/common v0.63.0
+ fix(deps): update module github.com/docker/docker to v28.1.0+incompatible
+ fix(deps): update module github.com/containers/storage to v1.58.0
+ cirrus: make Total Success wait for rootless integration
+ chroot: use symbolic names when complaining about mount() errors
+ cli: hide the `completion` command instead of disabling it outright
+ Document rw and src options for --mount flag in buildah-run(1)
+ fix(deps): update module github.com/moby/buildkit to v0.21.0
+ build: add support for inherit-labels
+ chore(deps): update dependency golangci/golangci-lint to v2.1.0
+ .github: check_cirrus_cron work around github bug
+ stage_executor,getCreatedBy: expand buildArgs for sources correctly
+ Add a link to project governance and MAINTAINERS file
+ fix(deps): update github.com/containers/storage digest to b1d1b45
+ generateHostname: simplify
+ Use maps.Copy
+ Use slices.Concat
+ Use slices.Clone
+ Use slices.Contains
+ Use for range over integers
+ tests/testreport: don't copy os.Environ
+ Use any instead of interface{}
+ ci: add golangci-lint run with --tests=false
+ ci: add nolintlint, fix found issues
+ copier: rm nolint:unparam annotation
+ .golangci.yml: add unused linter
+ chroot: fix unused warnings
+ copier: fix unused warnings
+ tests/conformance: fix unused warning
+ ci: switch to golangci-lint v2
+ internal/mkcw: disable ST1003 warnings
+ tests/conformance: do not double import (fix ST1019)
+ cmd/buildah: don't double import (fix ST1019)
+ Do not capitalize error strings
+ cmd/buildah: do not capitalize error strings
+ tests/conformance: fix QF1012 warnings
+ tests/serve: fix QF1012 warning
+ Use strings.ReplaceAll to fix QF1004 warnings
+ Use switch to fix QF1003 warnings
+ Apply De Morgan's law to fix QF1001 warnings
+ Fix QF1007 staticcheck warnings
+ imagebuildah: fix revive warning
+ Rename max variable
+ tests/tools: install lint from binary, use renovate
+ fix(deps): update module github.com/containernetworking/cni to v1.3.0
+ Update Buildah issue template to new version and support podman build
+ fix(deps): update module golang.org/x/crypto to v0.37.0
+ stage_executor: reset platform in systemcontext for stages
+ fix(deps): update github.com/opencontainers/runtime-tools digest to 260e151
+ cmd/buildah: rm unused containerOutputUsingTemplate
+ cmd/buildah: rm unused getDateAndDigestAndSize
+ build: return ExecErrorCodeGeneric when git operation fails
+ add: report error while creating dir for URL source.
+ createPlatformContainer: drop MS_REMOUNT|MS_BIND
+ fix(deps): update module github.com/docker/docker to v28.0.3+incompatible
+ fix: bats won't fail on ! without cleverness
+ feat: use HistoryTimestamp, if set, for oci-archive entries
+ Allow extendedGlob to work with Windows paths
+ fix(deps): update module github.com/moby/buildkit to v0.20.2
+ fix(deps): update github.com/openshift/imagebuilder digest to e87e4e1
+ fix(deps): update module github.com/docker/docker to v28.0.2+incompatible
+ fix(deps): update module tags.cncf.io/container-device-interface to v1.0.1
+ chore(deps): update dependency containers/automation_images to v20250324
+ vendor: update github.com/opencontainers/selinux to v1.12.0
+ replace deprecated selinux/label calls
+ vendor: bump c/common to dbeb17e40c80
+ Use builtin arg defaults from imagebuilder
+ linux: accept unmask paths as glob values
+ vendor: update containers/common
+ Add --parents option for COPY in Dockerfiles
+ fix(deps): update module github.com/opencontainers/runc to v1.2.6
+ update go.sum from the previous commit
+ fix(deps): update module tags.cncf.io/container-device-interface to v1
+ chore(deps): update module golang.org/x/net to v0.36.0 [security]
+ packit: remove f40 from copr builds
+ cirrus: update to go 1.23 image
+ vendor bump to golang.org/x/crypto v0.36.0
+ cirrus: update PRIOR_FEDORA comment
+ github: remove cirrus rerun action
+ fix(deps): update module github.com/containers/common to v0.62.2
+ fix(deps): update module github.com/containers/image/v5 to v5.34.2
+ fix: close files properly when BuildDockerfiles exits
+ fix(deps): update module github.com/containers/storage to v1.57.2
+ stage_executor: history should include heredoc summary correctly
+ fix(deps): update module github.com/containers/common to v0.62.1
+ github: disable cron rerun action
+ fix(deps): update module github.com/moby/buildkit to v0.20.1
+ internal/mkcw.Archive(): use github.com/containers/storage/pkg/ioutils
+ [skip-ci] TMT: system tests
+ buildah-build.1.md: secret examples
+ fix(deps): update github.com/containers/luksy digest to 40bd943
+ fix(deps): update module github.com/opencontainers/image-spec to v1.1.1
+ fix(deps): update module github.com/containers/image/v5 to v5.34.1
+ Use UnparsedInstance.Manifest instead of ImageSource.GetManifest
+ fix(deps): update module github.com/opencontainers/runtime-spec to v1.2.1
+ tests/conformance/testdata/Dockerfile.add: update some URLs
+ Vendor imagebuilder
+ Fix source of OS, architecture and variant
+ chore(deps): update module github.com/go-jose/go-jose/v4 to v4.0.5 [security]
+ fix(deps): update module tags.cncf.io/container-device-interface to v0.8.1
+ fix(deps): update module github.com/moby/buildkit to v0.20.0
+ chroot createPlatformContainer: use MS_REMOUNT
+ conformance: make TestCommit and TestConformance parallel
+ cirrus: reduce task timeout
+ mkcw: mkcw_check_image use bats run_with_log
+ test: use /tmp as TMPDIR
+ heredoc: create temp subdirs for each build
+ test: heredoc remove python dependency from test
+ Support the containers.conf container_name_as_hostname option
+ fix(deps): update module github.com/opencontainers/runc to v1.2.5
+ fix(deps): update module github.com/spf13/cobra to v1.9.0
+ .cirrus: use more cores for smoke
+ Switch to the CNCF Code of Conduct
+ .cirrus: bump ci resources
+ fix(deps): update module golang.org/x/crypto to v0.33.0
+ Distinguish --mount=type=cache locations by ownership, too
+ fix(deps): update module golang.org/x/term to v0.29.0
+ .cirrus: run -race only on non-PR branch
+ unit: deparallize some tests
+ .cirrus: use multiple cpu for unit tests
+ Makefile: use -parallel for go test
+ unit_test: use Parallel test where possible
+ Update module golang.org/x/sys to v0.30.0
+ Update module golang.org/x/sync to v0.11.0
+ Update dependency containers/automation_images to v20250131
+ Bump to Buildah v1.40.0-dev
+
+## v1.39.0 (2025-01-31)
+
+ Bump c/storage v1.57.1, c/image 5.34.0, c/common v0.62.0
+ Update module github.com/containers/storage to v1.57.0
+ CI, .cirrus: parallelize containerized integration
+ ed's comment: cleanup
+ use seperate blobinfocache for flaky test
+ bump CI VMs to 4 CPUs (was: 2) for integration tests
+ cleanup, debug, and disable parallel in blobcache tests
+ bats tests - parallelize
+ pkg/overlay: cleanups
+ RPM: include check section to silence rpmlint
+ RPM: use default gobuild macro on RHEL
+ tests: remove masked /sys/dev/block check
+ vendor to latest c/{common,image,storage}
+ build, run: record hash or digest in image history
+ Accept image names as sources for cache mounts
+ Run(): always clean up options.ExternalImageMounts
+ refactor: replace golang.org/x/exp with stdlib
+ Update to c/image @main
+ fix broken doc link
+ run_freebsd.go: only import runtime-spec once
+ fix(deps): update module github.com/docker/docker to v27.5.1+incompatible
+ bump github.com/vbatts/tar-split
+ Add more checks to the --mount flag parsing logic
+ chroot mount flags integration test: copy binaries
+ fix(deps): update module github.com/moby/buildkit to v0.19.0
+ relabel(): correct a misleading parameter name
+ Fix TOCTOU error when bind and cache mounts use "src" values
+ define.TempDirForURL(): always use an intermediate subdirectory
+ internal/volume.GetBindMount(): discard writes in bind mounts
+ pkg/overlay: add a MountLabel flag to Options
+ pkg/overlay: add a ForceMount flag to Options
+ Add internal/volumes.bindFromChroot()
+ Add an internal/open package
+ fix(deps): update module github.com/containers/common to v0.61.1
+ fix(deps): update module github.com/containers/image/v5 to v5.33.1
+ [CI:DOCS] Touch up changelogs
+ fix(deps): update module github.com/docker/docker to v27.5.0+incompatible
+ copy-preserving-extended-attributes: use a different base image
+ fix(deps): update github.com/containers/luksy digest to a3a812d
+ chore(deps): update module golang.org/x/net to v0.33.0 [security]
+ fix(deps): update module golang.org/x/crypto to v0.32.0
+ New VM Images
+ fix(deps): update module github.com/opencontainers/runc to v1.2.4
+ fix(deps): update module github.com/docker/docker to v27.4.1+incompatible
+ fix(deps): update module github.com/containers/ocicrypt to v1.2.1
+ Add support for --security-opt mask and unmask
+ Allow cache mounts to be stages or additional build contexts
+ [skip-ci] RPM: cleanup changelog conditionals
+ fix(deps): update module github.com/cyphar/filepath-securejoin to v0.3.6
+ fix(deps): update module github.com/moby/buildkit to v0.18.2
+ Fix an error message in the chroot unit test
+ copier: use .PAXRecords instead of .Xattrs
+ chroot: on Linux, try to pivot_root before falling back to chroot
+ manifest add: add --artifact-annotation
+ Add context to an error message
+ Update module golang.org/x/crypto to v0.31.0
+ Update module github.com/opencontainers/runc to v1.2.3
+ Update module github.com/docker/docker to v27.4.0+incompatible
+ Update module github.com/cyphar/filepath-securejoin to v0.3.5
+ CI: don't build a binary in the unit tests task
+ CI: use /tmp for $GOCACHE
+ CI: remove dependencies on the cross-build task
+ CI: run cross-compile task with make -j
+ Update module github.com/docker/docker to v27.4.0-rc.4+incompatible
+ Update module github.com/moby/buildkit to v0.18.1
+ Update module golang.org/x/crypto to v0.30.0
+ Update golang.org/x/exp digest to 2d47ceb
+ Update github.com/opencontainers/runtime-tools digest to f7e3563
+ [skip-ci] Packit: remove rhel copr build jobs
+ [skip-ci] Packit: switch to fedora-all for copr
+ Update module github.com/stretchr/testify to v1.10.0
+ Update module github.com/moby/buildkit to v0.17.2
+ Makefile: use `find` to detect source files
+ Tests: make _prefetch() parallel-safe
+ Update module github.com/opencontainers/runc to v1.2.2
+ executor: allow to specify --no-pivot-root
+ Update module github.com/moby/sys/capability to v0.4.0
+ Makefile: mv codespell config to .codespellrc
+ Fix some codespell errors
+ Makefile,install.md: rm gopath stuff
+ Makefile: rm targets working on ..
+ build: rm exclude_graphdriver_devicemapper tag
+ Makefile: rm unused var
+ Finish updating to go 1.22
+ CI VMs: bump again
+ Bump to Buidah v1.39.0-dev
+ stage_executor: set avoidLookingCache only if mounting stage
+ imagebuildah: additionalContext is not a local built stage
+
+## v1.38.0 (2024-11-08)
+
+ Bump to c/common v0.61.0, c/image v5.33.0, c/storage v1.56.0
+ fix(deps): update module golang.org/x/crypto to v0.29.0
+ fix(deps): update module github.com/moby/buildkit to v0.17.1
+ fix(deps): update module github.com/containers/storage to v1.56.0
+ tests: skip two ulimit tests
+ CI VMs: bump f40 -> f41
+ tests/tools: rebuild tools when we change versions
+ tests/tools: update golangci-lint to v1.61.0
+ fix(deps): update module github.com/moby/buildkit to v0.17.0
+ Handle RUN --mount with relative targets and no configured workdir
+ tests: bud: make parallel-safe
+ fix(deps): update module github.com/opencontainers/runc to v1.2.1
+ fix(deps): update golang.org/x/exp digest to f66d83c
+ fix(deps): update github.com/opencontainers/runtime-tools digest to 6c9570a
+ tests: blobcache: use unique image name
+ tests: sbom: never write to cwd
+ tests: mkcw: bug fixes, refactor
+ deps: bump runc to v1.2.0
+ deps: switch to moby/sys/userns
+ tests/test_runner.sh: remove some redundancies
+ Integration tests: run git daemon on a random-but-bind()able port
+ fix(deps): update module github.com/opencontainers/selinux to v1.11.1
+ go.mod: remove unnecessary replace
+ Document more buildah build --secret options
+ Add support for COPY --exclude and ADD --exclude options
+ fix(deps): update github.com/containers/luksy digest to e2530d6
+ chore(deps): update dependency containers/automation_images to v20241010
+ fix(deps): update module github.com/cyphar/filepath-securejoin to v0.3.4
+ Properly validate cache IDs and sources
+ [skip-ci] Packit: constrain koji job to fedora package to avoid dupes
+ Audit and tidy OWNERS
+ fix(deps): update module golang.org/x/crypto to v0.28.0
+ tests: add quotes to names
+ vendor: update c/common to latest
+ CVE-2024-9407: validate "bind-propagation" flag settings
+ vendor: switch to moby/sys/capability
+ Don't set ambient capabilities
+ Document that zstd:chunked is downgraded to zstd when encrypting
+ fix(deps): update module github.com/cyphar/filepath-securejoin to v0.3.3
+ buildah-manifest-create.1: Fix manpage section
+ chore(deps): update dependency ubuntu to v24
+ Make `buildah manifest push --all` true by default
+ chroot: add newlines at the end of printed error messages
+ Do not error on trying to write IMA xattr as rootless
+ fix: remove duplicate conditions
+ fix(deps): update module github.com/moby/buildkit to v0.16.0
+ fix(deps): update module github.com/cyphar/filepath-securejoin to v0.3.2
+ Document how entrypoint is configured in buildah config
+ In a container, try to register binfmt_misc
+ imagebuildah.StageExecutor: clean up volumes/volumeCache
+ build: fall back to parsing a TARGETPLATFORM build-arg
+ `manifest add --artifact`: handle multiple values
+ Packit: split out ELN jobs and reuse fedora downstream targets
+ Packit: Enable sidetags for bodhi updates
+ fix(deps): update module github.com/docker/docker to v27.2.1+incompatible
+ tests/bud.bats: add git source
+ add: add support for git source
+ Add support for the new c/common pasta options
+ vendor latest c/common
+ fix(deps): update module golang.org/x/term to v0.24.0
+ fix(deps): update module github.com/fsouza/go-dockerclient to v1.12.0
+ packit: update fedora and epel targets
+ cirrus: disable f39 testing
+ cirrus: fix fedora names
+ update to go 1.22
+ Vendor c/common:9d025e4cb348
+ copier: handle globbing with "**" path components
+ fix(deps): update golang.org/x/exp digest to 9b4947d
+ fix(deps): update github.com/containers/luksy digest to 2e7307c
+ imagebuildah: make scratch config handling toggleable
+ fix(deps): update module github.com/docker/docker to v27.2.0+incompatible
+ Add a validation script for Makefile $(SOURCES)
+ fix(deps): update module github.com/openshift/imagebuilder to v1.2.15
+ New VMs
+ Update some godocs, use 0o to prefix an octal in a comment
+ buildah-build.1.md: expand the --layer-label description
+ fix(deps): update module github.com/containers/common to v0.60.2
+ run: fix a nil pointer dereference on FreeBSD
+ CI: enable the whitespace linter
+ Fix some govet linter warnings
+ Commit(): retry committing to local storage on storage.LayerUnknown
+ CI: enable the gofumpt linter
+ conformance: move weirdly-named files out of the repository
+ fix(deps): update module github.com/docker/docker to v27.1.2+incompatible
+ fix(deps): update module github.com/containers/common to v0.60.1
+ *: use gofmt -s, add gofmt linter
+ *: fix build tags
+ fix(deps): update module github.com/containers/image/v5 to v5.32.1
+ Add(): re-escape any globbed items that included escapes
+ conformance tests: use mirror.gcr.io for most images
+ unit tests: use test-specific policy.json and registries.conf
+ fix(deps): update module golang.org/x/sys to v0.24.0
+ Update to spun-out "github.com/containerd/platforms"
+ Bump github.com/containerd/containerd
+ test/tools/Makefile: duplicate the vendor-in-container target
+ linters: unchecked error
+ linters: don't end loop iterations with "else" when "then" would
+ linters: unused arguments shouldn't have names
+ linters: rename checkIdsGreaterThan5() to checkIDsGreaterThan5()
+ linters: don't name variables "cap"
+ `make lint`: use --timeout instead of --deadline
+ Drop the e2e test suite
+ fix(deps): update module golang.org/x/crypto to v0.26.0
+ fix(deps): update module github.com/onsi/gomega to v1.34.1
+ `make vendor-in-container`: use the caller's Go cache if it exists
+ fix(deps): fix test/tools ginkgo typo
+ fix(deps): update module github.com/onsi/ginkgo/v2 to v2.19.1
+ Update to keep up with API changes in storage
+ fix(deps): update github.com/containers/luksy digest to 1f482a9
+ install: On Debian/Ubuntu, add installation of libbtrfs-dev
+ fix(deps): update module golang.org/x/sys to v0.23.0
+ fix(deps): update golang.org/x/exp digest to 8a7402a
+ fix(deps): update module github.com/fsouza/go-dockerclient to v1.11.2
+ Use Epoch: 2 and respect the epoch in dependencies.
+ Bump to Buildah v1.38.0-dev
+ AddAndCopyOptions: add CertPath, InsecureSkipTLSVerify, Retry fields
+ Add PrependedLinkedLayers/AppendedLinkedLayers to CommitOptions
+ integration tests: teach starthttpd() about TLS and pid files
+
+## v1.37.0 (2024-07-26)
+
+ Bump c/storage, c/image, c/common for v1.37.0
+ "build with basename resolving user arg" tests: correct ARG use
+ bud-multiple-platform-no-run test: correct ARG use
+ imagebuildah: always have default values for $TARGET... args ready
+ bump github.com/openshift/imagebuilder to v1.2.14
+ fix(deps): update module github.com/docker/docker to v27.1.1+incompatible
+ fix(deps): update module github.com/cyphar/filepath-securejoin to v0.3.1
+ fix(deps): update module github.com/docker/docker to v27.1.0+incompatible
+ CI: use local registry, part 2 of 2
+ CI: use local registry, part 1 of 2
+ fix(deps): update module github.com/fsouza/go-dockerclient to v1.11.1
+ Revert "fix(deps): update github.com/containers/image/v5 to v5.31.1"
+ Replace libimage.LookupReferenceFunc with the manifests version
+ conformance tests: enable testing CompatVolumes
+ conformance tests: add a test that tries to chown a volume
+ imagebuildah: make traditional volume handling not the default
+ StageExecutor.prepare(): mark base image volumes for preservation
+ fix(deps): update module github.com/containers/image/v5 to v5.31.1
+ Vendor in latest containers/(common, storage, image)
+ fix(deps): update module golang.org/x/term to v0.22.0
+ fix(deps): update module golang.org/x/sys to v0.22.0
+ fix(deps): update golang.org/x/exp digest to 7f521ea
+ fix(deps): update github.com/containers/luksy digest to a8846e2
+ imagebuildah.StageExecutor.Copy(): reject new flags for now
+ bump github.com/openshift/imagebuilder to v1.2.11
+ Rework parsing of --pull flags
+ fix(deps): update module github.com/containers/image/v5 to v5.31.1
+ imagebuildah.StageExecutor.prepare(): log the --platform flag
+ CI VMs: bump
+ buildah copy: preserve owner info with --from= a container or image
+ conformance tests: enable testing CompatSetParent
+ containerImageRef.NewImageSource(): move the FROM comment to first
+ commit: set "parent" for docker format only when requested
+ Update godoc for Builder.EnsureContainerPathAs
+ fix(deps): update module github.com/spf13/cobra to v1.8.1
+ fix(deps): update module github.com/containernetworking/cni to v1.2.0
+ fix(deps): update module github.com/opencontainers/runc to v1.1.13
+ Change default for podman build to --pull missing
+ fix(deps): update module github.com/containers/common to v0.59.1
+ Clarify definition of --pull options
+ buildah: fix a nil pointer reference on FreeBSD
+ Use /var/tmp for $TMPDIR for vfs conformance jobs
+ Cirrus: run `df` during job setup
+ conformance: use quay.io/libpod/centos:7 instead of centos:8
+ Stop setting "parent" in docker format
+ conformance: check if workdir trims path separator suffixes
+ push integration test: pass password to docker login via stdin
+ Re-enable the "copy with chown" conformance test
+ healthcheck: Add support for `--start-interval`
+ fix(deps): update module github.com/docker/docker to v26.1.4+incompatible
+ fix(deps): update module github.com/containerd/containerd to v1.7.18
+ tests: set _CONTAINERS_USERNS_CONFIGURED=done for libnetwork
+ Cross-build on Fedora
+ Drop copyStringSlice() and copyStringStringMap()
+ fix(deps): update module golang.org/x/crypto to v0.24.0
+ fix(deps): update module github.com/openshift/imagebuilder to v1.2.10
+ Provide an uptime_netbsd.go
+ Spell unix as "!windows"
+ Add netbsd to lists-of-OSes
+ fix(deps): update golang.org/x/exp digest to fd00a4e
+ [skip-ci] Packit: enable c10s downstream sync
+ CI VMs: bump, to debian with cgroups v2
+ Document when BlobDirectory is overridden
+ fix secret mounts for env vars when using chroot isolation
+ Change to take a types.ImageReference arg
+ imagebuildah: Support custom image reference lookup for cache push/pull
+ fix(deps): update module github.com/onsi/ginkgo/v2 to v2.19.0
+ Bump to v1.37.0-dev
+ CI: Clarify Debian use for conformance tests
+
+## v1.36.0 (2024-05-23)
+
+ build: be more selective about specifying the default OS
+ Bump to c/common v0.59.0
+ Fix buildah prune --help showing the same example twice
+ fix(deps): update module github.com/onsi/ginkgo/v2 to v2.18.0
+ fix(deps): update module github.com/containers/image/v5 to v5.31.0
+ bud tests: fix breakage when vendoring into podman
+ Integration tests: fake up a replacement for nixery.dev/shell
+ copierWithSubprocess(): try to capture stderr on io.ErrClosedPipe
+ Don't expand RUN heredocs ourselves, let the shell do it
+ Don't leak temp files on failures
+ Add release note template to split dependency chores
+ fix CentOS/RHEL build - no BATS there
+ fix(deps): update module github.com/containers/luksy to v0.0.0-20240506205542-84b50f50f3ee
+ Address CVE-2024-3727
+ chore(deps): update module github.com/opencontainers/runtime-spec to v1.2.0
+ Builder.cdiSetupDevicesInSpecdefConfig(): use configured CDI dirs
+ Setting --arch should set the TARGETARCH build arg
+ fix(deps): update module golang.org/x/exp to v0.0.0-20240416160154-fe59bbe5cc7f
+ [CI:DOCS] Add link to Buildah image page to README.md
+ Don't set GOTOOLCHAIN=local
+ fix(deps): update module github.com/cyphar/filepath-securejoin to v0.2.5
+ Makefile: set GOTOOLCHAIN=local
+ Integration tests: switch some base images
+ containerImageRef.NewImageSource: merge the tar filters
+ fix(deps): update module github.com/onsi/ginkgo/v2 to v2.17.2
+ fix(deps): update module github.com/containers/luksy to v0.0.0-20240408185936-afd8e7619947
+ Disable packit builds for centos-stream+epel-next-8
+ Makefile: add missing files to $(SOURCES)
+ CI VMs: bump to new versions with tmpfs /tmp
+ chore(deps): update module golang.org/x/net to v0.23.0 [security]
+ integration test: handle new labels in "bud and test --unsetlabel"
+ Switch packit configuration to use epel-9-$arch ...
+ Give unit tests a bit more time
+ Integration tests: remove a couple of duplicated tests
+ Integration tests: whitespace tweaks
+ Integration tests: don't remove images at start or end of test
+ Integration tests: use cached images more
+ Integration tests _prefetch: use registry configs
+ internal: use fileutils.(Le|E)xists
+ pkg/parse: use fileutils.(Le|E)xists
+ buildah: use fileutils.(Le|E)xists
+ chroot: use fileutils.(Le|E)xists
+ vendor: update containers/(common|storage)
+ Fix issue/pr lock workflow
+ [CI:DOCS] Add golang 1.21 update warning
+ heredoc: honor inline COPY irrespective of ignorefiles
+ Update install.md
+ source-push: add support for --digestfile
+ Fix caching when mounting a cached stage with COPY/ADD
+ fix(deps): update github.com/containers/luksy digest to 3d2cf0e
+ Makefile: softcode `strip`, use it from env var
+ Man page updates
+ Add support for passing CDI specs to --device
+ Update comments on some API objects
+ pkg/parse.DeviceFromPath(): dereference src symlinks
+ fix(deps): update module github.com/onsi/ginkgo/v2 to v2.17.1
+
+## v1.35.0 (2024-03-06)
+
+ fix(deps): update module github.com/stretchr/testify to v1.9.0
+ cgroups: reuse version check from c/common
+ Update vendor of containers/(common,image)
+ fix(deps): update github.com/containers/storage digest to eadc620
+ fix(deps): update github.com/containers/luksy digest to ceb12d4
+ fix(deps): update github.com/containers/image/v5 digest to cdc6802
+ manifest add: complain if we get artifact flags without --artifact
+ Use retry logic from containers/common
+ Vendor in containers/(storage,image,common)
+ Update module golang.org/x/crypto to v0.20.0
+ Add comment re: Total Success task name
+ tests: skip_if_no_unshare(): check for --setuid
+ Properly handle build --pull=false
+ [skip-ci] Update tim-actions/get-pr-commits action to v1.3.1
+ Update module go.etcd.io/bbolt to v1.3.9
+ Revert "Reduce official image size"
+ Update module github.com/opencontainers/image-spec to v1.1.0
+ Reduce official image size
+ Build with CNI support on FreeBSD
+ build --all-platforms: skip some base "image" platforms
+ Bump main to v1.35.0-dev
+ Vendor in latest containers/(storage,image,common)
+ Split up error messages for missing --sbom related flags
+ `buildah manifest`: add artifact-related options
+ cmd/buildah/manifest.go: lock lists before adding/annotating/pushing
+ cmd/buildah/manifest.go: don't make struct declarations aliases
+ Use golang.org/x/exp/slices.Contains
+ Disable loong64 again
+ Fix a couple of typos in one-line comments
+ egrep is obsolescent; use grep -E
+ Try Cirrus with a newer VM version
+ Set CONTAINERS_CONF in the chroot-mount-flags integration test
+ Update to match dependency API update
+ Update github.com/openshift/imagebuilder and containers/common
+ docs: correct default authfile path
+ fix(deps): update module github.com/containerd/containerd to v1.7.13
+ tests: retrofit test for heredoc summary
+ build, heredoc: show heredoc summary in build output
+ manifest, push: add support for --retry and --retry-delay
+ fix(deps): update github.com/openshift/imagebuilder digest to b767bc3
+ imagebuildah: fix crash with empty RUN
+ fix(deps): update github.com/containers/luksy digest to b62d551
+ fix(deps): update module github.com/opencontainers/runc to v1.1.12 [security]
+ fix(deps): update module github.com/moby/buildkit to v0.12.5 [security]
+ Make buildah match podman for handling of ulimits
+ docs: move footnotes to where they're applicable
+ Allow users to specify no-dereference
+ Run codespell on code
+ Fix FreeBSD version parsing
+ Fix a build break on FreeBSD
+ Remove a bad FROM line
+ fix(deps): update module github.com/onsi/gomega to v1.31.1
+ fix(deps): update module github.com/opencontainers/image-spec to v1.1.0-rc6
+ docs: use reversed logo for dark theme in README
+ build,commit: add --sbom to scan and produce SBOMs when committing
+ commit: force omitHistory if the parent has layers but no history
+ docs: fix a couple of typos
+ internal/mkcw.Archive(): handle extra image content
+ stage_executor,heredoc: honor interpreter in heredoc
+ stage_executor,layers: burst cache if heredoc content is changed
+ fix(deps): update module golang.org/x/crypto to v0.18.0
+ Replace map[K]bool with map[K]struct{} where it makes sense
+ fix(deps): update module golang.org/x/sync to v0.6.0
+ fix(deps): update module golang.org/x/term to v0.16.0
+ Bump CI VMs
+ Replace strings.SplitN with strings.Cut
+ fix(deps): update github.com/containers/storage digest to ef81e9b
+ fix(deps): update github.com/containers/image/v5 digest to 1b221d4
+ fix(deps): update module github.com/fsouza/go-dockerclient to v1.10.1
+ Document use of containers-transports values in buildah
+ fix(deps): update module golang.org/x/crypto to v0.17.0 [security]
+ chore(deps): update dependency containers/automation_images to v20231208
+ manifest: addCompression use default from containers.conf
+ commit: add a --add-file flag
+ mkcw: populate the rootfs using an overlay
+ chore(deps): update dependency containers/automation_images to v20230517
+ [skip-ci] Update actions/stale action to v9
+ fix(deps): update module github.com/containernetworking/plugins to v1.4.0
+ fix(deps): update github.com/containers/image/v5 digest to 7a40fee
+ Bump to v1.34.1-dev
+ Ignore errors if label.Relabel returns ENOSUP
+
+## v1.34.0 (2023-12-11)
+
+ vendor: update c/{common,image,storage}
+ run: Allow using just one jail per container on FreeBSD
+ Remove makefile targets entrypoint{,.gz} for non x86_64
+
+## v1.33.2 (2023-11-22)
+
+ Update minimum to golang 1.20
+ fix(deps): update module github.com/fsouza/go-dockerclient to v1.10.0
+ fix(deps): update module github.com/moby/buildkit to v0.12.3
+ Bump to v1.33.2-dev
+
+## v1.33.1 (2023-11-18)
+
+ fix(deps): update module github.com/moby/buildkit to v0.11.4 [security]
+ test,heredoc: use fedora instead of docker.io/library/python:latest
+ Bump to v1.33.1-dev
+
+## v1.33.0 (2023-11-17)
+
+ Never omit layers for emptyLayer instructions when squashing/cwing
+ Add OverrideChanges and OverrideConfig to CommitOptions
+ buildah: add heredoc support for RUN, COPY and ADD
+ vendor: bump imagebuilder to v1.2.6-0.20231110114814-35a50d57f722
+ conformance tests: archive the context directory as 0:0 (#5171)
+ blobcacheinfo,test: blobs must be resued when pushing across registry
+ Bump c/storage v1.51.0, c/image v5.29.0, c/common v0.57.0
+ pkg/util.MirrorToTempFileIfPathIsDescriptor(): don't leak an fd
+ StageExecutor.Execute: force a commit for --unsetenv, too
+ Increase a copier+chroot test timeout
+ Add support for --compat-auth-file in login/logout
+ Update existing tests for error message change
+ Update c/image and c/common to latest
+ fix(deps): update module github.com/containerd/containerd to v1.7.9
+ build: downgrade to go 1.20
+ Add godoc for pkg/parse.GetTempDir
+ conformance tests: use go-dockerclient for BuildKit builds
+ Make TEE types case-insensitive
+ fix(deps): update module golang.org/x/crypto to v0.15.0
+ Tweak some help descriptions
+ Stop using DefaultNetworkSysctl and use containers.conf only
+ Implement ADD checksum flag #5135
+ vendor of openshift/imagebuilder #5135
+ Pass secrets from the host down to internal podman containers
+ Update cirrus and version of golang
+ image: replace GetStoreImage with ResolveReference
+ vendor: bump c/image to 373c52a9466f
+ pkg/parse.Platform(): minor simplification
+ createConfigsAndManifests: clear history before cw-specific logic
+ Use a constant definition instead of "scratch"
+ conformance: use require.NoErrorf() more
+ fix(deps): update module golang.org/x/term to v0.14.0
+ fix(deps): update module golang.org/x/sync to v0.5.0
+ fix(deps): update module github.com/spf13/cobra to v1.8.0
+ fix(deps): update module golang.org/x/sys to v0.14.0
+ fix(deps): update github.com/containers/common digest to 8354404
+ fix(deps): update module github.com/opencontainers/runc to v1.1.10
+ fix(deps): update github.com/containers/luksy digest to b5a7f79
+ Log the platform for build errors during multi-platform builds
+ Use mask definitions from containers/common
+ Vendor in latest containers/common
+ fix(deps): update module github.com/containerd/containerd to v1.7.8
+ fix(deps): update module go.etcd.io/bbolt to v1.3.8
+ container.conf: support attributed string slices
+ fix(deps): update module sigs.k8s.io/yaml to v1.4.0
+ Use cutil.StringInSlice rather then contains
+ Add --no-hostname option to buildah containers
+ vendor c/common: appendable containers.conf strings, Part 1
+ fix(deps): update module github.com/onsi/gomega to v1.28.1
+ chroot.setupChrootBindMounts: pay more attention to flags
+ chore(deps): update dependency containers/automation_images to v20231004
+ Vendor containers/common
+ chore(deps): update module golang.org/x/net to v0.17.0 [security]
+ run: use internal.GetTempDir with os.MkdirTemp
+ fix(deps): update module github.com/containerd/containerd to v1.7.7
+ imagebuildah,multi-stage: do not remove base images
+ gitignore: add mkcw binary
+ mkcw: remove entrypoint binaries
+ fix(deps): update module golang.org/x/crypto to v0.14.0
+ fix(deps): update module golang.org/x/sys to v0.13.0
+ fix(deps): update module golang.org/x/sync to v0.4.0
+ Update some comments related to confidential workload
+ Use the parent's image ID in the config that we pass to imagebuilder
+ fix(deps): update github.com/containers/common digest to 8892536
+ fix(deps): update github.com/containers/luksy digest to 6df88cb
+ bug: Ensure the mount type is always BindMount by default
+ Protocol can be specified with --port. Ex. --port 514/udp
+ fix(deps): update module github.com/onsi/gomega to v1.28.0
+ build,config: add support for --unsetlabel
+ tests/bud: add tests
+ [CI:BUILD] Packit: tag @containers/packit-build on copr build failures
+ stage_executor: allow images without layers
+ vendor of containers/common
+ Removing selinux_tag.sh as no longer needed after 580356f [NO NEW TESTS NEEDED]
+ add/copy: make sure we handle relative path names correctly
+ fix(deps): update module github.com/opencontainers/image-spec to v1.1.0-rc5
+ Bump to v1.33.0-dev
+ imagebuildah: consider ignorefile with --build-context
+
+## v1.32.0 (2023-09-14)
+
+ GetTmpDir is not using ImageCopyTmpdir correctly
+ Run codespell on code
+ Bump vendor containers/(common, storage, image)
+ Cirrus: Remove multi-arch buildah image builds
+ fix(deps): update module github.com/containerd/containerd to v1.7.6
+ Split GetTempDir from internal/util
+ Move most of internal/parse to internal/volumes
+ copier: remove libimage dependency via util package
+ Add some docs for `build --cw`, `commit --cw`, and `mkcw`
+ Add `buildah mkcw`, add `--cw` to `buildah commit` and `buildah build`
+ Make sure that pathnames picked up from the environment are absolute
+ fix(deps): update module github.com/cyphar/filepath-securejoin to v0.2.4
+ fix(deps): update module github.com/docker/docker to v24.0.6+incompatible
+ Don't try to look up names when committing images
+ fix(deps): update module golang.org/x/crypto to v0.13.0
+ docs: use valid github repo
+ fix(deps): update module golang.org/x/sys to v0.12.0
+ vendor containers/common@12405381ff45
+ push: --force-compression should be true with --compression-format
+ Update module github.com/containerd/containerd to v1.7.5
+ [skip-ci] Update tim-actions/commit-message-checker-with-regex action to v0.3.2
+ docs: add reference to oci-hooks
+ Support passing of ULimits as -1 to mean max
+ GHA: Attempt to fix discussion_lock workflow
+ Fixing the owner of the storage.conf.
+ pkg/chrootuser: Ignore comments when parsing /etc/group on FreeBSD
+ Use buildah repo rather then podman repo
+ GHA: Closed issue/PR comment-lock test
+ fix(deps): update module github.com/containers/storage to v1.49.0
+ chore(deps): update dependency containers/automation_images to v20230816
+ Replace troff code with markdown in buildah-{copy,add}.1.md
+ [CI:BUILD] rpm: spdx compatible license field
+ executor: build-arg warnings must honor global args
+ fix(deps): update module github.com/containers/ocicrypt to v1.1.8
+ chroot: `setSeccomp` add support for `ArchPARISC(64)` and `ArchRISCV64`
+ make,cross: restore loong64
+ Clear CommonBuildOpts when loading Builder status
+ buildah/push/manifest-push: add support for --force-compression
+ vendor: bump c/common to v0.55.1-0.20230811093040-524b4d5c12f9
+ chore(deps): update dependency containers/automation_images to v20230809
+ [CI:BUILD] RPM: fix buildtags
+ fix(deps): update module github.com/opencontainers/runc to v1.1.9
+ chore(deps): update dependency ubuntu to v22
+ chore(deps): update dependency containers/automation_images to v20230807
+ [CI:BUILD] Packit: add fedora-eln targets
+ [CI:BUILD] RPM: build docs with vendored go-md2man
+ packit: Build PRs into default packit COPRs
+ Update install.md
+ Update install.md changes current Debian stable version name
+ fix(deps): update module golang.org/x/term to v0.11.0
+ fix(deps): update module golang.org/x/crypto to v0.12.0
+ tests: fix layer-label tests
+ buildah: add --layer-label for setting labels on layers
+ Cirrus: container/rootless env. var. passthrough
+ Cirrus: Remove duplicate env. var. definitions
+ fix(deps): update github.com/containers/storage digest to c3da76f
+ Add a missing .Close() call on an ImageSource
+ Create only a reference when that's all we need
+ Add a missing .Close() call on an ImageDestination
+ CI:BUILD] RPM: define gobuild macro for rhel/centos stream
+ manifest/push: add support for --add-compression
+ manifest/inspect: add support for tls-verify and authfile
+ vendor: bump c/common to v0.55.1-0.20230727095721-647ed1d4d79a
+ vendor: bump c/image to v5.26.1-0.20230726142307-8c387a14f4ac
+ fix(deps): update module github.com/containerd/containerd to v1.7.3
+ fix(deps): update module github.com/onsi/gomega to v1.27.10
+ fix(deps): update module github.com/docker/docker to v24.0.5+incompatible
+ fix(deps): update module github.com/containers/image/v5 to v5.26.1
+ fix(deps): update module github.com/opencontainers/runtime-spec to v1.1.0
+ Update vendor of containers/(storage,image,common)
+ fix(deps): update module github.com/opencontainers/runc to v1.1.8
+ [CI:BUILD] Packit: remove pre-sync action
+ fix(deps): update module github.com/containers/common to v0.55.2
+ [CI:BUILD] Packit: downstream task script needs GOPATH
+ Vendor in containers/(common, image, storage)
+ fix(deps): update module golang.org/x/term to v0.10.0
+ [CI:BUILD] Packit: fix pre-sync action for downstream tasks
+ contrib/buildahimage: set config correctly for rootless build user
+ fix(deps): update module github.com/opencontainers/image-spec to v1.1.0-rc4
+ Bump to v1.32.0-dev
+ Update debian install instructions
+ pkg/overlay: add limited support for FreeBSD
+
+## v1.31.0 (2023-06-30)
+
+ Bump c/common to 0.55.1 and c/image to 5.26.1
+ Bump c/image to 5.26.0 and c/common to 0.54.0
+ vendor: update c/{common,image,storage} to latest
+ chore: pkg imported more than once
+ buildah: add pasta(1) support
+ use slirp4netns package from c/common
+ update c/common to latest
+ add hostname to /etc/hosts when running with host network
+ vendor: update c/common to latest
+ [CI:BUILD] Packit: add jobs for downstream Fedora package builds
+ fix(deps): update module golang.org/x/sync to v0.3.0
+ fix(deps): update module golang.org/x/crypto to v0.10.0
+ Add smoke tests for encryption CLI helpers
+ fix(deps): update module golang.org/x/term to v0.9.0
+ fix(deps): update module github.com/opencontainers/runtime-spec to v1.1.0-rc.3
+ Remove device mapper support
+ Remove use of deprecated tar.TypeRegA
+ Update tooling to support newer golangci-lint
+ Make cli.EncryptConfig,DecryptConfig, GetFormat public
+ Don't decrypt images by default
+ fix(deps): update module github.com/onsi/gomega to v1.27.8
+ fix(deps): update github.com/containers/storage digest to 3f3fb2f
+ Renovate: Don't touch fragile test stuffs
+ [CI:DOCS] Update comment to remove ambiguity
+ fix(deps): update github.com/containers/image/v5 digest to abe5133
+ fix(deps): update module github.com/sirupsen/logrus to v1.9.3
+ fix(deps): update module github.com/containerd/containerd to v1.7.2
+ Explicitly ref. quay images for CI
+ At startup, log the effective capabilities for debugging
+ parse: use GetTempDir from internal utils
+ GetTmpDir: honor image_copy_tmp_dir from containers.conf
+ docs/Makefile: don't show sed invocations
+ CI: Support testing w/ podman-next COPR packages
+ intermediate-images inherit-label test: make it debuggable
+ fix(deps): update github.com/containers/common digest to 462ccdd
+ Add a warning to `--secret` docs
+ vendor: bump c/storage to v1.46.2-0.20230526114421-55ee2d19292f
+ executor: apply label to only final stage
+ remove registry.centos.org
+ Go back to setting SysProcAttr.Pdeathsig for child processes
+ Fix auth.json path (validated on Fedora 38) wq Signed-off-by: Andreas Mack
+ fix(deps): update module github.com/stretchr/testify to v1.8.3
+ CI: fix test broken by renovatebot
+ chore(deps): update quay.io/libpod/testimage docker tag to v20221018
+ fix(deps): update module github.com/onsi/gomega to v1.27.7
+ test: use debian instead of docker.io/library/debian:testing-slim
+ vendor: bump logrus to 1.9.2
+ [skip-ci] Update tim-actions/get-pr-commits action to v1.3.0
+ Revert "Proof of concept: nightly dependency treadmill"
+ fix(deps): update module github.com/sirupsen/logrus to v1.9.1
+ vendor in containers/(common,storage,image)
+ fix(deps): update module github.com/docker/distribution to v2.8.2+incompatible
+ run: drop Pdeathsig
+ chroot: lock thread before setPdeathsig
+ tests: add a case for required=false
+ fix(deps): update module github.com/openshift/imagebuilder to v1.2.5
+ build: validate volumes on backend
+ secret: accept required flag w/o value
+ fix(deps): update module github.com/containerd/containerd to v1.7.1
+ fix(deps): update module golang.org/x/crypto to v0.9.0
+ Update the demos README file to fix minor typos
+ fix(deps): update module golang.org/x/sync to v0.2.0
+ fix(deps): update module golang.org/x/term to v0.8.0
+ manifest, push: use source as destination if not specified
+ run,mount: remove path only if they didnt pre-exist
+ Cirrus: Fix meta task failing to find commit
+ parse: filter edge-case for podman-remote
+ fix(deps): update module github.com/opencontainers/runc to v1.1.7
+ fix(deps): update module github.com/docker/docker to v23.0.5+incompatible
+ build: --platform must accept only arch
+ fix(deps): update module github.com/containers/common to v0.53.0
+ makefile: increase conformance timeout
+ Cap suffixDigitsModulo to a 9-digits suffix.
+ Rename conflict to suffixDigitsModulo
+ fix(deps): update module github.com/opencontainers/runtime-spec to v1.1.0-rc.2
+ fix(deps): update module github.com/opencontainers/runc to v1.1.6
+ chore(deps): update centos docker tag to v8
+ Clarify the need for qemu-user-static package
+ chore(deps): update quay.io/centos/centos docker tag to v8
+ Renovate: Ensure test/tools/go.mod is managed
+ Revert "buildah image should not enable fuse-overlayfs for rootful mode"
+ Bump to v1.31.0-dev
+ parse: add support for relabel bind mount option
+
+## v1.30.0 (2023-04-06)
+
+ fix(deps): update module github.com/opencontainers/runc to v1.1.5
+ fix(deps): update module github.com/fsouza/go-dockerclient to v1.9.7
+ buildah image should not enable fuse-overlayfs for rootful mode
+ stage_executor: inline network add default string
+ fix(deps): update module github.com/containers/common to v0.51.2
+ chore(deps): update dependency containers/automation_images to v20230330
+ fix(deps): update module github.com/docker/docker to v23.0.2+incompatible
+ chore(deps): update dependency containers/automation_images to v20230320
+ fix(deps): update module github.com/onsi/gomega to v1.27.6
+ fix(deps): update github.com/opencontainers/runtime-tools digest to e931285
+ [skip-ci] Update actions/stale action to v8
+ test: don't allow to override io.buildah.version
+ executor: only apply label on the final stage
+ Update docs/buildah-build.1.md
+ update build instruction for Ubuntu
+ code review
+ build: accept arguments from file with --build-arg-file
+ run_linux: Update heuristic for mounting /sys
+ [CI:BUILD] Packit: Enable Copr builds on PR and commit to main
+ fix(deps): update module github.com/fsouza/go-dockerclient to v1.9.6
+ Update to Go 1.18
+ Disable dependabot in favor of renovate
+ chore(deps): update dependency containers/automation_images to v20230314
+ Fix requiring tests on Makefile changes
+ Vendor in latest containers/(storage, common, image)
+ imagebuildah: set len(short_image_id) to 12
+ Re-enable conformance tests
+ Skip conformance test failures with Docker 23.0.1
+ Cirrus: Replace Ubuntu -> Debian SID
+ run: add support for inline --network in RUN stmt
+ vendor: bump imagebuilder to a3c3f8358ca31b1e4daa6
+ stage_executor: attempt to push cache only when cacheKey is valid
+ Add "ifnewer" as option in help message for pull command
+ build: document behaviour of buildah's distributed cache
+ fix(deps): update module golang.org/x/term to v0.6.0
+ Add default list of capabilities required to run buildah in a container
+ executor,copy: honor default ARG value while eval stage
+ sshagent: use ExtendedAgent instead of Agent
+ tests/bud: remove unwated test
+ executor: do not warn on builtin default args
+ executor: don't warn about unused TARGETARCH,TARGETOS,TARGETPLATFORM
+ Fix tutorial for rootless mode
+ Vendor in latest containers/(common, storage, image)
+ Ignore the base image's base image annotations
+ fix(deps): update module github.com/fsouza/go-dockerclient to v1.9.5
+ build(deps): bump github.com/containers/storage from 1.45.3 to 1.45.4
+ Vendor in latest containers/common
+ docs/tutorials/04: add defaults for Run()
+ imagebuildah.StageExecutor: suppress bogus "Pushing cache []:..."
+ executor: also add stage with no children to cleanupStages
+ [CI:BUILD] copr: fix el8 builds
+ Fix documentation on which Capabilities are allowed by default
+ Skip subject-length validation for renovate PRs
+ Temporarily hard-skip bud-multiple-platform-values test
+ fix(deps): update github.com/openshift/imagebuilder digest to 86828bf
+ build(deps): bump github.com/containerd/containerd from 1.6.16 to 1.6.17
+ build(deps): bump tim-actions/get-pr-commits from 1.1.0 to 1.2.0
+ build(deps): bump github.com/containers/image/v5 from 5.24.0 to 5.24.1
+ [skip-ci] Update tim-actions/get-pr-commits digest to 55b867b
+ build(deps): bump github.com/opencontainers/selinux
+ build(deps): bump golang.org/x/crypto from 0.5.0 to 0.6.0
+ Add renovate configuration
+ Run codespell on codebase
+ login: support interspersed args for password
+ conformance: use scratch for minimal test
+ pkg/parse: expose public CleanCacheMount API
+ build(deps): bump go.etcd.io/bbolt from 1.3.6 to 1.3.7
+ build(deps): bump github.com/containerd/containerd from 1.6.15 to 1.6.16
+ docs: specify order preference for FROM
+ Bump to v1.30.0-dev
+
+## v1.29.0 (2023-01-25)
+
+ tests: improve build-with-network-test
+ Bump c/storagev1.45.3, c/imagev5.24.0, c/commonv0.51.0
+ build(deps): bump github.com/onsi/gomega from 1.25.0 to 1.26.0
+ Flake 3710 has been closed. Reenable the test.
+ [CI:DOCS] Fix two diversity issues in a tutorial
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.9.2 to 1.9.3
+ vendor in latests containers/(storage, common, image)
+ fix bud-multiple-platform-with-base-as-default-arg flake
+ stage_executor: while mounting stages use freshly built stage
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.9.0 to 1.9.2
+ build(deps): bump github.com/onsi/gomega from 1.24.2 to 1.25.0
+ vendor in latests containers/(storage, common, image, ocicyrpt)
+ [Itests: change the runtime-flag test for crun
+ [CI:DOCS] README: drop sudo
+ Fix multi-arch manifest-list build timeouts
+ Cirrus: Update VM Images
+ bud: Consolidate multiple synthetic LABEL instructions
+ build, secret: allow realtive mountpoints wrt to work dir
+ fixed squash documentation
+ build(deps): bump github.com/containerd/containerd from 1.6.14 to 1.6.15
+ Correct minor comment
+ Vendor in latest containers/(common, image, storage)
+ system tests: remove unhelpful assertions
+ buildah: add prune command and expose CleanCacheMount API
+ vendor: bump c/storage to a747b27
+ Add support for --group-add to buildah from
+ build(deps): bump actions/stale from 6 to 7
+ Add documentation for buildah build --pull=missing
+ build(deps): bump github.com/containerd/containerd from 1.6.12 to 1.6.14
+ build(deps): bump github.com/docker/docker
+ parse: default ignorefile must not point to symlink outside context
+ buildah: wrap network setup errors
+ build, mount: allow realtive mountpoints wrt to work dir
+ Update to F37 CI VM Images, re-enable prior-fedora
+ Update vendor or containers/(image, storage, common)
+ build(deps): bump golang.org/x/crypto from 0.3.0 to 0.4.0
+ Update contact information
+ build(deps): bump golang.org/x/term from 0.2.0 to 0.3.0
+ Replace io/ioutil calls with os calls
+ [skip-ci] GHA/Cirrus-cron: Fix execution order
+ Vendor in containers/common
+ build(deps): bump golang.org/x/sys from 0.2.0 to 0.3.0
+ remote-cache: support multiple sources and destinations
+ Update c/storage after https://github.com/containers/storage/pull/1436
+ util.SortMounts(): make the returned order more stable
+ version: Bump to 1.29.0-dev
+ [CI:BUILD] Cirrus: Migrate OSX task to M1
+ Update vendor of containers/(common, storage, image)
+ mount=type=cache: seperate cache parent on host for each user
+ Fix installation instructions for Gentoo Linux
+ build(deps): bump github.com/containerd/containerd from 1.6.9 to 1.6.10
+ GHA: Reuse both cirrus rerun and check workflows
+ Vendor in latest containers/(common,image,storage)
+ build(deps): bump github.com/onsi/gomega from 1.24.0 to 1.24.1
+ copier.Put(): clear up os/syscall mode bit confusion
+ build(deps): bump golang.org/x/sys from 0.1.0 to 0.2.0
+ Use TypeBind consistently to name bind/nullfs mounts
+ Add no-new-privileges flag
+ Update vendor of containers/(common, image, storage)
+ imagebuildah:build with --all-platforms must honor args for base images
+ codespell code
+ Expand args and env when using --all-platforms
+ build(deps): bump github.com/onsi/gomega from 1.23.0 to 1.24.0
+ GHA: Simplify Cirrus-Cron check slightly
+ Stop using ubi8
+ remove unnecessary (hence misleading) rmi
+ chroot: fix mounting of ro bind mounts
+ executor: honor default ARG value while eval base name
+ userns: add arbitrary steps/stage to --userns=auto test
+ Don't set allow.mount in the vnet jail on Freebsd
+ copier: Preserve file flags when copying archives on FreeBSD
+ Remove quiet flag, so that it works in podman-remote
+ test: fix preserve rootfs with --mount for podman-remote
+ test: fix prune logic for cache-from after adding content summary
+ vendor in latest containers/(storage, common, image)
+ Fix RUN --mount=type=bind,from= not preserving rootfs of stage
+ Define and use a safe, reliable test image
+ Fix word missing in Container Tools Guide
+ Makefile: Use $(MAKE) to start sub-makes in install.tools
+ imagebuildah: pull cache from remote repo after adding content summary
+ Makefile: Fix install on FreeBSD
+ Ensure the cache volume locks are unlocked on all paths
+ Vendor in latest containers/(common,storage)
+ Simplify the interface of GetCacheMount and getCacheMount
+ Fix cache locks with multiple mounts
+ Remove calls to Lockfile.Locked()
+ Maintain cache mount locks as lock objects instead of paths
+ test: cleaning cache must not clean lockfiles
+ run: honor lockfiles for multiple --mount instruction
+ mount,cache: lockfiles must not be part of users cache content
+ Update vendor containers/(common,image,storage)
+ [CI:BUILD] copr: buildah rpm should depend on containers-common-extra
+ pr-should-include-tests: allow specfile, golangci
+ build(deps): bump dawidd6/action-send-mail from 3.7.0 to 3.7.1
+ build(deps): bump github.com/docker/docker
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.8.3 to 1.9.0
+ Update vendor containers/(common,image,storage)
+ build(deps): bump actions/upload-artifact from 2 to 3
+ build(deps): bump actions/checkout from 2 to 3
+ build(deps): bump actions/stale from 1 to 6
+ build(deps): bump dawidd6/action-send-mail from 2.2.2 to 3.7.0
+ build(deps): bump tim-actions/get-pr-commits from 1.1.0 to 1.2.0
+ sshagent: LockOSThread before setting SocketLabel
+ Update tests for error message changes
+ Update c/image after https://github.com/containers/image/pull/1299
+ Fix ident for dependabot gha block
+ build(deps): bump github.com/containers/ocicrypt from 1.1.5 to 1.1.6
+ Fix man pages to match latest cobra settings
+ build(deps): bump github.com/spf13/cobra from 1.5.0 to 1.6.0
+ build(deps): bump github.com/onsi/gomega from 1.20.2 to 1.22.1
+ test: retrofit 'bud with undefined build arg directory'
+ imagebuildah: warnOnUnsetBuildArgs while processing stages from executor
+ Update contrib/buildahimage/Containerfile
+ Cirrus CI add flavor parameter
+ Correction - `FLAVOR` not `FLAVOUR`
+ Changed build argument from `RELEASE` to `FLAVOUR`
+ Combine buildahimage Containerfiles
+ bud.bats refactoring: $TEST_SCRATCH_DIR, part 2 of 2
+ bud.bats refactoring: $TEST_SCRATCH_DIR, part 1 of 2
+ System test cleanup: document, clarify, fix
+ test: removing unneeded/expensive COPY
+ test: warning behaviour for unset/set TARGETOS,TARGETARCH,TARGETPLATFORM
+ Bump to v1.28.1-dev
+
+## v1.28.0 (2022-09-30)
+
+ Update vendor containers/(common,image)
+ [CI:DOCS] Add quay-description update reminder
+ vendor: bump c/common to v0.49.2-0.20220929111928-2d1b45ae2423
+ build(deps): bump github.com/opencontainers/selinux
+ Vendor in latest containers/storage
+ Changing shell list operators from `;` to `&&`
+ Fix buildahimage container.conf permissions regression
+ Set sysctls from containers.conf
+ refactor: stop using Normalize directly from containerd package
+ config,builder: process variant while populating image spec
+ Proof of concept: nightly dependency treadmill
+ Run codespell on code
+ Check for unset build args after TARGET args
+ pkg/cli: improve completion test
+ vendor in latest containers/(common,storage,image)
+ copier: work around freebsd bug for "mkdir /"
+ vendor: update c/image
+ test: run in the host cgroup namespace
+ vendor: update c/storage
+ vendor: update c/common
+ cmd: check for user UID instead of privileges
+ run,build: conflict --isolation=chroot and --network
+ Fix broken dns test (from merge collision)
+ Fix stutters
+ Fix broken command completion
+ buildah bud --network=none should have no network
+ build: support --skip-unused-stages for multi-stage builds
+ Prevent use of --dns* options with --net=none
+ buildah: make --cache-ttl=0s equivalent to --no-cache
+ parse: make processing flags in --mount order agnostic
+ Minor test fix for podman-remote
+ build: honor .containerignore as ignore file
+ Update install.md: Debian 11 (Bullseye) is stable
+ build(deps): bump github.com/docker/docker
+ Use constants from containers/common for finding seccomp.json
+ Don't call os.Exit(1) from manifest exist
+ manifest: add support for buildah manifest exists
+ Buildah should ignore /etc/crio/seccomp.json
+ chroot: Fix cross build break
+ chroot: Move isDevNull to run_common.go
+ chroot: Fix setRlimit build on FreeBSD
+ chroot: Move parseRLimits and setRlimits to run_common.go
+ chroot: Fix runUsingChrootExecMain on FreeBSD
+ chroot: Move runUsingChrootExecMain to run_common.go
+ chroot: Factor out Linux-specific unshare options from runUsingChroot
+ chroot: Move runUsingChroot to run_common.go
+ chroot: Move RunUsingChroot and runUsingChrootMain to run_common.go
+ chroot: Factor out /dev/ptmx pty implementation
+ chroot: Add FreeBSD support for run with chroot isolation
+ build(deps): bump github.com/docker/go-units from 0.4.0 to 0.5.0
+ Replace k8s.gcr.io/pause in tests with registry.k8s.io/pause
+ build(deps): bump github.com/onsi/gomega from 1.20.0 to 1.20.1
+ Cirrus: use image with fewer downloaded dependencies
+ build(deps): bump github.com/opencontainers/runc from 1.1.3 to 1.1.4
+ run: add container gid to additional groups
+ buildah: support for --retry and --retry-delay for push/pull failures
+ Makefile: always call $(GO) instead of `go`
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.8.2 to 1.8.3
+ test: use `T.TempDir` to create temporary test directory
+ mount,cache: enable SElinux shared content label option by default
+ commit: use race-free RemoveNames instead of SetNames
+ Drop util/util.Cause()
+ cmd/buildah: add "manifest create --amend"
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.8.1 to 1.8.2
+ docs: specify git protocol is not supported for github hosted repo
+ Scrub user and group names from layer diffs
+ build(deps): bump github.com/containerd/containerd from 1.6.6 to 1.6.8
+ version: bump to 1.28.0-dev
+
+## v1.27.0 (2022-08-01)
+
+ build: support filtering cache by duration using `--cache-ttl`.
+ build: support building from commit when using git repo as build context.
+ build: clean up git repos correctly when using subdirs.
+ build: add support for distributing cache to remote sources using `--cache-to` and `--cache-from`.
+ imagebuildah: optimize cache hits for `COPY` and `ADD` instructions.
+ build: support OCI hooks for ephemeral build containers.
+ build: add support for `--userns=auto`.
+ copier: add NoOverwriteNonDirDir option .
+ add initial support for building images using Buildah on FreeBSD.
+ multistage: this now skips the computing of unwanted stages to improve performance.
+ multiarch: support splitting build logs for `--platform` using `--logsplit`.
+ build: add support for building images where the base image has no history.
+ commit: allow disabling image history with `--omit-history`.
+ build: add support for renaming a device in rootless setups.
+ build: now supports additionalBuildContext in builds via the `--build-context` option.
+ build: `--output` produces artifacts even if the build container is not committed.
+ build: now accepts `-cpp-flag`, allowing users to pass in CPP flags when processing a Containerfile with C Preprocessor-like syntax.
+ build: now accepts a branch and a subdirectory when the build context is a git repository.
+ build: output now shows a progress bar while pushing and pulling images
+ build: now errors out if the path to Containerfile is a directory.
+ build: support building container images on environments that are rootless and without any valid login sessions.
+ fix: `--output` now generates artifacts even if the entire build is cached.
+ fix: `--output` generates artifacts only for the target stage in multi-stage builds.
+ fix,add: now fails on a bad HTTP response instead of writing to container
+ fix,squash: never use build cache when computing the last step of the last stage
+ fix,build,run: allow reusing secret more than once in different RUN steps
+ fix: compatibility with Docker build by making its --label and --annotate options set empty labels and annotations when given a name but no `=` or label value.
+
+## v1.26.0 (2022-05-04)
+
+ imagebuildah,build: move deepcopy of args before we spawn goroutine
+ Vendor in containers/storage v1.40.2
+ buildah.BuilderOptions.DefaultEnv is ignored, so mark it as deprecated
+ help output: get more consistent about option usage text
+ Handle OS version and features flags
+ buildah build: --annotation and --label should remove values
+ buildah build: add a --env
+ buildah: deep copy options.Args before performing concurrent build/stage
+ test: inline platform and builtinargs behaviour
+ vendor: bump imagebuilder to master/009dbc6
+ build: automatically set correct TARGETPLATFORM where expected
+ build(deps): bump github.com/fsouza/go-dockerclient
+ Vendor in containers/(common, storage, image)
+ imagebuildah, executor: process arg variables while populating baseMap
+ buildkit: add support for custom build output with --output
+ Cirrus: Update CI VMs to F36
+ fix staticcheck linter warning for deprecated function
+ Fix docs build on FreeBSD
+ build(deps): bump github.com/containernetworking/cni from 1.0.1 to 1.1.0
+ copier.unwrapError(): update for Go 1.16
+ copier.PutOptions: add StripSetuidBit/StripSetgidBit/StripStickyBit
+ copier.Put(): write to read-only directories
+ build(deps): bump github.com/cpuguy83/go-md2man/v2 in /tests/tools
+ Rename $TESTSDIR (the plural one), step 4 of 3
+ Rename $TESTSDIR (the plural one), step 3 of 3
+ Rename $TESTSDIR (the plural one), step 2 of 3
+ Rename $TESTSDIR (the plural one), step 1 of 3
+ build(deps): bump github.com/containerd/containerd from 1.6.2 to 1.6.3
+ Ed's periodic test cleanup
+ using consistent lowercase 'invalid' word in returned err msg
+ Update vendor of containers/(common,storage,image)
+ use etchosts package from c/common
+ run: set actual hostname in /etc/hostname to match docker parity
+ update c/common to latest main
+ Update vendor of containers/(common,storage,image)
+ Stop littering
+ manifest-create: allow creating manifest list from local image
+ Update vendor of storage,common,image
+ Bump golang.org/x/crypto to 7b82a4e
+ Initialize network backend before first pull
+ oci spec: change special mount points for namespaces
+ tests/helpers.bash: assert handle corner cases correctly
+ buildah: actually use containers.conf settings
+ integration tests: learn to start a dummy registry
+ Fix error check to work on Podman
+ buildah build should accept at most one arg
+ tests: reduce concurrency for flaky bud-multiple-platform-no-run
+ vendor in latest containers/common,image,storage
+ manifest-add: allow override arch,variant while adding image
+ Remove a stray `\` from .containerenv
+ Vendor in latest opencontainers/selinux v1.10.1
+ build, commit: allow removing default identity labels
+ Create shorter names for containers based on image IDs
+ test: skip rootless on cgroupv2 in root env
+ fix hang when oci runtime fails
+ Set permissions for GitHub actions
+ copier test: use correct UID/GID in test archives
+ run: set parent-death signals and forward SIGHUP/SIGINT/SIGTERM
+ Bump back to v1.26.0-dev
+ build(deps): bump github.com/opencontainers/runc from 1.1.0 to 1.1.1
+ Included the URL to check the SHA
+
+## v1.25.1 (2022-03-30)
+
+ buildah: create WORKDIR with USER permissions
+ vendor: update github.com/openshift/imagebuilder
+ copier: attempt to open the dir before adding it
+ Updated dependabot to get updates for GitHub actions.
+ Switch most calls to filepath.Walk to filepath.WalkDir
+ build: allow --no-cache and --layers so build cache can be overrided
+ build(deps): bump github.com/onsi/gomega from 1.18.1 to 1.19.0
+ Bump to v1.26.0-dev
+ build(deps): bump github.com/golangci/golangci-lint in /tests/tools
+
+## v1.25.0 (2022-03-25)
+
+ install: drop RHEL/CentOS 7 doc
+ build(deps): bump github.com/containers/common from 0.47.4 to 0.47.5
+ Bump c/storage to v1.39.0 in main
+ Add a test for CVE-2022-27651
+ build(deps): bump github.com/docker/docker
+ Bump github.com/prometheus/client_golang to v1.11.1
+ [CI:DOCS] man pages: sort flags, and keep them that way
+ build(deps): bump github.com/containerd/containerd from 1.6.1 to 1.6.2
+ Don't pollute
+ network setup: increase timeout to 4 minutes
+ do not set the inheritable capabilities
+ build(deps): bump github.com/golangci/golangci-lint in /tests/tools
+ build(deps): bump github.com/containers/ocicrypt from 1.1.2 to 1.1.3
+ parse: convert exposed GetVolumes to internal only
+ buildkit: mount=type=cache support locking external cache store
+ .in support: improve error message when cpp is not installed
+ buildah image: install cpp
+ build(deps): bump github.com/stretchr/testify from 1.7.0 to 1.7.1
+ build(deps): bump github.com/spf13/cobra from 1.3.0 to 1.4.0
+ build(deps): bump github.com/docker/docker
+ Add --no-hosts flag to eliminate use of /etc/hosts within containers
+ test: remove skips for rootless users
+ test: unshare mount/umount if test is_rootless
+ tests/copy: read correct containers.conf
+ build(deps): bump github.com/docker/distribution
+ cirrus: add seperate task and matrix for rootless
+ tests: skip tests for rootless which need unshare
+ buildah: test rootless integration
+ vendor: bump c/storage to main/93ce26691863
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.7.9 to 1.7.10
+ tests/copy: initialize the network, too
+ [CI:DOCS] remove references to Kubic for CentOS and Ubuntu
+ build(deps): bump github.com/containerd/containerd from 1.6.0 to 1.6.1
+ use c/image/pkg/blobcache
+ vendor c/image/v5@v5.20.0
+ add: ensure the context directory is an absolute path
+ executor: docker builds must inherit healthconfig from base if any
+ docs: Remove Containerfile and containeringore
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.7.8 to 1.7.9
+ helpers.bash: Use correct syntax
+ speed up combination-namespaces test
+ build(deps): bump github.com/golangci/golangci-lint in /tests/tools
+ Bump back to 1.25.0-dev
+ build(deps): bump github.com/containerd/containerd from 1.5.9 to 1.6.0
+
+## v1.24.2 (2022-02-16)
+
+ Increase subuid/subgid to 65535
+ history: only add proxy vars to history if specified
+ run_linux: use --systemd-cgroup
+ buildah: new global option --cgroup-manager
+ Makefile: build with systemd when available
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.7.7 to 1.7.8
+ Bump c/common to v0.47.4
+ Cirrus: Use updated VM images
+ conformance: add a few "replace-directory-with-symlink" tests
+ Bump back to v1.25.0-dev
+
+## v1.24.1 (2022-02-03)
+
+ executor: Add support for inline --platform within Dockerfile
+ caps: fix buildah run --cap-add=all
+ Update vendor of openshift/imagebuilder
+ Bump version of containers/image and containers/common
+ Update vendor of containers/common
+ System tests: fix accidental vandalism of source dir
+ build(deps): bump github.com/containers/storage from 1.38.1 to 1.38.2
+ imagebuildah.BuildDockerfiles(): create the jobs semaphore
+ build(deps): bump github.com/onsi/gomega from 1.18.0 to 1.18.1
+ overlay: always honor mountProgram
+ overlay: move mount program invocation to separate function
+ overlay: move mount program lookup to separate function
+ Bump to v1.25.0-dev [NO TESTS NEEDED]
+
+## v1.24.0 (2022-01-26)
+
+ Update vendor of containers/common
+ build(deps): bump github.com/golangci/golangci-lint in /tests/tools
+ Github-workflow: Report both failures and errors.
+ build(deps): bump github.com/containers/image/v5 from 5.18.0 to 5.19.0
+ Update docs/buildah-build.1.md
+ [CI:DOCS] Fix typos and improve language
+ buildah bud --network add support for custom networks
+ Make pull commands be consistent
+ docs/buildah-build.1.md: don't imply that -v isn't just a RUN thing
+ build(deps): bump github.com/onsi/gomega from 1.17.0 to 1.18.0
+ Vendor in latest containers/image
+ Run codespell on code
+ .github/dependabot.yml: add tests/tools go.mod
+ CI: rm git-validation, add GHA job to validate PRs
+ tests/tools: bump go-md2man to v2.0.1
+ tests/tools/Makefile: simplify
+ tests/tools: bump onsi/ginkgo to v1.16.5
+ vendor: bump c/common and others
+ mount: add support for custom upper and workdir with overlay mounts
+ linux: fix lookup for runtime
+ overlay: add MountWithOptions to API which extends support for advanced overlay
+ Allow processing of SystemContext from FlagSet
+ .golangci.yml: enable unparam linter
+ util/resolveName: rm bool return
+ tests/tools: bump golangci-lint
+ .gitignore: fixups
+ all: fix capabilities.NewPid deprecation warnings
+ bind/mount.go: fix linter comment
+ all: fix gosimple warning S1039
+ tests/e2e/buildah_suite_test.go: fix gosimple warnings
+ imagebuildah/executor.go: fix gosimple warning
+ util.go: fix gosimple warning
+ build(deps): bump github.com/opencontainers/runc from 1.0.3 to 1.1.0
+ Enable git-daemon tests
+ Allow processing of id options from FlagSet
+ Cirrus: Re-order tasks for more parallelism
+ Cirrus: Freshen VM images
+ Fix platform handling for empty os/arch values
+ Allow processing of network options from FlagSet
+ Fix permissions on secrets directory
+ Update containers/image and containers/common
+ bud.bats: use a local git daemon for the git protocol test
+ Allow processing of common options from FlagSet
+ Cirrus: Run int. tests in parallel with unit
+ vendor c/common
+ Fix default CNI paths
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.7.6 to 1.7.7
+ multi-stage: enable mounting stages across each other with selinux enabled
+ executor: Share selinux label of first stage with other stages in a build
+ buildkit: add from field to bind and cache mounts so images can be used as source
+ Use config.ProxyEnv from containers/common
+ use libnetwork from c/common for networking
+ setup the netns in the buildah parent process
+ build(deps): bump github.com/containerd/containerd from 1.5.8 to 1.5.9
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.7.4 to 1.7.6
+ build: fix libsubid test
+ Allow callers to replace the ContainerSuffix
+ parse: allow parsing anomaly non-human value for memory control group
+ .cirrus: remove static_build from ci
+ stage_executor: re-use all possible layers from cache for squashed builds
+ build(deps): bump github.com/spf13/cobra from 1.2.1 to 1.3.0
+ Allow rootless buildah to set resource limits on cgroup V2
+ build(deps): bump github.com/docker/docker
+ tests: move buildkit mount tests files from TESTSDIR to TESTDIR before modification
+ build(deps): bump github.com/opencontainers/runc from 1.0.2 to 1.0.3
+ Wire logger through to config
+ copier.Put: check for is-not-a-directory using lstat, not stat
+ Turn on rootless cgroupv2 tests
+ Grab all of the containers.conf settings for namespaces.
+ image: set MediaType in OCI manifests
+ copier: RemoveAll possibly-directories
+ Simple README fix
+ images: accept multiple filter with logical AND
+ build(deps): bump github.com/containernetworking/cni from 0.8.1 to 1.0.1
+ UPdate vendor of container/storage
+ build(deps): bump github.com/onsi/gomega from 1.16.0 to 1.17.0
+ build(deps): bump github.com/containers/image/v5 from 5.16.1 to 5.17.0
+ Make LocalIP public function so Podman can use it
+ Fix UnsetEnv for buildah bud
+ Tests should rely only on static/unchanging images
+ run: ensure that stdio pipes are labeled correctly
+ build(deps): bump github.com/docker/docker
+ Cirrus: Bump up to Fedora 35 & Ubuntu 21.10
+ chroot: don't use the generate default seccomp filter for unit tests
+ build(deps): bump github.com/containerd/containerd from 1.5.7 to 1.5.8
+ ssh-agent: Increase timeout before we explicitly close connection
+ docs/tutorials: update
+ Clarify that manifest defaults to localhost as the registry name
+ "config": remove a stray bit of debug output
+ "commit": fix a flag typo
+ Fix an error message: unlocking vs locking
+ Expand the godoc for CommonBuildOptions.Secrets
+ chroot: accept an "rw" option
+ Add --unsetenv option to buildah commit and build
+ define.TempDirForURL(): show CombinedOutput when a command fails
+ config: support the variant field
+ rootless: do not bind mount /sys if not needed
+ Fix tutorial to specify command on buildah run line
+ build: history should not contain ARG values
+ docs: Use guaranteed path for go-md2man
+ run: honor --network=none from builder if nothing specified
+ networkpolicy: Should be enabled instead of default when explictly set
+ Add support for env var secret sources
+ build(deps): bump github.com/docker/docker
+ fix: another non-portable shebang
+ Rootless containers users should use additional groups
+ Support overlayfs path contains colon
+ Report ignorefile location when no content added
+ Add support for host.containers.internal in the /etc/hosts
+ build(deps): bump github.com/onsi/ginkgo from 1.16.4 to 1.16.5
+ imagebuildah: fix nil deref
+ buildkit: add support for mount=type=cache
+ Default secret mode to 400
+ [CI:DOCS] Include manifest example usage
+ docs: update buildah-from, buildah-pull 'platform' option compatibility notes
+ docs: update buildah-build 'platform' option compatibility notes
+ De-dockerize the man page as much as possible
+ [CI:DOCS] Touch up Containerfile man page to show ARG can be 1st
+ docs: Fix and Update Containerfile man page with supported mount types
+ mount: add tmpcopyup to tmpfs mount option
+ buildkit: Add support for --mount=type=tmpfs
+ build(deps): bump github.com/opencontainers/selinux from 1.8.5 to 1.9.1
+ Fix command doc links in README.md
+ build(deps): bump github.com/containers/image/v5 from 5.16.0 to 5.16.1
+ build: Add support for buildkit like --mount=type=bind
+ Bump containerd to v1.5.7
+ build(deps): bump github.com/docker/docker
+ tests: stop pulling php, composer
+ Fix .containerignore link file
+ Cirrus: Fix defunct package metadata breaking cache
+ build(deps): bump github.com/containers/storage from 1.36.0 to 1.37.0
+ buildah build: add --all-platforms
+ Add man page for Containerfile and .containerignore
+ Plumb the remote logger throughut Buildah
+ Replace fmt.Sprintf("%d", x) with strconv.Itoa(x)
+ Run: Cleanup run directory after every RUN step
+ build(deps): bump github.com/containers/common from 0.45.0 to 0.46.0
+ Makefile: adjust -ldflags/-gcflags/-gccgoflags depending on the go implementation
+ Makefile: check for `-race` using `-mod=vendor`
+ imagebuildah: fix an attempt to write to a nil map
+ push: support to specify the compression format
+ conformance: allow test cases to specify dockerUseBuildKit
+ build(deps): bump github.com/containers/common from 0.44.1 to 0.45.0
+ build(deps): bump github.com/containers/common from 0.44.0 to 0.44.1
+ unmarshalConvertedConfig(): handle zstd compression
+ tests/copy/copy: wire up compression options
+ Update to github.com/vbauerster/mpb v7.1.5
+ Add flouthoc to OWNERS
+ build: Add additional step nodes when labels are modified
+ Makefile: turn on race detection whenever it's available
+ conformance: add more tests for exclusion short-circuiting
+ Update VM Images + Drop prior-ubuntu testing
+ Bump to v1.24.0-dev
+
+## v1.23.0 (2021-09-13)
+
+ Vendor in containers/common v0.44.0
+ build(deps): bump github.com/containers/storage from 1.35.0 to 1.36.0
+ Update 05-openshift-rootless-build.md
+ build(deps): bump github.com/opencontainers/selinux from 1.8.4 to 1.8.5
+ .cirrus.yml: run cross_build_task on Big Sur
+ Makefile: update cross targets
+ Add support for rootless overlay mounts
+ Cirrus: Increase unit-test timeout
+ Docs: Clarify rmi w/ manifest/index use
+ build: mirror --authfile to filesystem if pointing to FD instead of file
+ Fix build with .git url with branch
+ manifest: rm should remove only manifests not referenced images.
+ vendor: bump c/common to v0.43.3-0.20210902095222-a7acc160fb25
+ Avoid rehashing and noop compression writer
+ corrected man page section; .conf file to mention its man page
+ copy: add --max-parallel-downloads to tune that copy option
+ copier.Get(): try to avoid descending into directories
+ tag: Support tagging manifest list instead of resolving to images
+ Install new manpages to correct sections
+ conformance: tighten up exception specifications
+ Add support for libsubid
+ Add epoch time field to buildah images
+ Fix ownership of /home/build/.local/share/containers
+ build(deps): bump github.com/containers/image/v5 from 5.15.2 to 5.16.0
+ Rename bud to build, while keeping an alias for to bud.
+ Replace golang.org/x/crypto/ssh/terminal with golang.org/x/term
+ build(deps): bump github.com/opencontainers/runc from 1.0.1 to 1.0.2
+ build(deps): bump github.com/onsi/gomega from 1.15.0 to 1.16.0
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.7.3 to 1.7.4
+ build(deps): bump github.com/containers/common from 0.43.1 to 0.43.2
+ Move DiscoverContainerfile to pkg/util directory
+ build(deps): bump github.com/containers/image/v5 from 5.15.1 to 5.15.2
+ Remove some references to Docker
+ build(deps): bump github.com/containers/image/v5 from 5.15.0 to 5.15.1
+ imagebuildah: handle --manifest directly
+ build(deps): bump github.com/containers/common from 0.42.1 to 0.43.1
+ build(deps): bump github.com/opencontainers/selinux from 1.8.3 to 1.8.4
+ executor: make sure imageMap is updated with terminatedStage
+ tests/serve/serve.go: use a kernel-assigned port
+ Bump go for vendor-in-container from 1.13 to 1.16
+ imagebuildah: move multiple-platform building internal
+ Adds GenerateStructure helper function to support rootfs-overlay.
+ Run codespell to fix spelling
+ Implement SSH RUN mount
+ build(deps): bump github.com/onsi/gomega from 1.14.0 to 1.15.0
+ Fix resolv.conf content with run --net=private
+ run: fix nil deref using the option's logger
+ build(deps): bump github.com/containerd/containerd from 1.5.1 to 1.5.5
+ make vendor-in-container
+ bud: teach --platform to take a list
+ set base-image annotations
+ build(deps): bump github.com/opencontainers/selinux from 1.8.2 to 1.8.3
+ [CI:DOCS] Fix CHANGELOG.md
+ Bump to v1.23.0-dev [NO TESTS NEEDED]
+ Accept repositories on login/logout
+
+## v1.22.0 (2021-08-02)
+ c/image, c/storage, c/common vendor before Podman 3.3 release
+ WIP: tests: new assert()
+ Proposed patch for 3399 (shadowutils)
+ Fix handling of --restore shadow-utils
+ build(deps): bump github.com/containers/image/v5 from 5.13.2 to 5.14.0
+ runtime-flag (debug) test: handle old & new runc
+ build(deps): bump github.com/containers/storage from 1.32.6 to 1.33.0
+ Allow dst and destination for target in secret mounts
+ Multi-arch: Always push updated version-tagged img
+ Add a few tests on cgroups V2
+ imagebuildah.stageExecutor.prepare(): remove pseudonym check
+ refine dangling filter
+ Chown with environment variables not set should fail
+ Just restore protections of shadow-utils
+ build(deps): bump github.com/opencontainers/runc from 1.0.0 to 1.0.1
+ Remove specific kernel version number requirement from install.md
+ Multi-arch image workflow: Make steps generic
+ chroot: fix environment value leakage to intermediate processes
+ Update nix pin with `make nixpkgs`
+ buildah source - create and manage source images
+ Update cirrus-cron notification GH workflow
+ Reuse code from containers/common/pkg/parse
+ Cirrus: Freshen VM images
+ build(deps): bump github.com/containers/storage from 1.32.5 to 1.32.6
+ Fix excludes exception begining with / or ./
+ Fix syntax for --manifest example
+ build(deps): bump github.com/onsi/gomega from 1.13.0 to 1.14.0
+ vendor containers/common@main
+ Cirrus: Drop dependence on fedora-minimal
+ Adjust conformance-test error-message regex
+ Workaround appearance of differing debug messages
+ Cirrus: Install docker from package cache
+ build(deps): bump github.com/containers/ocicrypt from 1.1.1 to 1.1.2
+ Switch rusagelogfile to use options.Out
+ build(deps): bump github.com/containers/storage from 1.32.4 to 1.32.5
+ Turn stdio back to blocking when command finishes
+ Add support for default network creation
+ Cirrus: Updates for master->main rename
+ Change references from master to main
+ Add `--env` and `--workingdir` flags to run command
+ build(deps): bump github.com/opencontainers/runc
+ [CI:DOCS] buildah bud: spelling --ignore-file requires parameter
+ [CI:DOCS] push/pull: clarify supported transports
+ Remove unused function arguments
+ Create mountOptions for mount command flags
+ Extract version command implementation to function
+ Add --json flags to `mount` and `version` commands
+ build(deps): bump github.com/containers/storage from 1.32.2 to 1.32.3
+ build(deps): bump github.com/containers/common from 0.40.0 to 0.40.1
+ copier.Put(): set xattrs after ownership
+ buildah add/copy: spelling
+ build(deps): bump github.com/containers/common from 0.39.0 to 0.40.0
+ buildah copy and buildah add should support .containerignore
+ Remove unused util.StartsWithValidTransport
+ Fix documentation of the --format option of buildah push
+ Don't use alltransports.ParseImageName with known transports
+ build(deps): bump github.com/containers/image/v5 from 5.13.0 to 5.13.1
+ man pages: clarify `rmi` removes dangling parents
+ tests: make it easer to override the location of the copy helper
+ build(deps): bump github.com/containers/image/v5 from 5.12.0 to 5.13.0
+ [CI:DOCS] Fix links to c/image master branch
+ imagebuildah: use the specified logger for logging preprocessing warnings
+ Fix copy into workdir for a single file
+ Fix docs links due to branch rename
+ Update nix pin with `make nixpkgs`
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.7.2 to 1.7.3
+ build(deps): bump github.com/opencontainers/selinux from 1.8.1 to 1.8.2
+ build(deps): bump go.etcd.io/bbolt from 1.3.5 to 1.3.6
+ build(deps): bump github.com/containers/storage from 1.32.1 to 1.32.2
+ build(deps): bump github.com/mattn/go-shellwords from 1.0.11 to 1.0.12
+ build(deps): bump github.com/onsi/ginkgo from 1.16.3 to 1.16.4
+ fix(docs): typo
+ Move to v1.22.0-dev
+ Fix handling of auth.json file while in a user namespace
+ Add rusage-logfile flag to optionally send rusage to a file
+ imagebuildah: redo step logging
+ build(deps): bump github.com/onsi/ginkgo from 1.16.2 to 1.16.3
+ build(deps): bump github.com/containers/storage from 1.32.0 to 1.32.1
+ Add volumes to make running buildah within a container easier
+ build(deps): bump github.com/onsi/gomega from 1.12.0 to 1.13.0
+ Add and use a "copy" helper instead of podman load/save
+ Bump github.com/containers/common from 0.38.4 to 0.39.0
+ containerImageRef/containerImageSource: don't buffer uncompressed layers
+ containerImageRef(): squashed images have no parent images
+ Sync. workflow across skopeo, buildah, and podman
+ Bump github.com/containers/storage from 1.31.1 to 1.31.2
+ Bump github.com/opencontainers/runc from 1.0.0-rc94 to 1.0.0-rc95
+ Bump to v1.21.1-dev [NO TESTS NEEDED]
+
+## v1.21.0 (2021-05-19)
+ Don't blow up if cpp detects errors
+ Vendor in containers/common v0.38.4
+ Remove 'buildah run --security-opt' from completion
+ update c/common
+ Fix handling of --default-mounts-file
+ update vendor of containers/storage v1.31.1
+ Bump github.com/containers/storage from 1.30.3 to 1.31.0
+ Send logrus messages back to caller when building
+ github: Fix bad repo. ref in workflow config
+ Check earlier for bad image tags name
+ buildah bud: fix containers/podman/issues/10307
+ Bump github.com/containers/storage from 1.30.1 to 1.30.3
+ Cirrus: Support [CI:DOCS] test skipping
+ Notification email for cirrus-cron build failures
+ Bump github.com/opencontainers/runc from 1.0.0-rc93 to 1.0.0-rc94
+ Fix race condition
+ Fix copy race while walking paths
+ Preserve ownership of lower directory when doing an overlay mount
+ Bump github.com/onsi/gomega from 1.11.0 to 1.12.0
+ Update nix pin with `make nixpkgs`
+ codespell cleanup
+ Multi-arch github-action workflow unification
+ Bump github.com/containers/image/v5 from 5.11.1 to 5.12.0
+ Bump github.com/onsi/ginkgo from 1.16.1 to 1.16.2
+ imagebuildah: ignore signatures when tagging images
+ update to latest libimage
+ Bump github.com/containers/common from 0.37.0 to 0.37.1
+ Bump github.com/containers/storage from 1.30.0 to 1.30.1
+ Upgrade to GitHub-native Dependabot
+ Document location of auth.json file if XDG_RUNTIME_DIR is not set
+ run.bats: fix flake in run-user test
+ Cirrus: Update F34beta -> F34
+ pr-should-include-tests: try to make work in buildah
+ runUsingRuntime: when relaying error from the runtime, mention that
+ Run(): avoid Mkdir() into the rootfs
+ imagebuildah: replace archive with chrootarchive
+ imagebuildah.StageExecutor.volumeCacheSaveVFS(): set up bind mounts
+ conformance: use :Z with transient mounts when SELinux is enabled
+ bud.bats: fix a bats warning
+ imagebuildah: create volume directories when using overlays
+ imagebuildah: drop resolveSymlink()
+ namespaces test - refactoring and cleanup
+ Refactor 'idmapping' system test
+ Cirrus: Update Ubuntu images to 21.04
+ Tiny fixes in bud system tests
+ Add compabitility wrappers for removed packages
+ Fix expected message at pulling image
+ Fix system tests of 'bud' subcommand
+ [CI:DOCS] Update steps for CentOS runc users
+ Add support for secret mounts
+ Add buildah manifest rm command
+ restore push/pull and util API
+ [CI:DOCS] Remove older distro docs
+ Rename rhel secrets to subscriptions
+ vendor in openshift/imagebuilder
+ Remove buildah bud --loglevel ...
+ use new containers/common/libimage package
+ Fix copier when using globs
+ Test namespace flags of 'bud' subcommand
+ Add system test of 'bud' subcommand
+ Output names of multiple tags in buildah bud
+ push to docker test: don't get fooled by podman
+ copier: add Remove()
+ build(deps): bump github.com/containers/image/v5 from 5.10.5 to 5.11.1
+ Restore log timestamps
+ Add system test of 'buildah help' with a tiny fix
+ tests: copy.bats: fix infinite hang
+ Do not force hard code to crun in rootless mode
+ build(deps): bump github.com/openshift/imagebuilder from 1.2.0 to 1.2.1
+ build(deps): bump github.com/containers/ocicrypt from 1.1.0 to 1.1.1
+ build(deps): bump github.com/containers/common from 0.35.4 to 0.36.0
+ Fix arg missing warning in bud
+ Check without flag in 'from --cgroup-parent' test
+ Minor fixes to Buildah as a library tutorial documentation
+ Add system test of 'buildah version' for packaged buildah
+ Add a few system tests of 'buildah from'
+ Log the final error with %+v at logging level "trace"
+ copier: add GetOptions.NoCrossDevice
+ Update nix pin with `make nixpkgs`
+ Bump to v1.20.2-dev
+
+## v1.20.1 (2021-04-13)
+ Run container with isolation type set at 'from'
+ bats helpers.bash - minor refactoring
+ Bump containers/storage vendor to v1.29.0
+ build(deps): bump github.com/onsi/ginkgo from 1.16.0 to 1.16.1
+ Cirrus: Update VMs w/ F34beta
+ CLI add/copy: add a --from option
+ build(deps): bump github.com/onsi/ginkgo from 1.15.2 to 1.16.0
+ Add authentication system tests for 'commit' and 'bud'
+ fix local image lookup for custom platform
+ Double-check existence of OCI runtimes
+ Cirrus: Make use of shared get_ci_vm container
+ Add system tests of "buildah run"
+ Update nix pin with `make nixpkgs`
+ Remove some stuttering on returns errors
+ Setup alias for --tty to --terminal
+ Add conformance tests for COPY /...
+ Put a few more minutes on the clock for the CI conformance test
+ Add a conformance test for COPY --from $symlink
+ Add conformance tests for COPY ""
+ Check for symlink in builtin volume
+ Sort all mounts by destination directory
+ System-test cleanup
+ Export parse.Platform string to be used by podman-remote
+ blobcache: fix sequencing error
+ build(deps): bump github.com/containers/common from 0.35.3 to 0.35.4
+ Fix URL in demos/buildah_multi_stage.sh
+ Add a few system tests
+ [NO TESTS NEEDED] Use --recurse-modules when building git context
+ Bump to v1.20.1-dev
+
+## v1.20.0 (2021-03-25)
+ * vendor in containers/storage v1.28.1
+ * build(deps): bump github.com/containers/common from 0.35.2 to 0.35.3
+ * tests: prefetch: use buildah, not podman, for pulls
+ * Use faster way to check image tag existence during multi-arch build
+ * Add information about multi-arch images to the Readme
+ * COPY --chown: expand the conformance test
+ * pkg/chrootuser: use a bufio.Scanner
+ * [CI:DOCS] Fix rootful typo in docs
+ * build(deps): bump github.com/onsi/ginkgo from 1.15.1 to 1.15.2
+ * Add documentation and testing for .containerignore
+ * build(deps): bump github.com/sirupsen/logrus from 1.8.0 to 1.8.1
+ * build(deps): bump github.com/hashicorp/go-multierror from 1.1.0 to 1.1.1
+ * Lookup Containerfile if user specifies a directory
+ * Add Tag format placeholder to docs
+ * copier: ignore sockets
+ * image: propagate errors from extractRootfs
+ * Remove system test of 'buildah containers -a'
+ * Clarify userns options are usable only as root in man pages
+ * Fix system test of 'containers -a'
+ * Remove duplicated code in addcopy
+ * build(deps): bump github.com/onsi/ginkgo from 1.15.0 to 1.15.1
+ * build(deps): bump github.com/onsi/gomega from 1.10.5 to 1.11.0
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.7.1 to 1.7.2
+ * Update multi-arch buildah build setup with new logic
+ * Update nix pin with `make nixpkgs`
+ * overlay.bats: fix the "overlay source permissions" test
+ * imagebuildah: use overlay for volumes when using overlay
+ * Make PolicyMap and PullPolicy names align
+ * copier: add GetOptions.IgnoreUnreadable
+ * Check local image to match system context
+ * fix: Containerfiles - smaller set of userns u/gids
+ * Set upperdir permissions based on source
+ * Shrink the vendoring size of pkc/cli
+ * Clarify image name match failure message
+ * ADD/COPY: create the destination directory first, chroot to it
+ * copier.GetOptions: add NoDerefSymLinks
+ * copier: add an Eval function
+ * Update system test for 'from --cap-add/drop'
+ * copier: fix a renaming bug
+ * copier: return child process stderr if we can't JSON decode the response
+ * Add some system tests
+ * build(deps): bump github.com/containers/storage from 1.26.0 to 1.27.0
+ * complement add/copy --chmod documentation
+ * buildah login and logout, do not need to enter user namespace
+ * Add multi-arch image build
+ * chmod/chown added/fixed in bash completions
+ * OWNERS: add @lsm5
+ * buildah add/copy --chmod dockerfile implementation
+ * bump github.com/openshift/imagebuilder from 1.1.8 to 1.2.0
+ * buildah add/copy --chmod cli implementation for files and urls
+ * Make sure we set the buildah version label
+ * Isolation strings, should match user input
+ * [CI:DOCS] buildah-from.md: remove dup arch,os
+ * build(deps): bump github.com/containers/image/v5 from 5.10.2 to 5.10.3
+ * Cirrus: Temp. disable prior-fedora (F32) testing
+ * pr-should-include-tests: recognized "renamed" tests
+ * build(deps): bump github.com/sirupsen/logrus from 1.7.0 to 1.8.0
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.7.0 to 1.7.1
+ * build(deps): bump github.com/containers/common from 0.34.2 to 0.35.0
+ * Fix reaping of stages with no instructions
+ * add stale bot
+ * Add base image name to comment
+ * build(deps): bump github.com/spf13/cobra from 1.1.1 to 1.1.3
+ * Don't fail copy to emptydir
+ * buildah: use volatile containers
+ * vendor: update containers/storage
+ * Eliminate the use of containers/building import in pkg subdirs
+ * Add more support for removing config
+ * Improve messages about --cache-from not being supported
+ * Revert patch to allow COPY/ADD of empty dirs.
+ * Don't fail copy to emptydir
+ * Fix tutorial for rootless mode
+ * Fix caching layers with build args
+ * Vendor in containers/image v5.10.2
+ * build(deps): bump github.com/containers/common from 0.34.0 to 0.34.2
+ * build(deps): bump github.com/onsi/ginkgo from 1.14.2 to 1.15.0
+ * 'make validate': require PRs to include tests
+ * build(deps): bump github.com/onsi/gomega from 1.10.4 to 1.10.5
+ * build(deps): bump github.com/containers/storage from 1.24.5 to 1.25.0
+ * Use chown function for U volume flag from containers/common repository
+ * --iidfile: print hash prefix
+ * bump containernetworking/cni to v0.8.1 - fix for CVE-2021-20206
+ * run: fix check for host pid namespace
+ * Finish plumbing for buildah bud --manifest
+ * buildah manifest add localimage should work
+ * Stop testing directory permissions with latest docker
+ * Fix build arg check
+ * build(deps): bump github.com/containers/ocicrypt from 1.0.3 to 1.1.0
+ * [ci:docs] Fix man page for buildah push
+ * Update nix pin with `make nixpkgs`
+ * Bump to containers/image v5.10.1
+ * Rebuild layer if a change in ARG is detected
+ * Bump golang.org/x/crypto to the latest
+ * Add Ashley and Urvashi to Approvers
+ * local image lookup by digest
+ * Use build-arg ENV val from local environment if set
+ * Pick default OCI Runtime from containers.conf
+ * Added required devel packages
+ * Cirrus: Native OSX Build
+ * Cirrus: Two minor cleanup items
+ * Workaround for RHEL gating test failure
+ * build(deps): bump github.com/stretchr/testify from 1.6.1 to 1.7.0
+ * build(deps): bump github.com/mattn/go-shellwords from 1.0.10 to 1.0.11
+ * Reset upstream branch to dev version
+ * If destination does not exists, do not throw error
+
+## v1.19.0 (2021-01-08)
+ Update vendor of containers/storage and containers/common
+ Buildah inspect should be able to inspect manifests
+ Make buildah push support pushing manifests lists and digests
+ Fix handling of TMPDIR environment variable
+ Add support for --manifest flags
+ Upper directory should match mode of destination directory
+ Only grab the OS, Arch if the user actually specified them
+ Use --arch and --os and --variant options to select architecture and os
+ Cirrus: Track libseccomp and golang version
+ copier.PutOptions: add an "IgnoreDevices" flag
+ fix: `rmi --prune` when parent image is in store.
+ build(deps): bump github.com/containers/storage from 1.24.3 to 1.24.4
+ build(deps): bump github.com/containers/common from 0.31.1 to 0.31.2
+ Allow users to specify stdin into containers
+ Drop log message on failure to mount on /sys file systems to info
+ Spelling
+ SELinux no longer requires a tag.
+ build(deps): bump github.com/opencontainers/selinux from 1.6.0 to 1.8.0
+ build(deps): bump github.com/containers/common from 0.31.0 to 0.31.1
+ Update nix pin with `make nixpkgs`
+ Switch references of /var/run -> /run
+ Allow FROM to be overriden with from option
+ copier: don't assume we can chroot() on Unixy systems
+ copier: add PutOptions.NoOverwriteDirNonDir, Get/PutOptions.Rename
+ copier: handle replacing directories with not-directories
+ copier: Put: skip entries with zero-length names
+ build(deps): bump github.com/containers/storage from 1.24.2 to 1.24.3
+ Add U volume flag to chown source volumes
+ Turn off PRIOR_UBUNTU Test until vm is updated
+ pkg, cli: rootless uses correct isolation
+ build(deps): bump github.com/onsi/gomega from 1.10.3 to 1.10.4
+ update installation doc to reflect current status
+ Move away from using docker.io
+ enable short-name aliasing
+ build(deps): bump github.com/containers/storage from 1.24.1 to 1.24.2
+ build(deps): bump github.com/containers/common from 0.30.0 to 0.31.0
+ Throw errors when using bogus --network flags
+ pkg/supplemented test: replace our null blobinfocache
+ build(deps): bump github.com/containers/common from 0.29.0 to 0.30.0
+ inserts forgotten quotation mark
+ Not prefer use local image create/add manifest
+ Add container information to .containerenv
+ Add --ignorefile flag to use alternate .dockerignore flags
+ Add a source debug build
+ Fix crash on invalid filter commands
+ build(deps): bump github.com/containers/common from 0.27.0 to 0.29.0
+ Switch to using containers/common pkg's
+ fix: non-portable shebang #2812
+ Remove copy/paste errors that leaked `Podman` into man pages.
+ Add suggests cpp to spec file
+ Apply suggestions from code review
+ update docs for debian testing and unstable
+ imagebuildah: disable pseudo-terminals for RUN
+ Compute diffID for mapped-layer at creating image source
+ intermediateImageExists: ignore images whose history we can't read
+ Bump to v1.19.0-dev
+ build(deps): bump github.com/containers/common from 0.26.3 to 0.27.0
+
+## v1.18.0 (2020-11-16)
+ Fix testing error caused by simultanious merge
+ Vendor in containers/storage v1.24.0
+ short-names aliasing
+ Add --policy flag to buildah pull
+ Stop overwrapping and stuttering
+ copier.Get(): ignore ENOTSUP/ENOSYS when listing xattrs
+ Run: don't forcibly disable UTS namespaces in rootless mode
+ test: ensure non-directory in a Dockerfile path is handled correctly
+ Add a few tests for `pull` command
+ Fix buildah config --cmd to handle array
+ build(deps): bump github.com/containers/storage from 1.23.8 to 1.23.9
+ Fix NPE when Dockerfile path contains non-directory entries
+ Update buildah bud man page from podman build man page
+ Move declaration of decryption-keys to common cli
+ Run: correctly call copier.Mkdir
+ util: digging UID/GID out of os.FileInfo should work on Unix
+ imagebuildah.getImageTypeAndHistoryAndDiffIDs: cache results
+ Verify userns-uid-map and userns-gid-map input
+ Use CPP, CC and flags in dep check scripts
+ Avoid overriding LDFLAGS in Makefile
+ ADD: handle --chown on URLs
+ Update nix pin with `make nixpkgs`
+ (*Builder).Run: MkdirAll: handle EEXIST error
+ copier: try to force loading of nsswitch modules before chroot()
+ fix MkdirAll usage
+ build(deps): bump github.com/containers/common from 0.26.2 to 0.26.3
+ build(deps): bump github.com/containers/storage from 1.23.7 to 1.23.8
+ Use osusergo build tag for static build
+ imagebuildah: cache should take image format into account
+ Bump to v1.18.0-dev
+
+## v1.17.0 (2020-10-29)
+ Handle cases where other tools mount/unmount containers
+ overlay.MountReadOnly: support RO overlay mounts
+ overlay: use fusermount for rootless umounts
+ overlay: fix umount
+ Switch default log level of Buildah to Warn. Users need to see these messages
+ Drop error messages about OCI/Docker format to Warning level
+ build(deps): bump github.com/containers/common from 0.26.0 to 0.26.2
+ tests/testreport: adjust for API break in storage v1.23.6
+ build(deps): bump github.com/containers/storage from 1.23.5 to 1.23.7
+ build(deps): bump github.com/fsouza/go-dockerclient from 1.6.5 to 1.6.6
+ copier: put: ignore Typeflag="g"
+ Use curl to get repo file (fix #2714)
+ build(deps): bump github.com/containers/common from 0.25.0 to 0.26.0
+ build(deps): bump github.com/spf13/cobra from 1.0.0 to 1.1.1
+ Remove docs that refer to bors, since we're not using it
+ Buildah bud should not use stdin by default
+ bump containerd, docker, and golang.org/x/sys
+ Makefile: cross: remove windows.386 target
+ copier.copierHandlerPut: don't check length when there are errors
+ Stop excessive wrapping
+ CI: require that conformance tests pass
+ bump(github.com/openshift/imagebuilder) to v1.1.8
+ Skip tlsVerify insecure BUILD_REGISTRY_SOURCES
+ Fix build path wrong https://github.com/containers/podman/issues/7993
+ refactor pullpolicy to avoid deps
+ build(deps): bump github.com/containers/common from 0.24.0 to 0.25.0
+ CI: run gating tasks with a lot more memory
+ ADD and COPY: descend into excluded directories, sometimes
+ copier: add more context to a couple of error messages
+ copier: check an error earlier
+ copier: log stderr output as debug on success
+ Update nix pin with `make nixpkgs`
+ Set directory ownership when copied with ID mapping
+ build(deps): bump github.com/sirupsen/logrus from 1.6.0 to 1.7.0
+ build(deps): bump github.com/containers/common from 0.23.0 to 0.24.0
+ Cirrus: Remove bors artifacts
+ Sort build flag definitions alphabetically
+ ADD: only expand archives at the right time
+ Remove configuration for bors
+ Shell Completion for podman build flags
+ Bump c/common to v0.24.0
+ New CI check: xref --help vs man pages
+ CI: re-enable several linters
+ Move --userns-uid-map/--userns-gid-map description into buildah man page
+ add: preserve ownerships and permissions on ADDed archives
+ Makefile: tweak the cross-compile target
+ Bump containers/common to v0.23.0
+ chroot: create bind mount targets 0755 instead of 0700
+ Change call to Split() to safer SplitN()
+ chroot: fix handling of errno seccomp rules
+ build(deps): bump github.com/containers/image/v5 from 5.5.2 to 5.6.0
+ Add In Progress section to contributing
+ integration tests: make sure tests run in ${topdir}/tests
+ Run(): ignore containers.conf's environment configuration
+ Warn when setting healthcheck in OCI format
+ Cirrus: Skip git-validate on branches
+ tools: update git-validation to the latest commit
+ tools: update golangci-lint to v1.18.0
+ Add a few tests of push command
+ Add(): fix handling of relative paths with no ContextDir
+ build(deps): bump github.com/containers/common from 0.21.0 to 0.22.0
+ Lint: Use same linters as podman
+ Validate: reference HEAD
+ Fix buildah mount to display container names not ids
+ Update nix pin with `make nixpkgs`
+ Add missing --format option in buildah from man page
+ Fix up code based on codespell
+ build(deps): bump github.com/openshift/imagebuilder from 1.1.6 to 1.1.7
+ build(deps): bump github.com/containers/storage from 1.23.4 to 1.23.5
+ Improve buildah completions
+ Cirrus: Fix validate commit epoch
+ Fix bash completion of manifest flags
+ Uniform some man pages
+ Update Buildah Tutorial to address BZ1867426
+ Update bash completion of `manifest add` sub command
+ copier.Get(): hard link targets shouldn't be relative paths
+ build(deps): bump github.com/onsi/gomega from 1.10.1 to 1.10.2
+ Pass timestamp down to history lines
+ Timestamp gets updated everytime you inspect an image
+ bud.bats: use absolute paths in newly-added tests
+ contrib/cirrus/lib.sh: don't use CN for the hostname
+ tests: Add some tests
+ Update `manifest add` man page
+ Extend flags of `manifest add`
+ build(deps): bump github.com/containers/storage from 1.23.3 to 1.23.4
+ build(deps): bump github.com/onsi/ginkgo from 1.14.0 to 1.14.1
+ Bump to v1.17.0-dev
+ CI: expand cross-compile checks
+
+## v1.16.0 (2020-09-03)
+ fix build on 32bit arches
+ containerImageRef.NewImageSource(): don't always force timestamps
+ Add fuse module warning to image readme
+ Heed our retry delay option values when retrying commit/pull/push
+ Switch to containers/common for seccomp
+ Use --timestamp rather then --omit-timestamp
+ docs: remove outdated notice
+ docs: remove outdated notice
+ build-using-dockerfile: add a hidden --log-rusage flag
+ build(deps): bump github.com/containers/image/v5 from 5.5.1 to 5.5.2
+ Discard ReportWriter if user sets options.Quiet
+ build(deps): bump github.com/containers/common from 0.19.0 to 0.20.3
+ Fix ownership of content copied using COPY --from
+ newTarDigester: zero out timestamps in tar headers
+ Update nix pin with `make nixpkgs`
+ bud.bats: correct .dockerignore integration tests
+ Use pipes for copying
+ run: include stdout in error message
+ run: use the correct error for errors.Wrapf
+ copier: un-export internal types
+ copier: add Mkdir()
+ in_podman: don't get tripped up by $CIRRUS_CHANGE_TITLE
+ docs/buildah-commit.md: tweak some wording, add a --rm example
+ imagebuildah: don’t blank out destination names when COPYing
+ Replace retry functions with common/pkg/retry
+ StageExecutor.historyMatches: compare timestamps using .Equal
+ Update vendor of containers/common
+ Fix errors found in coverity scan
+ Change namespace handling flags to better match podman commands
+ conformance testing: ignore buildah.BuilderIdentityAnnotation labels
+ Vendor in containers/storage v1.23.0
+ Add buildah.IsContainer interface
+ Avoid feeding run_buildah to pipe
+ fix(buildahimage): add xz dependency in buildah image
+ Bump github.com/containers/common from 0.15.2 to 0.18.0
+ Howto for rootless image building from OpenShift
+ Add --omit-timestamp flag to buildah bud
+ Update nix pin with `make nixpkgs`
+ Shutdown storage on failures
+ Handle COPY --from when an argument is used
+ Bump github.com/seccomp/containers-golang from 0.5.0 to 0.6.0
+ Cirrus: Use newly built VM images
+ Bump github.com/opencontainers/runc from 1.0.0-rc91 to 1.0.0-rc92
+ Enhance the .dockerignore man pages
+ conformance: add a test for COPY from subdirectory
+ fix bug manifest inspct
+ Add documentation for .dockerignore
+ Add BuilderIdentityAnnotation to identify buildah version
+ DOC: Add quay.io/containers/buildah image to README.md
+ Update buildahimages readme
+ fix spelling mistake in "info" command result display
+ Don't bind /etc/host and /etc/resolv.conf if network is not present
+ blobcache: avoid an unnecessary NewImage()
+ Build static binary with `buildGoModule`
+ copier: split StripSetidBits into StripSetuidBit/StripSetgidBit/StripStickyBit
+ tarFilterer: handle multiple archives
+ Fix a race we hit during conformance tests
+ Rework conformance testing
+ Update 02-registries-repositories.md
+ test-unit: invoke cmd/buildah tests with --flags
+ parse: fix a type mismatch in a test
+ Fix compilation of tests/testreport/testreport
+ build.sh: log the version of Go that we're using
+ test-unit: increase the test timeout to 40/45 minutes
+ Add the "copier" package
+ Fix & add notes regarding problematic language in codebase
+ Add dependency on github.com/stretchr/testify/require
+ CompositeDigester: add the ability to filter tar streams
+ BATS tests: make more robust
+ vendor golang.org/x/text@v0.3.3
+ Switch golang 1.12 to golang 1.13
+ imagebuildah: wait for stages that might not have even started yet
+ chroot, run: not fail on bind mounts from /sys
+ chroot: do not use setgroups if it is blocked
+ Set engine env from containers.conf
+ imagebuildah: return the right stage's image as the "final" image
+ Fix a help string
+ Deduplicate environment variables
+ switch containers/libpod to containers/podman
+ Bump github.com/containers/ocicrypt from 1.0.2 to 1.0.3
+ Bump github.com/opencontainers/selinux from 1.5.2 to 1.6.0
+ Mask out /sys/dev to prevent information leak
+ linux: skip errors from the runtime kill
+ Mask over the /sys/fs/selinux in mask branch
+ Add VFS additional image store to container
+ tests: add auth tests
+ Allow "readonly" as alias to "ro" in mount options
+ Ignore OS X specific consistency mount option
+ Bump github.com/onsi/ginkgo from 1.13.0 to 1.14.0
+ Bump github.com/containers/common from 0.14.0 to 0.15.2
+ Rootless Buildah should default to IsolationOCIRootless
+ imagebuildah: fix inheriting multi-stage builds
+ Make imagebuildah.BuildOptions.Architecture/OS optional
+ Make imagebuildah.BuildOptions.Jobs optional
+ Resolve a possible race in imagebuildah.Executor.startStage()
+ Switch scripts to use containers.conf
+ Bump openshift/imagebuilder to v1.1.6
+ Bump go.etcd.io/bbolt from 1.3.4 to 1.3.5
+ buildah, bud: support --jobs=N for parallel execution
+ executor: refactor build code inside new function
+ Add bud regression tests
+ Cirrus: Fix missing htpasswd in registry img
+ docs: clarify the 'triples' format
+ CHANGELOG.md: Fix markdown formatting
+ Add nix derivation for static builds
+ Bump to v1.16.0-dev
+ version centos7 for compatible
+
+## v1.15.0 (2020-06-17)
+ Bump github.com/containers/common from 0.12.0 to 0.13.1
+ Bump github.com/containers/storage from 1.20.1 to 1.20.2
+ Bump github.com/seccomp/containers-golang from 0.4.1 to 0.5.0
+ Bump github.com/stretchr/testify from 1.6.0 to 1.6.1
+ Bump github.com/opencontainers/runc from 1.0.0-rc9 to 1.0.0-rc90
+ Add CVE-2020-10696 to CHANGELOG.md and changelog.txt
+ Bump github.com/stretchr/testify from 1.5.1 to 1.6.0
+ Bump github.com/onsi/ginkgo from 1.12.2 to 1.12.3
+ Vendor in containers/common v0.12.0
+ fix lighttpd example
+ Vendor in new go.etcd.io/bbolt
+ Bump github.com/onsi/ginkgo from 1.12.1 to 1.12.2
+ Bump imagebuilder for ARG fix
+ Bump github.com/containers/common from 0.11.2 to 0.11.4
+ remove dependency on openshift struct
+ Warn on unset build arguments
+ vendor: update seccomp/containers-golang to v0.4.1
+ Ammended docs
+ Updated docs
+ clean up comments
+ update exit code for tests
+ Implement commit for encryption
+ implementation of encrypt/decrypt push/pull/bud/from
+ fix resolve docker image name as transport
+ Bump github.com/opencontainers/go-digest from 1.0.0-rc1 to 1.0.0
+ Bump github.com/onsi/ginkgo from 1.12.0 to 1.12.1
+ Bump github.com/containers/storage from 1.19.1 to 1.19.2
+ Bump github.com/containers/image/v5 from 5.4.3 to 5.4.4
+ Add preliminary profiling support to the CLI
+ Bump github.com/containers/common from 0.10.0 to 0.11.2
+ Evaluate symlinks in build context directory
+ fix error info about get signatures for containerImageSource
+ Add Security Policy
+ Cirrus: Fixes from review feedback
+ Bump github.com/containers/storage from 1.19.0 to 1.19.1
+ Bump github.com/sirupsen/logrus from 1.5.0 to 1.6.0
+ imagebuildah: stages shouldn't count as their base images
+ Update containers/common v0.10.0
+ Bump github.com/fsouza/go-dockerclient from 1.6.4 to 1.6.5
+ Add registry to buildahimage Dockerfiles
+ Cirrus: Use pre-installed VM packages + F32
+ Cirrus: Re-enable all distro versions
+ Cirrus: Update to F31 + Use cache images
+ golangci-lint: Disable gosimple
+ Lower number of golangci-lint threads
+ Fix permissions on containers.conf
+ Don't force tests to use runc
+ Bump github.com/containers/common from 0.9.1 to 0.9.5
+ Return exit code from failed containers
+ Bump github.com/containers/storage from 1.18.2 to 1.19.0
+ Bump github.com/containers/common from 0.9.0 to 0.9.1
+ cgroup_manager should be under [engine]
+ Use c/common/pkg/auth in login/logout
+ Cirrus: Temporarily disable Ubuntu 19 testing
+ Add containers.conf to stablebyhand build
+ Update gitignore to exclude test Dockerfiles
+ Bump github.com/fsouza/go-dockerclient from 1.6.3 to 1.6.4
+ Bump github.com/containers/common from 0.8.1 to 0.9.0
+ Bump back to v1.15.0-dev
+ Remove warning for systemd inside of container
+
+## v1.14.8 (2020-04-09)
+ Run (make vendor)
+ Run (make -C tests/tools vendor)
+ Run (go mod tidy) before (go mod vendor) again
+ Fix (make vendor)
+ Bump validation
+ Bump back to v1.15.0-dev
+
+## v1.14.7 (2020-04-07)
+ Bump github.com/containers/image/v5 from 5.3.1 to 5.4.3
+ make vendor: run `tidy` after `vendor`
+ Do not skip the directory when the ignore pattern matches
+ Bump github.com/containers/common from 0.7.0 to 0.8.1
+ Downgrade siruspen/logrus from 1.4.2
+ Fix errorf conventions
+ dockerignore tests : remove symlinks, rework
+ Bump back to v1.15.0-dev
+
+## v1.14.6 (2020-04-02)
+ bud.bats - cleanup, refactoring
+ vendor in latest containers/storage 1.18.0 and containers/common v0.7.0
+ Bump github.com/spf13/cobra from 0.0.6 to 0.0.7
+ Bump github.com/containers/storage from 1.16.5 to 1.17.0
+ Bump github.com/containers/image/v5 from 5.2.1 to 5.3.1
+ Fix Amazon install step
+ Bump back to v1.15.0-dev
+ Fix bud-build-arg-cache test
+ Make image history work correctly with new args handling
+ Don't add args to the RUN environment from the Builder
+ Update github.com/openshift/imagebuilder to v1.1.4
+ Add .swp files to .gitignore
+
+## v1.14.5 (2020-03-26)
+ revert #2246 FIPS mode change
+ Bump back to v1.15.0-dev
+ image with dup layers: we now have one on quay
+ digest test : make more robust
+
+## v1.14.4 (2020-03-25)
+ Fix fips-mode check for RHEL8 boxes
+ Fix potential CVE in tarfile w/ symlink (Edit 02-Jun-2020: Addresses CVE-2020-10696)
+ Fix .dockerignore with globs and ! commands
+ update install steps for Amazon Linux 2
+ Bump github.com/openshift/imagebuilder from 1.1.2 to 1.1.3
+ Add comment for RUN command in volume ownership test
+ Run stat command directly for volume ownership test
+ vendor in containers/common v0.6.1
+ Cleanup go.sum
+ Bump back to v1.15.0-dev
+
+## v1.14.3 (2020-03-17)
+ Update containers/storage to v1.16.5
+ Bump github.com/containers/storage from 1.16.2 to 1.16.4
+ Bump github.com/openshift/imagebuilder from 1.1.1 to 1.1.2
+ Update github.com/openshift/imagebuilder vendoring
+ Update unshare man page to fix script example
+ Fix compilation errors on non linux platforms
+ Bump containers/common and opencontainers/selinux versions
+ Add tests for volume ownership
+ Preserve volume uid and gid through subsequent commands
+ Fix FORWARD_NULL errors found by Coverity
+ Bump github.com/containers/storage from 1.16.1 to 1.16.2
+ Fix errors found by codespell
+ Bump back to v1.15.0-dev
+ Add Pull Request Template
+
+## v1.14.2 (2020-03-03)
+ Add Buildah pull request template
+ Bump to containers/storage v1.16.1
+ run_linux: fix tight loop if file is not pollable
+ Bump github.com/opencontainers/selinux from 1.3.2 to 1.3.3
+ Bump github.com/containers/common from 0.4.1 to 0.4.2
+ Bump back to v1.15.0-dev
+ Add Containerfile to build a versioned stable image on quay.io
+
+## v1.14.1 (2020-02-27)
+ Search for local runtime per values in containers.conf
+ Set correct ownership on working directory
+ BATS : in teardown, umount stale mounts
+ Bump github.com/spf13/cobra from 0.0.5 to 0.0.6
+ Bump github.com/fsouza/go-dockerclient from 1.6.1 to 1.6.3
+ Bump github.com/stretchr/testify from 1.4.0 to 1.5.1
+ Replace unix with syscall to allow vendoring into libpod
+ Update to containers/common v0.4.1
+ Improve remote manifest retrieval
+ Fix minor spelling errors in containertools README
+ Clear the right variable in buildahimage
+ Correct a couple of incorrect format specifiers
+ Update to containers/common v0.3.0
+ manifest push --format: force an image type, not a list type
+ run: adjust the order in which elements are added to $PATH
+ getDateAndDigestAndSize(): handle creation time not being set
+ Bump github.com/containers/common from 0.2.0 to 0.2.1
+ include installation steps for CentOS 8 and Stream
+ include installation steps for CentOS7 and forks
+ Adjust Ubuntu install info to also work on Pop!_OS
+ Make the commit id clear like Docker
+ Show error on copied file above context directory in build
+ Bump github.com/containers/image/v5 from 5.2.0 to 5.2.1
+ pull/from/commit/push: retry on most failures
+ Makefile: fix install.cni.sudo
+ Repair buildah so it can use containers.conf on the server side
+ Bump github.com/mattn/go-shellwords from 1.0.9 to 1.0.10
+ Bump github.com/fsouza/go-dockerclient from 1.6.0 to 1.6.1
+ Fixing formatting & build instructions
+ Add Code of Conduct
+ Bors: Fix no. req. github reviews
+ Cirrus+Bors: Simplify temp branch skipping
+ Bors-ng: Add documentation and status-icon
+ Bump github.com/onsi/ginkgo from 1.11.0 to 1.12.0
+ fix XDG_RUNTIME_DIR for authfile
+ Cirrus: Disable F29 testing
+ Cirrus: Add jq package
+ Cirrus: Fix lint + validation using wrong epoch
+ Stop using fedorproject registry
+ Bors: Workaround ineffective required statuses
+ Bors: Enable app + Disable Travis
+ Cirrus: Add standardized log-collection
+ Cirrus: Improve automated lint + validation
+ Allow passing options to golangci-lint
+ Cirrus: Fixes from review feedback
+ Cirrus: Temporarily ignore VM testing failures
+ Cirrus: Migrate off papr + implement VM testing
+ Cirrus: Update packages + fixes for get_ci_vm.sh
+ Show validation command-line
+ Skip overlay test w/ vfs driver
+ use alpine, not centos, for various tests
+ Flake handling: cache and prefetch images
+ Bump to v1.15.0-dev
+
+## v1.14.0 (2020-02-05)
+ bump github.com/mtrmac/gpgme
+ Update containers/common to v0.1.4
+ manifest push: add --format option
+ Bump github.com/onsi/gomega from 1.8.1 to 1.9.0
+ vendor github.com/containers/image/v5@v5.2.0
+ info test: deal with random key order
+ Bump back to v1.14.0-dev
+
+## v1.13.2 (2020-01-29)
+ sign.bats: set GPG_TTY=/dev/null
+ Fix parse_unsupported.go
+ getDateAndDigestAndSize(): use manifest.Digest
+ Bump github.com/opencontainers/selinux from 1.3.0 to 1.3.1
+ Bump github.com/containers/common from 0.1.0 to 0.1.2
+ Touch up os/arch doc
+ chroot: handle slightly broken seccomp defaults
+ buildahimage: specify fuse-overlayfs mount options
+ Bump github.com/mattn/go-shellwords from 1.0.7 to 1.0.9
+ copy.bats: make sure we detect failures due to missing source
+ parse: don't complain about not being able to rename something to itself
+ Makefile: use a $(GO_TEST) macro, fix a typo
+ manifests: unit test fix
+ Fix build for 32bit platforms
+ Allow users to set OS and architecture on bud
+ Fix COPY in containerfile with envvar
+ Bump c/storage to v1.15.7
+ add --sign-by to bud/commit/push, --remove-signatures for pull/push
+ Remove cut/paste error in CHANGELOG.md
+ Update vendor of containers/common to v0.1.0
+ update install instructions for Debian, Raspbian and Ubuntu
+ Add support for containers.conf
+ Bump back to v1.14.0-dev
+
+## v1.13.1 (2020-01-14)
+ Bump github.com/containers/common from 0.0.5 to 0.0.7
+ Bump github.com/onsi/ginkgo from 1.10.3 to 1.11.0
+ Bump github.com/pkg/errors from 0.8.1 to 0.9.0
+ Bump github.com/onsi/gomega from 1.7.1 to 1.8.1
+ Add codespell support
+ copyFileWithTar: close source files at the right time
+ copy: don't digest files that we ignore
+ Check for .dockerignore specifically
+ Travis: rm go 1.12.x
+ Don't setup excludes, if their is only one pattern to match
+ set HOME env to /root on chroot-isolation by default
+ docs: fix references to containers-*.5
+ update openshift/api
+ fix bug Add check .dockerignore COPY file
+ buildah bud --volume: run from tmpdir, not source dir
+ Fix imageNamePrefix to give consistent names in buildah-from
+ cpp: use -traditional and -undef flags
+ Fix image reference in tutorial 4
+ discard outputs coming from onbuild command on buildah-from --quiet
+ make --format columnizing consistent with buildah images
+ Bump to v1.14.0-dev
+
+## v1.13.0 (2019-12-27)
+ Bump to c/storage v1.15.5
+ Update container/storage to v1.15.4
+ Fix option handling for volumes in build
+ Rework overlay pkg for use with libpod
+ Fix buildahimage builds for buildah
+ Add support for FIPS-Mode backends
+ Set the TMPDIR for pulling/pushing image to $TMPDIR
+ WIP: safer test for pull --all-tags
+ BATS major cleanup: blobcache.bats: refactor
+ BATS major cleanup: part 4: manual stuff
+ BATS major cleanup, step 3: yet more run_buildah
+ BATS major cleanup, part 2: use more run_buildah
+ BATS major cleanup, part 1: log-level
+ Bump github.com/containers/image/v5 from 5.0.0 to 5.1.0
+ Bump github.com/containers/common from 0.0.3 to 0.0.5
+ Bump to v1.13.0-dev
+
+## v1.12.0 (2019-12-13)
+ Allow ADD to use http src
+ Bump to c/storage v.1.15.3
+ install.md: update golang dependency
+ imgtype: reset storage opts if driver overridden
+ Start using containers/common
+ overlay.bats typo: fuse-overlays should be fuse-overlayfs
+ chroot: Unmount with MNT_DETACH instead of UnmountMountpoints()
+ bind: don't complain about missing mountpoints
+ imgtype: check earlier for expected manifest type
+ Vendor containers/storage fix
+ Vendor containers/storage v1.15.1
+ Add history names support
+ PR takeover of #1966
+ Tests: Add inspect test check steps
+ Tests: Add container name and id check in containers test steps
+ Test: Get permission in add test
+ Tests: Add a test for tag by id
+ Tests: Add test cases for push test
+ Tests: Add image digest test
+ Tests: Add some buildah from tests
+ Tests: Add two commit test
+ Tests: Add buildah bud with --quiet test
+ Tests: Add two test for buildah add
+ Bump back to v1.12.0-dev
+
+## v1.11.6 (2019-12-03)
+ Handle missing equal sign in --from and --chown flags for COPY/ADD
+ bud COPY does not download URL
+ Bump github.com/onsi/gomega from 1.7.0 to 1.7.1
+ Fix .dockerignore exclude regression
+ Ran buildah through codespell
+ commit(docker): always set ContainerID and ContainerConfig
+ Touch up commit man page image parameter
+ Add builder identity annotations.
+ info: use util.Runtime()
+ Bump github.com/onsi/ginkgo from 1.10.2 to 1.10.3
+ Bump back to v1.12.0-dev
+
+## v1.11.5 (2019-11-11)
+ Enhance error on unsafe symbolic link targets
+ Add OCIRuntime to info
+ Check nonexsit authfile
+ Only output image id if running buildah bud --quiet
+ Fix --pull=true||false and add --pull-never to bud and from (retry)
+ cgroups v2: tweak or skip tests
+ Prepwork: new 'skip' helpers for tests
+ Handle configuration blobs for manifest lists
+ unmarshalConvertedConfig: avoid using the updated image's ref
+ Add completions for Manifest commands
+ Add disableFips option to secrets pkg
+ Update bud.bats test archive test
+ Add test for caching based on content digest
+ Builder.untarPath(): always evaluate b.ContentDigester.Hash()
+ Bump github.com/onsi/ginkgo from 1.10.1 to 1.10.2
+ Fix another broken test: copy-url-mtime
+ yet more fixes
+ Actual bug fix for 'add' test: fix the expected mode
+ BATS tests - lots of mostly minor cleanup
+ build: drop support for ostree
+ Add support for make vendor-in-container
+ imgtype: exit with error if storage fails
+ remove XDG_RUNTIME_DIR from default authfile path
+ fix troubleshooting redirect instructions
+ Bump back to v1.12.0-dev
+
+## v1.11.4 (2019-10-28)
+ buildah: add a "manifest" command
+ manifests: add the module
+ pkg/supplemented: add a package for grouping images together
+ pkg/manifests: add a manifest list build/manipulation API
+ Update for ErrUnauthorizedForCredentials API change in containers/image
+ Update for manifest-lists API changes in containers/image
+ version: also note the version of containers/image
+ Move to containers/image v5.0.0
+ Enable --device directory as src device
+ Fix git build with branch specified
+ Bump github.com/openshift/imagebuilder from 1.1.0 to 1.1.1
+ Bump github.com/fsouza/go-dockerclient from 1.4.4 to 1.5.0
+ Add clarification to the Tutorial for new users
+ Silence "using cache" to ensure -q is fully quiet
+ Add OWNERS File to Buildah
+ Bump github.com/containers/storage from 1.13.4 to 1.13.5
+ Move runtime flag to bud from common
+ Commit: check for storage.ErrImageUnknown using errors.Cause()
+ Fix crash when invalid COPY --from flag is specified.
+ Bump back to v1.12.0-dev
+
+## v1.11.3 (2019-10-04)
+ Update c/image to v4.0.1
+ Bump github.com/spf13/pflag from 1.0.3 to 1.0.5
+ Fix --build-args handling
+ Bump github.com/spf13/cobra from 0.0.3 to 0.0.5
+ Bump github.com/cyphar/filepath-securejoin from 0.2.1 to 0.2.2
+ Bump github.com/onsi/ginkgo from 1.8.0 to 1.10.1
+ Bump github.com/fsouza/go-dockerclient from 1.3.0 to 1.4.4
+ Add support for retrieving context from stdin "-"
+ Ensure bud remote context cleans up on error
+ info: add cgroups2
+ Bump github.com/seccomp/libseccomp-golang from 0.9.0 to 0.9.1
+ Bump github.com/mattn/go-shellwords from 1.0.5 to 1.0.6
+ Bump github.com/stretchr/testify from 1.3.0 to 1.4.0
+ Bump github.com/opencontainers/selinux from 1.2.2 to 1.3.0
+ Bump github.com/etcd-io/bbolt from 1.3.2 to 1.3.3
+ Bump github.com/onsi/gomega from 1.5.0 to 1.7.0
+ update c/storage to v1.13.4
+ Print build 'STEP' line to stdout, not stderr
+ Fix travis-ci on forks
+ Vendor c/storage v1.13.3
+ Use Containerfile by default
+ Added tutorial on how to include Buildah as library
+ util/util: Fix "configuraitno" -> "configuration" log typo
+ Bump back to v1.12.0-dev
+
+## v1.11.2 (2019-09-13)
+ Add some cleanup code
+ Move devices code to unit specific directory.
+ Bump back to v1.12.0-dev
+
+## v1.11.1 (2019-09-11)
+ Add --devices flag to bud and from
+ Downgrade .papr to highest atomic verion
+ Add support for /run/.containerenv
+ Truncate output of too long image names
+ Preserve file and directory mount permissions
+ Bump fedora version from 28 to 30
+ makeImageRef: ignore EmptyLayer if Squash is set
+ Set TMPDIR to /var/tmp by default
+ replace --debug=false with --log-level=error
+ Allow mounts.conf entries for equal source and destination paths
+ fix label and annotation for 1-line Dockerfiles
+ Enable interfacer linter and fix lints
+ install.md: mention goproxy
+ Makefile: use go proxy
+ Bump to v1.12.0-dev
+
+## v1.11.0 (2019-08-29)
+ tests/bud.bats: add --signature-policy to some tests
+ Vendor github.com/openshift/api
+ pull/commit/push: pay attention to $BUILD_REGISTRY_SOURCES
+ Add `--log-level` command line option and deprecate `--debug`
+ add support for cgroupsV2
+ Correctly detect ExitError values from Run()
+ Disable empty logrus timestamps to reduce logger noise
+ Remove outdated deps Makefile target
+ Remove gofmt.sh in favor of golangci-lint
+ Remove govet.sh in favor of golangci-lint
+ Allow to override build date with SOURCE_DATE_EPOCH
+ Update shebangs to take env into consideration
+ Fix directory pull image names
+ Add --digestfile and Re-add push statement as debug
+ README: mention that Podman uses Buildah's API
+ Use content digests in ADD/COPY history entries
+ add: add a DryRun flag to AddAndCopyOptions
+ Fix possible runtime panic on bud
+ Add security-related volume options to validator
+ use correct path for ginkgo
+ Add bud 'without arguments' integration tests
+ Update documentation about bud
+ add: handle hard links when copying with .dockerignore
+ add: teach copyFileWithTar() about symlinks and directories
+ Allow buildah bud to be called without arguments
+ imagebuilder: fix detection of referenced stage roots
+ Touch up go mod instructions in install
+ run_linux: fix mounting /sys in a userns
+ Vendor Storage v1.13.2
+ Cirrus: Update VM images
+ Fix handling of /dev/null masked devices
+ Update `bud`/`from` help to contain indicator for `--dns=none`
+ Bump back to v1.11.0-dev
+
+## v1.10.1 (2019-08-08)
+ Bump containers/image to v3.0.2 to fix keyring issue
+ Bug fix for volume minus syntax
+ Bump container/storage v1.13.1 and containers/image v3.0.1
+ bump github.com/containernetworking/cni to v0.7.1
+ Add overlayfs to fuse-overlayfs tip
+ Add automatic apparmor tag discovery
+ Fix bug whereby --get-login has no effect
+ Bump to v1.11.0-dev
+
+## v1.10.0 (2019-08-02)
+ vendor github.com/containers/image@v3.0.0
+ Remove GO111MODULE in favor of `-mod=vendor`
+ Vendor in containers/storage v1.12.16
+ Add '-' minus syntax for removal of config values
+ tests: enable overlay tests for rootless
+ rootless, overlay: use fuse-overlayfs
+ vendor github.com/containers/image@v2.0.1
+ Added '-' syntax to remove volume config option
+ delete `successfully pushed` message
+ Add golint linter and apply fixes
+ vendor github.com/containers/storage@v1.12.15
+ Change wait to sleep in buildahimage readme
+ Handle ReadOnly images when deleting images
+ Add support for listing read/only images
+
+## v1.9.2 (2019-07-19)
+ from/import: record the base image's digest, if it has one
+ Fix CNI version retrieval to not require network connection
+ Add misspell linter and apply fixes
+ Add goimports linter and apply fixes
+ Add stylecheck linter and apply fixes
+ Add unconvert linter and apply fixes
+ image: make sure we don't try to use zstd compression
+ run.bats: skip the "z" flag when testing --mount
+ Update to runc v1.0.0-rc8
+ Update to match updated runtime-tools API
+ bump github.com/opencontainers/runtime-tools to v0.9.0
+ Build e2e tests using the proper build tags
+ Add unparam linter and apply fixes
+ Run: correct a typo in the --cap-add help text
+ unshare: add a --mount flag
+ fix push check image name is not empty
+ Bump to v1.9.2-dev
+
+## v1.9.1 (2019-07-12)
+ add: fix slow copy with no excludes
+ Add errcheck linter and fix missing error check
+ Improve tests/tools/Makefile parallelism and abstraction
+ Fix response body not closed resource leak
+ Switch to golangci-lint
+ Add gomod instructions and mailing list links
+ On Masked path, check if /dev/null already mounted before mounting
+ Update to containers/storage v1.12.13
+ Refactor code in package imagebuildah
+ Add rootless podman with NFS issue in documentation
+ Add --mount for buildah run
+ import method ValidateVolumeOpts from libpod
+ Fix typo
+ Makefile: set GO111MODULE=off
+ rootless: add the built-in slirp DNS server
+ Update docker/libnetwork to get rid of outdated sctp package
+ Update buildah-login.md
+ migrate to go modules
+ install.md: mention go modules
+ tests/tools: go module for test binaries
+ fix --volume splits comma delimited option
+ Add bud test for RUN with a priv'd command
+ vendor logrus v1.4.2
+ pkg/cli: panic when flags can't be hidden
+ pkg/unshare: check all errors
+ pull: check error during report write
+ run_linux.go: ignore unchecked errors
+ conformance test: catch copy error
+ chroot/run_test.go: export funcs to actually be executed
+ tests/imgtype: ignore error when shutting down the store
+ testreport: check json error
+ bind/util.go: remove unused func
+ rm chroot/util.go
+ imagebuildah: remove unused `dedupeStringSlice`
+ StageExecutor: EnsureContainerPath: catch error from SecureJoin()
+ imagebuildah/build.go: return instead of branching
+ rmi: avoid redundant branching
+ conformance tests: nilness: allocate map
+ imagebuildah/build.go: avoid redundant `filepath.Join()`
+ imagebuildah/build.go: avoid redundant `os.Stat()`
+ imagebuildah: omit comparison to bool
+ fix "ineffectual assignment" lint errors
+ docker: ignore "repeats json tag" lint error
+ pkg/unshare: use `...` instead of iterating a slice
+ conformance: bud test: use raw strings for regexes
+ conformance suite: remove unused func/var
+ buildah test suite: remove unused vars/funcs
+ testreport: fix golangci-lint errors
+ util: remove redundant `return` statement
+ chroot: only log clean-up errors
+ images_test: ignore golangci-lint error
+ blobcache: log error when draining the pipe
+ imagebuildah: check errors in deferred calls
+ chroot: fix error handling in deferred funcs
+ cmd: check all errors
+ chroot/run_test.go: check errors
+ chroot/run.go: check errors in deferred calls
+ imagebuildah.Executor: remove unused onbuild field
+ docker/types.go: remove unused struct fields
+ util: use strings.ContainsRune instead of index check
+ Cirrus: Initial implementation
+ Bump to v1.9.1-dev
+
+## v1.9.0 (2019-06-15)
+ buildah-run: fix-out-of-range panic (2)
+ Bump back to v1.9.0-dev
+
+
+
+## v1.8.4 (2019-06-13)
+ Update containers/image to v2.0.0
+ run: fix hang with run and --isolation=chroot
+ run: fix hang when using run
+ chroot: drop unused function call
+ remove --> before imgageID on build
+ Always close stdin pipe
+ Write deny to setgroups when doing single user mapping
+ Avoid including linux/memfd.h
+ Add a test for the symlink pointing to a directory
+ Add missing continue
+ Fix the handling of symlinks to absolute paths
+ Only set default network sysctls if not rootless
+ Support --dns=none like podman
+ fix bug --cpu-shares parsing typo
+ Fix validate complaint
+ Update vendor on containers/storage to v1.12.10
+ Create directory paths for COPY thereby ensuring correct perms
+ imagebuildah: use a stable sort for comparing build args
+ imagebuildah: tighten up cache checking
+ bud.bats: add a test verying the order of --build-args
+ add -t to podman run
+ imagebuildah: simplify screening by top layers
+ imagebuildah: handle ID mappings for COPY --from
+ imagebuildah: apply additionalTags ourselves
+ bud.bats: test additional tags with cached images
+ bud.bats: add a test for WORKDIR and COPY with absolute destinations
+ Cleanup Overlay Mounts content
+
+## v1.8.3 (2019-06-04)
+ Add support for file secret mounts
+ Add ability to skip secrets in mounts file
+ allow 32bit builds
+ fix tutorial instructions
+ imagebuilder: pass the right contextDir to Add()
+ add: use fileutils.PatternMatcher for .dockerignore
+ bud.bats: add another .dockerignore test
+ unshare: fallback to single usermapping
+ addHelperSymlink: clear the destination on os.IsExist errors
+ bud.bats: test replacing symbolic links
+ imagebuildah: fix handling of destinations that end with '/'
+ bud.bats: test COPY with a final "/" in the destination
+ linux: add check for sysctl before using it
+ unshare: set _CONTAINERS_ROOTLESS_GID
+ Rework buildahimamges
+ build context: support https git repos
+ Add a test for ENV special chars behaviour
+ Check in new Dockerfiles
+ Apply custom SHELL during build time
+ config: expand variables only at the command line
+ SetEnv: we only need to expand v once
+ Add default /root if empty on chroot iso
+ Add support for Overlay volumes into the container.
+ Export buildah validate volume functions so it can share code with libpod
+ Bump baseline test to F30
+ Fix rootless handling of /dev/shm size
+ Avoid fmt.Printf() in the library
+ imagebuildah: tighten cache checking back up
+ Handle WORKDIR with dangling target
+ Default Authfile to proper path
+ Make buildah run --isolation follow BUILDAH_ISOLATION environment
+ Vendor in latest containers/storage and containers/image
+ getParent/getChildren: handle layerless images
+ imagebuildah: recognize cache images for layerless images
+ bud.bats: test scratch images with --layers caching
+ Get CHANGELOG.md updates
+ Add some symlinks to test our .dockerignore logic
+ imagebuildah: addHelper: handle symbolic links
+ commit/push: use an everything-allowed policy
+ Correct manpage formatting in files section
+ Remove must be root statement from buildah doc
+ Change image names to stable, testing and upstream
+ Bump back to v1.9.0-dev
+
+## v1.8.2 (2019-05-02)
+ Vendor Storage 1.12.6
+ Create scratch file in TESTDIR
+ Test bud-copy-dot with --layers picks up changed file
+ Bump back to 1.9.0-dev
+
+## v1.8.1 (2019-05-01)
+ Don't create directory on container
+ Replace kubernetes/pause in tests with k8s.gcr.io/pause
+ imagebuildah: don't remove intermediate images if we need them
+ Rework buildahimagegit to buildahimageupstream
+ Fix Transient Mounts
+ Handle WORKDIRs that are symlinks
+ allow podman to build a client for windows
+ Touch up 1.9-dev to 1.9.0-dev
+ Bump to 1.9-dev
+
+## v1.8.0 (2019-04-26)
+ Resolve symlink when checking container path
+ commit: commit on every instruction, but not always with layers
+ CommitOptions: drop the unused OnBuild field
+ makeImageRef: pass in the whole CommitOptions structure
+ cmd: API cleanup: stores before images
+ run: check if SELinux is enabled
+ Fix buildahimages Dockerfiles to include support for additionalimages mounted from host.
+ Detect changes in rootdir
+ Fix typo in buildah-pull(1)
+ Vendor in latest containers/storage
+ Keep track of any build-args used during buildah bud --layers
+ commit: always set a parent ID
+ imagebuildah: rework unused-argument detection
+ fix bug dest path when COPY .dockerignore
+ Move Host IDMAppings code from util to unshare
+ Add BUILDAH_ISOLATION rootless back
+ Travis CI: fail fast, upon error in any step
+ imagebuildah: only commit images for intermediate stages if we have to
+ Use errors.Cause() when checking for IsNotExist errors
+ auto pass http_proxy to container
+ Bump back to 1.8-dev
+
+## v1.7.3 (2019-04-16)
+ imagebuildah: don't leak image structs
+ Add Dockerfiles for buildahimages
+ Bump to Replace golang 1.10 with 1.12
+ add --dns* flags to buildah bud
+ Add hack/build_speed.sh test speeds on building container images
+ Create buildahimage Dockerfile for Quay
+ rename 'is' to 'expect_output'
+ squash.bats: test squashing in multi-layered builds
+ bud.bats: test COPY --from in a Dockerfile while using the cache
+ commit: make target image names optional
+ Fix bud-args to allow comma separation
+ oops, missed some tests in commit.bats
+ new helper: expect_line_count
+ New tests for #1467 (string slices in cmdline opts)
+ Workarounds for dealing with travis; review feedback
+ BATS tests - extensive but minor cleanup
+ imagebuildah: defer pulling images for COPY --from
+ imagebuildah: centralize COMMIT and image ID output
+ Travis: do not use traviswait
+ imagebuildah: only initialize imagebuilder configuration once per stage
+ Make cleaner error on Dockerfile build errors
+ unshare: move to pkg/
+ unshare: move some code from cmd/buildah/unshare
+ Fix handling of Slices versus Arrays
+ imagebuildah: reorganize stage and per-stage logic
+ imagebuildah: add empty layers for instructions
+ Add missing step in installing into Ubuntu
+ fix bug in .dockerignore support
+ imagebuildah: deduplicate prepended "FROM" instructions
+ Touch up intro
+ commit: set created-by to the shell if it isn't set
+ commit: check that we always set a "created-by"
+ docs/buildah.md: add "containers-" prefixes under "SEE ALSO"
+ Bump back to 1.8-dev
+
+## v1.7.2 (2019-03-28)
+ mount: do not create automatically a namespace
+ buildah: correctly create the userns if euid!=0
+ imagebuildah.Build: consolidate cleanup logic
+ CommitOptions: drop the redundant Store field
+ Move pkg/chrootuser from libpod to buildah.
+ imagebuildah: record image IDs and references more often
+ vendor imagebuilder v1.1.0
+ imagebuildah: fix requiresStart/noRunsRemaining confusion
+ imagebuildah: check for unused args across stages
+ bump github.com/containernetworking/cni to v0.7.0-rc2
+ imagebuildah: use "useCache" instead of "noCache"
+ imagebuildah.resolveNameToImageRef(): take name as a parameter
+ Export fields of the DokcerIgnore struct
+ imagebuildah: drop the duplicate containerIDs list
+ rootless: by default use the host network namespace
+ imagebuildah: split Executor and per-stage execution
+ imagebuildah: move some fields around
+ golint: make golint happy
+ docs: 01-intro.md: add missing . in Dockerfile examples
+ fix bug using .dockerignore
+ Do not create empty mounts.conf file
+ images: suppress a spurious blank line with no images
+ from: distinguish between ADD and COPY
+ fix bug to not separate each --label value with comma
+ buildah-bud.md: correct a typo, note a default
+ Remove mistaken code that got merged in other PR
+ add sample registries.conf to docs
+ escape shell variables in README example
+ slirp4netns: set mtu to 65520
+ images: imageReposToMap() already adds :
+ imagebuildah.ReposToMap: move to cmd
+ Build: resolve copyFrom references earlier
+ Allow rootless users to use the cache directory in homedir
+ bud.bats: use the per-test temp directory
+ bud.bats: log output before counting length
+ Simplify checks for leftover args
+ Print commitID with --layers
+ fix bug images use the template to print results
+ rootless: honor --net host
+ onsi/gomeage add missing files
+ vendor latest openshift/imagebuilder
+ Remove noop from squash help
+ Prepend a comment to files setup in container
+ imagebuildah resolveSymlink: fix handling of relative links
+ Errors should be printed to stderr
+ Add recommends for slirp4netns and fuse-overlay
+ Update pull and pull-always flags
+ Hide from users command options that we don't want them to use.
+ Update secrets fipsmode patch to work on rootless containers
+ fix unshare option handling and documentation
+ Vendor in latest containers/storage
+ Hard-code docker.Transport use in pull --all-tags
+ Use a types.ImageReference instead of (transport, name) strings in pullImage etc.
+ Move the computation of srcRef before first pullAndFindImage
+ Don't throw away user-specified tag for pull --all-tags
+ CHANGES BEHAVIOR: Remove the string format input to localImageNameForReference
+ Don't try to parse imageName as transport:image in pullImage
+ Use reference.WithTag instead of manual string manipulation in Pull
+ Don't pass image = transport:repo:tag, transport=transport to pullImage
+ Fix confusing variable naming in Pull
+ Don't try to parse image name as a transport:image
+ Fix error reporting when parsing trans+image
+ Remove 'transport == ""' handling from the pull path
+ Clean up "pulls" of local image IDs / ID prefixes
+ Simplify ExpandNames
+ Document the semantics of transport+name returned by ResolveName
+ UPdate gitvalidation epoch
+ Bump back to 1.8-dev
+
+## v1.7.1 (2019-02-26)
+ vendor containers/image v1.5
+ Move secrets code from libpod into buildah
+ Update CHANGELOG.md with the past changes
+ README.md: fix typo
+ Fix a few issues found by tests/validate/gometalinter.sh
+ Neutralize buildah/unshare on non-Linux platforms
+ Explicitly specify a directory to find(1)
+ README.md: rephrase Buildah description
+ Stop printing default twice in cli --help
+ install.md: add section about vendoring
+ Bump to 1.8-dev
+
+## v1.7 (2019-02-21)
+ vendor containers/image v1.4
+ Make "images --all" faster
+ Remove a misleading comment
+ Remove quiet option from pull options
+ Make sure buildah pull --all-tags only works with docker transport
+ Support oci layout format
+ Fix pulling of images within buildah
+ Fix tls-verify polarity
+ Travis: execute make vendor and hack/tree_status.sh
+ vendor.conf: remove unused dependencies
+ add missing vendor/github.com/containers/libpod/vendor.conf
+ vendor.conf: remove github.com/inconshreveable/mousetrap
+ make vendor: always fetch the latest vndr
+ add hack/tree_status.sh script
+ Bump c/Storage to 1.10
+ Add --all-tags test to pull
+ mount: make error clearer
+ Remove global flags from cli help
+ Set --disable-compression to true as documented
+ Help document using buildah mount in rootless mode
+ healthcheck start-period: update documentation
+ Vendor in latest c/storage and c/image
+ dumpbolt: handle nested buckets
+ Fix buildah commit compress by default
+ Test on xenial, not trusty
+ unshare: reexec using a memfd copy instead of the binary
+ Add --target to bud command
+ Fix example for setting multiple environment variables
+ main: fix rootless mode
+ buildah: force umask 022
+ pull.bats: specify registry config when using registries
+ pull.bats: use the temporary directory, not /tmp
+ unshare: do not set rootless mode if euid=0
+ Touch up cli help examples and a few nits
+ Add an undocumented dumpbolt command
+ Move tar commands into containers/storage
+ Fix bud issue with 2 line Dockerfile
+ Add package install descriptions
+ Note configuration file requirements
+ Replace urfave/cli with cobra
+ cleanup vendor.conf
+ Vendor in latest containers/storage
+ Add Quiet to PullOptions and PushOptions
+ cmd/commit: add flag omit-timestamp to allow for deterministic builds
+ Add options for empty-layer history entries
+ Make CLI help descriptions and usage a bit more consistent
+ vndr opencontainers/selinux
+ Bump baseline test Fedora to 29
+ Bump to v1.7-dev-1
+ Bump to v1.6-1
+ Add support for ADD --chown
+ imagebuildah: make EnsureContainerPath() check/create the right one
+ Bump 1.7-dev
+ Fix contrib/rpm/bulidah.spec changelog date
+
+## v1.6-1 (2019-01-18)
+ Add support for ADD --chown
+ imagebuildah: make EnsureContainerPath() check/create the right one
+ Fix contrib/rpm/bulidah.spec changelog date
+ Vendor in latest containers/storage
+ Revendor everything
+ Revendor in latest code by release
+ unshare: do not set USER=root
+ run: ignore EIO when flushing at the end, avoid double log
+ build-using-dockerfile,commit: disable compression by default
+ Update some comments
+ Make rootless work under no_pivot_root
+ Add CreatedAtRaw date field for use with Format
+ Properly format images JSON output
+ pull: add all-tags option
+ Fix support for multiple Short options
+ pkg/blobcache: add synchronization
+ Skip empty files in file check of conformance test
+ Use NoPivot also for RUN, not only for run
+ Remove no longer used isReferenceInsecure / isRegistryInsecure
+ Do not set OCIInsecureSkipTLSVerify based on registries.conf
+ Remove duplicate entries from images JSON output
+ vendor parallel-copy from containers/image
+ blobcache.bats: adjust explicit push tests
+ Handle one line Dockerfile with layers
+ We should only warn if user actually requests Hostname be set in image
+ Fix compiler Warning about comparing different size types
+ imagebuildah: don't walk if rootdir and path are equal
+ Add aliases for buildah containers, so buildah list, ls and ps work
+ vendor: use faster version instead compress/gzip
+ vendor: update libpod
+ Properly handle Hostname inside of RUN command
+ docs: mention how to mount in rootless mode
+ tests: use fully qualified name for centos image
+ travis.yml: use the fully qualified name for alpine
+ mount: allow mount only when using vfs
+ Add some tests for buildah pull
+ Touch up images -q processing
+ Refactor: Use library shared idtools.ParseIDMap() instead of bundling it
+ bump GITVALIDATE_EPOCH
+ cli.BudFlags: add `--platform` nop
+ Makefile: allow packagers to more easily add tags
+ Makefile: soften the requirement on git
+ tests: add containers json test
+ Inline blobCache.putBlob into blobCacheDestination.PutBlob
+ Move saveStream and putBlob near blobCacheDestination.PutBlob
+ Remove BlobCache.PutBlob
+ Update for API changes
+ Vendor c/image after merging c/image#536
+ Handle 'COPY --from' in Dockerfile
+ Vendor in latest content from github.com/containers/storage
+ Clarify docker.io default in push with docker-daemon
+ Test blob caching
+ Wire in a hidden --blob-cache option
+ Use a blob cache when we're asked to use one
+ Add --disable-compression to 'build-using-dockerfile'
+ Add a blob cache implementation
+ vendor: update containers/storage
+ Update for sysregistriesv2 API changes
+ Update containers/image to 63a1cbdc5e6537056695cf0d627c0a33b334df53
+ clean up makefile variables
+ Fix file permission
+ Complete the instructions for the command
+ Show warning when a build arg not used
+ Assume user 0 group 0, if /etc/passwd file in container.
+ Add buildah info command
+ Enable -q when --filter is used for images command
+ Add v1.5 Release Announcement
+ Fix dangling filter for images command
+ Fix completions to print Names as well as IDs
+ tests: Fix file permissions
+ Bump 1.6-dev
+
+## v1.5-1 (2018-11-21)
+ Bump min go to 1.10 in install.md
+ vendor: update ostree-go
+ Update docker build command line in conformance test
+ Print command in SystemExec as debug information
+ Add some skip word for inspect check in conformance test
+ Update regex for multi stage base test
+ Sort CLI flags
+ vendor: update containers/storage
+ Add note to install about non-root on RHEL/CentOS
+ Update imagebuild depdency to support heading ARGs in Dockerfile
+ rootless: do not specify --rootless to the OCI runtime
+ Export resolvesymlink function
+ Exclude --force-rm from common bud cli flags
+ run: bind mount /etc/hosts and /etc/resolv.conf if not in a volume
+ rootless: use slirp4netns to setup the network namespace
+ Instructions for completing the pull command
+ Fix travis to not run environment variable patch
+ rootless: only discard network configuration names
+ run: only set up /etc/hosts or /etc/resolv.conf with network
+ common: getFormat: match entire string not only the prefix
+ vendor: update libpod
+ Change validation EPOCH
+ Fixing broken link for container-registries.conf
+ Restore rootless isolation test for from volume ro test
+ ostree: fix tag for build constraint
+ Handle directories better in bud -f
+ vndr in latest containers/storage
+ Fix unshare gofmt issue
+ runSetupBuiltinVolumes(): break up volume setup
+ common: support a per-user registries conf file
+ unshare: do not override the configuration
+ common: honor the rootless configuration file
+ unshare: create a new mount namespace
+ unshare: support libpod rootless pkg
+ Use libpod GetDefaultStorage to report proper storage config
+ Allow container storage to manage the SELinux labels
+ Resolve image names with default transport in from command
+ run: When the value of isolation is set, use the set value instead of the default value.
+ Vendor in latest containers/storage and opencontainers/selinux
+ Remove no longer valid todo
+ Check for empty buildTime in version
+ Change gofmt so it runs on all but 1.10
+ Run gofmt only on Go 1.11
+ Walk symlinks when checking cached images for copied/added files
+ ReserveSELinuxLabels(): handle wrapped errors from OpenBuilder
+ Set WorkingDir to empty, not / for conformance
+ Update calls in e2e to addres 1101
+ imagebuilder.BuildDockerfiles: return the image ID
+ Update for changes in the containers/image API
+ bump(github.com/containers/image)
+ Allow setting --no-pivot default with an env var
+ Add man page and bash completion, for --no-pivot
+ Add the --no-pivot flag to the run command
+ Improve reporting about individual pull failures
+ Move the "short name but no search registries" error handling to resolveImage
+ Return a "search registries were needed but empty" indication in util.ResolveName
+ Simplify handling of the "tried to pull an image but found nothing" case in newBuilder
+ Don't even invoke the pull loop if options.FromImage == ""
+ Eliminate the long-running ref and img variables in resolveImage
+ In resolveImage, return immediately on success
+ Fix From As in Dockerfile
+ Vendor latest containers/image
+ Vendor in latest libpod
+ Sort CLI flags of buildah bud
+ Change from testing with golang 1.9 to 1.11.
+ unshare: detect when unprivileged userns are disabled
+ Optimize redundant code
+ fix missing format param
+ chroot: fix the args check
+ imagebuildah: make ResolveSymLink public
+ Update copy chown test
+ buildah: use the same logic for XDG_RUNTIME_DIR as podman
+ V1.4 Release Announcement
+ Podman --privileged selinux is broken
+ papr: mount source at gopath
+ parse: Modify the return value
+ parse: modify the verification of the isolation value
+ Make sure we log or return every error
+ pullImage(): when completing an image name, try docker://
+ Fix up Tutorial 3 to account for format
+ Vendor in latest containers/storage and containers/image
+ docs/tutorials/01-intro.md: enhanced installation instructions
+ Enforce "blocked" for registries for the "docker" transport
+ Correctly set DockerInsecureSkipTLSVerify when pulling images
+ chroot: set up seccomp and capabilities after supplemental groups
+ chroot: fix capabilities list setup and application
+ .papr.yml: log the podman version
+ namespaces.bats: fix handling of uidmap/gidmap options in pairs
+ chroot: only create user namespaces when we know we need them
+ Check /proc/sys/user/max_user_namespaces on unshare(NEWUSERNS)
+ bash/buildah: add isolation option to the from command
+
+## v1.4 (2018-10-02)
+ from: fix isolation option
+ Touchup pull manpage
+ Export buildah ReserveSELinuxLables so podman can use it
+ Add buildah.io to README.md and doc fixes
+ Update rmi man for prune changes
+ Ignore file not found removal error in bud
+ bump(github.com/containers/{storage,image})
+ NewImageSource(): only create one Diff() at a time
+ Copy ExposedPorts from base image into the config
+ tests: run conformance test suite in Travis
+ Change rmi --prune to not accept an imageID
+ Clear intermediate container IDs after each stage
+ Request podman version for build issues
+ unshare: keep the additional groups of the user
+ Builtin volumes should be owned by the UID/GID of the container
+ Get rid of dangling whitespace in markdown files
+ Move buildah from projecatatomic/buildah to containers/buildah
+ nitpick: parse.validateFlags loop in bud cli
+ bash: Completion options
+ Add signature policy to push tests
+ vendor in latest containers/image
+ Fix grammar in Container Tools Guide
+ Don't build btrfs if it is not installed
+ new: Return image-pulling errors from resolveImage
+ pull: Return image-pulling errors from pullImage
+ Add more volume mount tests
+ chroot: create missing parent directories for volume mounts
+ Push: Allow an empty destination
+ Add Podman relationship to readme, create container tools guide
+ Fix arg usage in buildah-tag
+ Add flags/arguments order verification to other commands
+ Handle ErrDuplicateName errors from store.CreateContainer()
+ Evaluate symbolic links on Add/Copy Commands
+ Vendor in latest containers/image and containers/storage
+ Retain bounding set when running containers as non root
+ run container-diff tests in Travis
+ buildah-images.md: Fix option contents
+ push: show image digest after push succeed
+ Vendor in latest containers/storage,image,libpod and runc
+ Change references to cri-o to point at new repository
+ Exclude --layers from the common bug cli flags
+ demos: Increase the executable permissions
+ run: clear default seccomp filter if not enabled
+ Bump maximum cyclomatic complexity to 45
+ stdin: on HUP, read everything
+ nitpick: use tabs in tests/helpers.bash
+ Add flags/arguments order verification to one arg commands
+ nitpick: decrease cognitive complexity in buildah-bud
+ rename: Avoid renaming the same name as other containers
+ chroot isolation: chroot() before setting up seccomp
+ Small nitpick at the "if" condition in tag.go
+ cmd/images: Modify json option
+ cmd/images: Disallow the input of image when using the -a option
+ Fix examples to include context directory
+ Update containers/image to fix commit layer issue
+ cmd/containers: End loop early when using the json option
+ Make buildah-from error message clear when flags are after arg
+ Touch up README.md for conformance tests
+ Update container/storage for lock fix
+ cmd/rm: restore the correct containerID display
+ Remove debug lines
+ Remove docker build image after each test
+ Add README for conformance test
+ Update the MakeOptions to accept all command options for buildah
+ Update regrex to fit the docker output in test "run with JSON"
+ cmd/buildah: Remove redundant variable declarations
+ Warn about using Commands in Dockerfile that are not supported by OCI.
+ Add buildah bud conformance test
+ Fix rename to also change container name in builder
+ Makefile: use $(GO) env-var everywhere
+ Cleanup code to more closely match Docker Build images
+ Document BUILDAH_* environment variables in buildah bud --help output
+ Return error immediately if error occurs in Prepare step
+ Fix --layers ADD from url issue
+ Add "Sign your PRs" TOC item to contributing.md.
+ Display the correct ID after deleting image
+ rmi: Modify the handling of errors
+ Let util.ResolveName() return parsing errors
+ Explain Open Container Initiative (OCI) acronym, add link
+ Update vendor for urfave/cli back to master
+ Handle COPY --chown in Dockerfile
+ Switch to Recommends container-selinux
+ Update vendor for containernetworking, imagebuildah and podman
+ Document STORAGE_DRIVER and STORAGE_OPTS environment variable
+ Change references to projectatomic/libpod to containers/libpod
+ Add container PATH retrieval example
+ Expand variables names for --env
+ imagebuildah: provide a way to provide stdin for RUN
+ Remove an unused srcRef.NewImageSource in pullImage
+ chroot: correct a comment
+ chroot: bind mount an empty directory for masking
+ Don't bother with --no-pivot for rootless isolation
+ CentOS need EPEL repo
+ Export a Pull() function
+ Remove stream options, since docker build does not have it
+ release v1.3: mention openSUSE
+ Add Release Announcements directory
+ Bump to v1.4-dev
+
+## 1.3 (2018-08-4)
+ Revert pull error handling from 881
+ bud should not search context directory for Dockerfile
+ Set BUILDAH_ISOLATION=rootless when running unprivileged
+ .papr.sh: Also test with BUILDAH_ISOLATION=rootless
+ Skip certain tests when we're using "rootless" isolation
+ .travis.yml: run integration tests with BUILDAH_ISOLATION=chroot
+ Add and implement IsolationOCIRootless
+ Add a value for IsolationOCIRootless
+ Fix rmi to remove intermediate images associated with an image
+ Return policy error on pull
+ Update containers/image to 216acb1bcd2c1abef736ee322e17147ee2b7d76c
+ Switch to github.com/containers/image/pkg/sysregistriesv2
+ unshare: make adjusting the OOM score optional
+ Add flags validation
+ chroot: handle raising process limits
+ chroot: make the resource limits name map module-global
+ Remove rpm.bats, we need to run this manually
+ Set the default ulimits to match Docker
+ buildah: no args is out of bounds
+ unshare: error message missed the pid
+ preprocess ".in" suffixed Dockerfiles
+ Fix the the in buildah-config man page
+ Only test rpmbuild on latest fedora
+ Add support for multiple Short options
+ Update to latest urvave/cli
+ Add additional SELinux tests
+ Vendor in latest github.com/containers/{image;storage}
+ Stop testing with golang 1.8
+ Fix volume cache issue with buildah bud --layers
+ Create buildah pull command
+ Increase the deadline for gometalinter during 'make validate'
+ .papr.sh: Also test with BUILDAH_ISOLATION=chroot
+ .travis.yml: run integration tests with BUILDAH_ISOLATION=chroot
+ Add a Dockerfile
+ Set BUILDAH_ISOLATION=chroot when running unprivileged
+ Add and implement IsolationChroot
+ Update github.com/opencontainers/runc
+ maybeReexecUsingUserNamespace: add a default for root
+ Allow ping command without NET_RAW Capabilities
+ rmi.storageImageID: fix Wrapf format warning
+ Allow Dockerfile content to come from stdin
+ Vendor latest container/storage to fix overlay mountopt
+ userns: assign additional IDs sequentially
+ Remove default dev/pts
+ Add OnBuild test to baseline test
+ tests/run.bats(volumes): use :z when SELinux is enabled
+ Avoid a stall in runCollectOutput()
+ Use manifest from container/image
+ Vendor in latest containers/image and containers/storage
+ add rename command
+ Completion command
+ Update CHANGELOG.md
+ Update vendor for runc to fix 32 bit builds
+ bash completion: remove shebang
+ Update vendor for runc to fix 32 bit builds
+
+## 1.2 (2018-07-14)
+ Vendor in lates containers/image
+ build-using-dockerfile: let -t include transports again
+ Block use of /proc/acpi and /proc/keys from inside containers
+ Fix handling of --registries-conf
+ Fix becoming a maintainer link
+ add optional CI test fo darwin
+ Don't pass a nil error to errors.Wrapf()
+ image filter test: use kubernetes/pause as a "since"
+ Add --cidfile option to from
+ vendor: update containers/storage
+ Contributors need to find the CONTRIBUTOR.md file easier
+ Add a --loglevel option to build-with-dockerfile
+ Create Development plan
+ cmd: Code improvement
+ allow buildah cross compile for a darwin target
+ Add unused function param lint check
+ docs: Follow man-pages(7) suggestions for SYNOPSIS
+ Start using github.com/seccomp/containers-golang
+ umount: add all option to umount all mounted containers
+ runConfigureNetwork(): remove an unused parameter
+ Update github.com/opencontainers/selinux
+ Fix buildah bud --layers
+ Force ownership of /etc/hosts and /etc/resolv.conf to 0:0
+ main: if unprivileged, reexec in a user namespace
+ Vendor in latest imagebuilder
+ Reduce the complexity of the buildah.Run function
+ mount: output it before replacing lastError
+ Vendor in latest selinux-go code
+ Implement basic recognition of the "--isolation" option
+ Run(): try to resolve non-absolute paths using $PATH
+ Run(): don't include any default environment variables
+ build without seccomp
+ vendor in latest runtime-tools
+ bind/mount_unsupported.go: remove import errors
+ Update github.com/opencontainers/runc
+ Add Capabilities lists to BuilderInfo
+ Tweaks for commit tests
+ commit: recognize committing to second storage locations
+ Fix ARGS parsing for run commands
+ Add info on registries.conf to from manpage
+ Switch from using docker to podman for testing in .papr
+ buildah: set the HTTP User-Agent
+ ONBUILD tutorial
+ Add information about the configuration files to the install docs
+ Makefile: add uninstall
+ Add tilde info for push to troubleshooting
+ mount: support multiple inputs
+ Use the right formatting when adding entries to /etc/hosts
+ Vendor in latest go-selinux bindings
+ Allow --userns-uid-map/--userns-gid-map to be global options
+ bind: factor out UnmountMountpoints
+ Run(): simplify runCopyStdio()
+ Run(): handle POLLNVAL results
+ Run(): tweak terminal mode handling
+ Run(): rename 'copyStdio' to 'copyPipes'
+ Run(): don't set a Pdeathsig for the runtime
+ Run(): add options for adding and removing capabilities
+ Run(): don't use a callback when a slice will do
+ setupSeccomp(): refactor
+ Change RunOptions.Stdin/Stdout/Stderr to just be Reader/Writers
+ Escape use of '_' in .md docs
+ Break out getProcIDMappings()
+ Break out SetupIntermediateMountNamespace()
+ Add Multi From Demo
+ Use the c/image conversion code instead of converting configs manually
+ Don't throw away the manifest MIME type and guess again
+ Consolidate loading manifest and config in initConfig
+ Pass a types.Image to Builder.initConfig
+ Require an image ID in importBuilderDataFromImage
+ Use c/image/manifest.GuessMIMEType instead of a custom heuristic
+ Do not ignore any parsing errors in initConfig
+ Explicitly handle "from scratch" images in Builder.initConfig
+ Fix parsing of OCI images
+ Simplify dead but dangerous-looking error handling
+ Don't ignore v2s1 history if docker_version is not set
+ Add --rm and --force-rm to buildah bud
+ Add --all,-a flag to buildah images
+ Separate stdio buffering from writing
+ Remove tty check from images --format
+ Add environment variable BUILDAH_RUNTIME
+ Add --layers and --no-cache to buildah bud
+ Touch up images man
+ version.md: fix DESCRIPTION
+ tests: add containers test
+ tests: add images test
+ images: fix usage
+ fix make clean error
+ Change 'registries' to 'container registries' in man
+ add commit test
+ Add(): learn to record hashes of what we add
+ Minor update to buildah config documentation for entrypoint
+ Bump to v1.2-dev
+ Add registries.conf link to a few man pages
+
+## 1.1 (2018-06-08)
+ Drop capabilities if running container processes as non root
+ Print Warning message if cmd will not be used based on entrypoint
+ Update 01-intro.md
+ Shouldn't add insecure registries to list of search registries
+ Report errors on bad transports specification when pushing images
+ Move parsing code out of common for namespaces and into pkg/parse.go
+ Add disable-content-trust noop flag to bud
+ Change freenode chan to buildah
+ runCopyStdio(): don't close stdin unless we saw POLLHUP
+ Add registry errors for pull
+ runCollectOutput(): just read until the pipes are closed on us
+ Run(): provide redirection for stdio
+ rmi, rm: add test
+ add mount test
+ Add parameter judgment for commands that do not require parameters
+ Add context dir to bud command in baseline test
+ run.bats: check that we can run with symlinks in the bundle path
+ Give better messages to users when image can not be found
+ use absolute path for bundlePath
+ Add environment variable to buildah --format
+ rm: add validation to args and all option
+ Accept json array input for config entrypoint
+ Run(): process RunOptions.Mounts, and its flags
+ Run(): only collect error output from stdio pipes if we created some
+ Add OnBuild support for Dockerfiles
+ Quick fix on demo readme
+ run: fix validate flags
+ buildah bud should require a context directory or URL
+ Touchup tutorial for run changes
+ Validate common bud and from flags
+ images: Error if the specified imagename does not exist
+ inspect: Increase err judgments to avoid panic
+ add test to inspect
+ buildah bud picks up ENV from base image
+ Extend the amount of time travis_wait should wait
+ Add a make target for Installing CNI plugins
+ Add tests for namespace control flags
+ copy.bats: check ownerships in the container
+ Fix SELinux test errors when SELinux is enabled
+ Add example CNI configurations
+ Run: set supplemental group IDs
+ Run: use a temporary mount namespace
+ Use CNI to configure container networks
+ add/secrets/commit: Use mappings when setting permissions on added content
+ Add CLI options for specifying namespace and cgroup setup
+ Always set mappings when using user namespaces
+ Run(): break out creation of stdio pipe descriptors
+ Read UID/GID mapping information from containers and images
+ Additional bud CI tests
+ Run integration tests under travis_wait in Travis
+ build-using-dockerfile: add --annotation
+ Implement --squash for build-using-dockerfile and commit
+ Vendor in latest container/storage for devicemapper support
+ add test to inspect
+ Vendor github.com/onsi/ginkgo and github.com/onsi/gomega
+ Test with Go 1.10, too
+ Add console syntax highlighting to troubleshooting page
+ bud.bats: print "$output" before checking its contents
+ Manage "Run" containers more closely
+ Break Builder.Run()'s "run runc" bits out
+ util.ResolveName(): handle completion for tagged/digested image names
+ Handle /etc/hosts and /etc/resolv.conf properly in container
+ Documentation fixes
+ Make it easier to parse our temporary directory as an image name
+ Makefile: list new pkg/ subdirectoris as dependencies for buildah
+ containerImageSource: return more-correct errors
+ API cleanup: PullPolicy and TerminalPolicy should be types
+ Make "run --terminal" and "run -t" aliases for "run --tty"
+ Vendor github.com/containernetworking/cni v0.6.0
+ Update github.com/containers/storage
+ Update github.com/containers/libpod
+ Add support for buildah bud --label
+ buildah push/from can push and pull images with no reference
+ Vendor in latest containers/image
+ Update gometalinter to fix install.tools error
+ Update troubleshooting with new run workaround
+ Added a bud demo and tidied up
+ Attempt to download file from url, if fails assume Dockerfile
+ Add buildah bud CI tests for ENV variables
+ Re-enable rpm .spec version check and new commit test
+ Update buildah scratch demo to support el7
+ Added Docker compatibility demo
+ Update to F28 and new run format in baseline test
+ Touchup man page short options across man pages
+ Added demo dir and a demo. chged distrorlease
+ builder-inspect: fix format option
+ Add cpu-shares short flag (-c) and cpu-shares CI tests
+ Minor fixes to formatting in rpm spec changelog
+ Fix rpm .spec changelog formatting
+ CI tests and minor fix for cache related noop flags
+ buildah-from: add effective value to mount propagation
+
+## 1.0 (2018-05-06)
+ Declare Buildah 1.0
+ Add cache-from and no-cache noops, and fix doco
+ Update option and documentation for --force-rm
+ Adding noop for --force-rm to match --rm
+ Add buildah bud ENTRYPOINT,CMD,RUN tests
+ Adding buildah bud RUN test scenarios
+ Extend tests for empty buildah run command
+ Fix formatting error in run.go
+ Update buildah run to make command required
+ Expanding buildah run cmd/entrypoint tests
+ Update test cases for buildah run behaviour
+ Remove buildah run cmd and entrypoint execution
+ Add Files section with registries.conf to pertinent man pages
+ tests/config: perfect test
+ tests/from: add name test
+ Do not print directly to stdout in Commit()
+ Touch up auth test commands
+ Force "localhost" as a default registry
+ Drop util.GetLocalTime()
+ Vendor in latest containers/image
+ Validate host and container paths passed to --volume
+ test/from: add add-host test
+ Add --compress, --rm, --squash flags as a noop for bud
+ Add FIPS mode secret to buildah run and bud
+ Add config --comment/--domainname/--history-comment/--hostname
+ 'buildah config': stop replacing Created-By whenever it's not specified
+ Modify man pages so they compile correctly in mandb
+ Add description on how to do --isolation to buildah-bud man page
+ Add support for --iidfile to bud and commit
+ Refactor buildah bud for vendoring
+ Fail if date or git not installed
+ Revert update of entrypoint behaviour to match docker
+ Vendor in latest imagebuilder code to fix multiple stage builds
+ Add /bin/sh -c to entrypoint in config
+ image_test: Improve the test
+ Fix README example of buildah config
+ buildah-image: add validation to 'format'
+ Simple changes to allow buildah to pass make validate
+ Clarify the use of buildah config options
+ containers_test: Perfect testing
+ buildah images and podman images are listing different sizes
+ buildah-containers: add tests and example to the man page
+ buildah-containers: add validation to 'format'
+ Clarify the use of buildah config options
+ Minor fix for lighttpd example in README
+ Add tls-verification to troubleshooting
+ Modify buildah rmi to account for changes in containers/storage
+ Vendor in latest containers/image and containers/storage
+ addcopy: add src validation
+ Remove tarball as an option from buildah push --help
+ Fix secrets patch
+ Update entrypoint behaviour to match docker
+ Display imageId after commit
+ config: add support for StopSignal
+ Fix docker login issue in travis.yml
+ Allow referencing stages as index and names
+ Add multi-stage builds tests
+ Add multi-stage builds support
+ Add accessor functions for comment and stop signal
+ Vendor in latest imagebuilder, to get mixed case AS support
+ Allow umount to have multi-containers
+ Update buildah push doc
+ buildah bud walks symlinks
+ Imagename is required for commit atm, update manpage
+
+## 0.16.0 (2018-04-08)
+ Bump to v0.16.0
+ Remove requires for ostree-lib in rpm spec file
+ Add support for shell
+ buildah.spec should require ostree-libs
+ Vendor in latest containers/image
+ bash: prefer options
+ Change image time to locale, add troubleshooting.md, add logo to other mds
+ buildah-run.md: fix error SYNOPSIS
+ docs: fix error example
+ Allow --cmd parameter to have commands as values
+ Touchup README to re-enable logo
+ Clean up README.md
+ Make default-mounts-file a hidden option
+ Document the mounts.conf file
+ Fix man pages to format correctly
+ Add various transport support to buildah from
+ Add unit tests to run.go
+ If the user overrides the storage driver, the options should be dropped
+ Show Config/Manifest as JSON string in inspect when format is not set
+ Switch which for that in README.md
+ Remove COPR
+ Fix wrong order of parameters
+ Vendor in latest containers/image
+ Remove shallowCopy(), which shouldn't be saving us time any more
+ shallowCopy: avoid a second read of the container's layer
+
+## 0.5 - 2017-11-07
+ Add secrets patch to buildah
+ Add proper SELinux labeling to buildah run
+ Add tls-verify to bud command
+ Make filtering by date use the image's date
+ images: don't list unnamed images twice
+ Fix timeout issue
+ Add further tty verbiage to buildah run
+ Make inspect try an image on failure if type not specified
+ Add support for `buildah run --hostname`
+ Tons of bug fixes and code cleanup
+
+## 0.4 - 2017-09-22
+### Added
+ Update buildah spec file to match new version
+ Bump to version 0.4
+ Add default transport to push if not provided
+ Add authentication to commit and push
+ Remove --transport flag
+ Run: don't complain about missing volume locations
+ Add credentials to buildah from
+ Remove export command
+ Bump containers/storage and containers/image
+
+## 0.3 - 2017-07-20
+## 0.2 - 2017-07-18
+### Added
+ Vendor in latest containers/image and containers/storage
+ Update image-spec and runtime-spec to v1.0.0
+ Add support for -- ending options parsing to buildah run
+ Add/Copy need to support glob syntax
+ Add flag to remove containers on commit
+ Add buildah export support
+ update 'buildah images' and 'buildah rmi' commands
+ buildah containers/image: Add JSON output option
+ Add 'buildah version' command
+ Handle "run" without an explicit command correctly
+ Ensure volume points get created, and with perms
+ Add a -a/--all option to "buildah containers"
+
+## 0.1 - 2017-06-14
+### Added
+ Vendor in latest container/storage container/image
+ Add a "push" command
+ Add an option to specify a Create date for images
+ Allow building a source image from another image
+ Improve buildah commit performance
+ Add a --volume flag to "buildah run"
+ Fix inspect/tag-by-truncated-image-ID
+ Include image-spec and runtime-spec versions
+ buildah mount command should list mounts when no arguments are given.
+ Make the output image format selectable
+ commit images in multiple formats
+ Also import configurations from V2S1 images
+ Add a "tag" command
+ Add an "inspect" command
+ Update reference comments for docker types origins
+ Improve configuration preservation in imagebuildah
+ Report pull/commit progress by default
+ Contribute buildah.spec
+ Remove --mount from buildah-from
+ Add a build-using-dockerfile command (alias: bud)
+ Create manpages for the buildah project
+ Add installation for buildah and bash completions
+ Rename "list"/"delete" to "containers"/"rm"
+ Switch `buildah list quiet` option to only list container id's
+ buildah delete should be able to delete multiple containers
+ Correctly set tags on the names of pulled images
+ Don't mix "config" in with "run" and "commit"
+ Add a "list" command, for listing active builders
+ Add "add" and "copy" commands
+ Add a "run" command, using runc
+ Massive refactoring
+ Make a note to distinguish compression of layers
+
+## 0.0 - 2017-01-26
+### Added
+ Initial version, needs work
diff --git a/vendor/github.com/containers/buildah/CODE-OF-CONDUCT.md b/vendor/github.com/containers/buildah/CODE-OF-CONDUCT.md
new file mode 100644
index 00000000..dd95fce2
--- /dev/null
+++ b/vendor/github.com/containers/buildah/CODE-OF-CONDUCT.md
@@ -0,0 +1,3 @@
+## The Buildah Project Community Code of Conduct
+
+The Buildah Project, as part of Podman Container Tools, follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md).
diff --git a/vendor/github.com/containers/buildah/CONTRIBUTING.md b/vendor/github.com/containers/buildah/CONTRIBUTING.md
new file mode 100644
index 00000000..e6516577
--- /dev/null
+++ b/vendor/github.com/containers/buildah/CONTRIBUTING.md
@@ -0,0 +1,177 @@
+
+
+# Contributing to Buildah
+
+We'd love to have you join the community! Below summarizes the processes
+that we follow.
+
+## Topics
+
+* [Reporting Issues](#reporting-issues)
+* [Working On Issues](#working-on-issues)
+* [Submitting Pull Requests](#submitting-pull-requests)
+* [Sign your PRs](#sign-your-prs)
+* [Merge bot interaction](#merge-bot-interaction)
+* [Communications](#communications)
+* [Becoming a Maintainer](#becoming-a-maintainer)
+
+## Reporting Issues
+
+Before reporting an issue, check our backlog of
+[open issues](https://github.com/containers/buildah/issues)
+to see if someone else has already reported it. If so, feel free to add
+your scenario, or additional information, to the discussion. Or simply
+"subscribe" to it to be notified when it is updated.
+
+If you find a new issue with the project we'd love to hear about it! The most
+important aspect of a bug report is that it includes enough information for
+us to reproduce it. So, please include as much detail as possible and try
+to remove the extra stuff that doesn't really relate to the issue itself.
+The easier it is for us to reproduce it, the faster it'll be fixed!
+
+Please don't include any private/sensitive information in your issue!
+
+## Working On Issues
+
+Once you have decided to contribute to Buildah by working on an issue, check our
+backlog of [open issues](https://github.com/containers/buildah/issues) looking
+for any that do not have an "In Progress" label attached to it. Often issues
+will be assigned to someone, to be worked on at a later time. If you have the
+time to work on the issue now, add yourself as an assignee, and set the
+"In Progress" label if you’re a member of the “Containers” GitHub organization.
+If you can not set the label, just add a quick comment in the issue asking that
+the “In Progress” label be set and a member will do so for you.
+
+## Submitting Pull Requests
+
+No Pull Request (PR) is too small! Typos, additional comments in the code,
+new testcases, bug fixes, new features, more documentation, ... it's all
+welcome!
+
+While bug fixes can first be identified via an "issue", that is not required.
+It's ok to just open up a PR with the fix, but make sure you include the same
+information you would have included in an issue - like how to reproduce it.
+
+PRs for new features should include some background on what use cases the
+new code is trying to address. When possible and when it makes sense, try to break-up
+larger PRs into smaller ones - it's easier to review smaller
+code changes. But only if those smaller ones make sense as stand-alone PRs.
+
+Regardless of the type of PR, all PRs should include:
+* well documented code changes
+* additional testcases. Ideally, they should fail w/o your code change applied
+* documentation changes
+
+Squash your commits into logical pieces of work that might want to be reviewed
+separate from the rest of the PRs. But, squashing down to just one commit is ok
+too since in the end the entire PR will be reviewed anyway. When in doubt,
+squash.
+
+PRs that fix issues should include a reference like `Closes #XXXX` in the
+commit message so that github will automatically close the referenced issue
+when the PR is merged.
+
+
+
+### Sign your PRs
+
+The sign-off is a line at the end of the explanation for the patch. Your
+signature certifies that you wrote the patch or otherwise have the right to pass
+it on as an open-source patch. The rules are simple: if you can certify
+the below (from [developercertificate.org](http://developercertificate.org/)):
+
+```
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
+660 York Street, Suite 102,
+San Francisco, CA 94110 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+ have the right to submit it under the open source license
+ indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+ of my knowledge, is covered under an appropriate open source
+ license and I have the right under that license to submit that
+ work with modifications, whether created in whole or in part
+ by me, under the same open source license (unless I am
+ permitted to submit under a different license), as indicated
+ in the file; or
+
+(c) The contribution was provided directly to me by some other
+ person who certified (a), (b) or (c) and I have not modified
+ it.
+
+(d) I understand and agree that this project and the contribution
+ are public and that a record of the contribution (including all
+ personal information I submit with it, including my sign-off) is
+ maintained indefinitely and may be redistributed consistent with
+ this project or the open source license(s) involved.
+```
+
+Then you just add a line to every git commit message:
+
+ Signed-off-by: Joe Smith
+
+Use your real name (sorry, no pseudonyms or anonymous contributions.)
+
+If you set your `user.name` and `user.email` git configs, you can sign your
+commit automatically with `git commit -s`.
+
+## Merge bot interaction
+
+Maintainers should never merge anything directly into upstream
+branches. Instead, interact with the [openshift-ci-robot](https://github.com/openshift-ci-robot/)
+through PR comments as summarized [here](https://prow.ci.openshift.org/command-help?repo=containers%2Fbuildah).
+This ensures all upstream
+branches contain commits in a predictable order, and that every commit
+has passed automated testing at some point in the past. A
+[Maintainer portal](https://prow.ci.openshift.org/pr?query=is%3Apr%20state%3Aopen%20repo%3Acontainers%2Fbuildah)
+is available, showing all PRs awaiting review and approval.
+
+## Communications
+
+For general questions or discussions, please use the
+IRC channel `#podman` on `irc.libera.chat`. If you are unfamiliar with IRC you can start a web client at https://web.libera.chat/#podman.
+
+Alternatively, [\[matrix\]](https://matrix.org) can be used to access the same channel via federation at https://matrix.to/#/#podman:chat.fedoraproject.org.
+
+### For discussions around issues/bugs and features:
+
+#### GitHub
+You can also use GitHub
+[issues](https://github.com/containers/buildah/issues)
+and
+[PRs](https://github.com/containers/buildah/pulls)
+tracking system.
+
+#### Buildah Mailing List
+
+
+You can join the Buildah mailing list by sending an email to `buildah-join@lists.buildah.io` with the word `subscribe` in the subject. You can also go to this [page](https://lists.podman.io/admin/lists/buildah.lists.buildah.io/), then scroll down to the bottom of the page and enter your email and optionally name, then click on the "Subscribe" button.
+
+## Becoming a Maintainer
+
+To become a maintainer you must first be nominated by an existing maintainer.
+If a majority (>50%) of maintainers agree then the proposal is adopted and
+you will be added to the list.
+
+Removing a maintainer requires at least 75% of the remaining maintainers
+approval, or if the person requests to be removed then it is automatic.
+Normally, a maintainer will only be removed if they are considered to be
+inactive for a long period of time or are viewed as disruptive to the community.
+
+The current list of maintainers can be found in the
+[MAINTAINERS](./MAINTAINERS.md) file.
+
diff --git a/vendor/github.com/containers/buildah/GOVERNANCE.md b/vendor/github.com/containers/buildah/GOVERNANCE.md
new file mode 100644
index 00000000..94b838e3
--- /dev/null
+++ b/vendor/github.com/containers/buildah/GOVERNANCE.md
@@ -0,0 +1,12 @@
+## The Buildah Project Community Governance
+
+The Buildah project, as part of Podman Container Tools, follows the [Podman Project Governance](https://github.com/containers/podman/blob/main/GOVERNANCE.md)
+except sections found in this document, which override those found in Podman's Governance.
+
+---
+
+# Maintainers File
+
+The definitive source of truth for maintainers of this repository is the local [MAINTAINERS.md](./MAINTAINERS.md) file. The [MAINTAINERS.md](https://github.com/containers/podman/blob/main/MAINTAINERS.md) file in the main Podman repository is used for project-spanning roles, including Core Maintainer and Community Manager. Some repositories in the project will also have a local [OWNERS](./OWNERS) file, which the CI system uses to map users to roles. Any changes to the [OWNERS](./OWNERS) file must make a corresponding change to the [MAINTAINERS.md](./MAINTAINERS.md) file to ensure that the file remains up to date. Most changes to [MAINTAINERS.md](./MAINTAINERS.md) will require a change to the repository’s [OWNERS](.OWNERS) file (e.g., adding a Reviewer), but some will not (e.g., promoting a Maintainer to a Core Maintainer, which comes with no additional CI-related privileges).
+
+Any Core Maintainers listed in Podman’s [MAINTAINERS.md](https://github.com/containers/podman/blob/main/MAINTAINERS.md) file should also be added to the list of “approvers” in the local [OWNERS](./OWNERS) file and as a Core Maintainer in the list of “Maintainers” in the local [MAINTAINERS.md](./MAINTAINERS.md) file.
diff --git a/vendor/github.com/containers/buildah/LICENSE b/vendor/github.com/containers/buildah/LICENSE
new file mode 100644
index 00000000..8dada3ed
--- /dev/null
+++ b/vendor/github.com/containers/buildah/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ 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.
diff --git a/vendor/github.com/containers/buildah/MAINTAINERS.md b/vendor/github.com/containers/buildah/MAINTAINERS.md
new file mode 100644
index 00000000..83fd6a71
--- /dev/null
+++ b/vendor/github.com/containers/buildah/MAINTAINERS.md
@@ -0,0 +1,36 @@
+# Buildah Maintainers
+
+[GOVERNANCE.md](GOVERNANCE.md)
+describes the project's governance and the Project Roles used below.
+
+## Maintainers
+
+| Maintainer | GitHub ID | Project Roles | Affiliation |
+|-------------------|----------------------------------------------------------|----------------------------------|----------------------------------------------|
+| Brent Baude | [baude](https://github.com/baude) | Core Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
+| Nalin Dahyabhai | [nalind](https://github.com/nalind) | Core Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
+| Matthew Heon | [mheon](https://github.com/mheon) | Core Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
+| Paul Holzinger | [Luap99](https://github.com/Luap99) | Core Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
+| Giuseppe Scrivano | [giuseppe](https://github.com/giuseppe) | Core Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
+| Miloslav Trmač | [mtrmac](https://github.com/mtrmac) | Core Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
+| Mohan Boddu | [mohanboddu](https://github.com/mohanboddu) | Community Manager | [Red Hat](https://github.com/RedHatOfficial) |
+| Neil Smith | [actionmancan](https://github.com/actionmancan) | Community Manager | [Red Hat](https://github.com/RedHatOfficial) |
+| Tom Sweeney | [TomSweeneyRedHat](https://github.com/TomSweeneyRedHat/) | Maintainer and Community Manager | [Red Hat](https://github.com/RedHatOfficial) |
+| Lokesh Mandvekar | [lsm5](https://github.com/lsm5) | Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
+| Aditya Rajan | [flouthoc](https://github.com/flouthoc) | Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
+| Dan Walsh | [rhatdan](https://github.com/rhatdan) | Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
+| Ashley Cui | [ashley-cui](https://github.com/ashley-cui) | Reviewer | [Red Hat](https://github.com/RedHatOfficial) |
+| Jan Rodák | [Honny1](https://github.com/Honny1) | Reviewer | [Red Hat](https://github.com/RedHatOfficial) |
+| Valentin Rothberg | [vrothberg](https://github.com/vrothberg) | Reviewer | [Red Hat](https://github.com/RedHatOfficial) |
+
+## Alumni
+
+None at present
+
+## Credits
+
+The structure of this document was based off of the equivalent one in the [CRI-O Project](https://github.com/cri-o/cri-o/blob/main/MAINTAINERS.md).
+
+## Note
+
+If there is a discrepancy between the [MAINTAINERS.md](https://github.com/containers/podman/blob/main/MAINTAINERS.md) file in the main Podman repository and this file regarding Core Maintainers or Community Managers, the file in the Podman Repository is considered the source of truth.
diff --git a/vendor/github.com/containers/buildah/Makefile b/vendor/github.com/containers/buildah/Makefile
new file mode 100644
index 00000000..42a25d01
--- /dev/null
+++ b/vendor/github.com/containers/buildah/Makefile
@@ -0,0 +1,212 @@
+export GOPROXY=https://proxy.golang.org
+
+APPARMORTAG := $(shell hack/apparmor_tag.sh)
+STORAGETAGS := $(shell ./btrfs_installed_tag.sh) $(shell ./hack/libsubid_tag.sh)
+SECURITYTAGS ?= seccomp $(APPARMORTAG)
+TAGS ?= $(SECURITYTAGS) $(STORAGETAGS) $(shell ./hack/systemd_tag.sh) $(shell ./hack/sqlite_tag.sh)
+ifeq ($(shell uname -s),FreeBSD)
+# FreeBSD needs CNI until netavark is supported
+TAGS += cni
+endif
+BUILDTAGS += $(TAGS) $(EXTRA_BUILD_TAGS)
+PREFIX := /usr/local
+BINDIR := $(PREFIX)/bin
+BASHINSTALLDIR = $(PREFIX)/share/bash-completion/completions
+BUILDFLAGS := -tags "$(BUILDTAGS)"
+BUILDAH := buildah
+SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo -Z)
+SELINUXTYPE=container_runtime_exec_t
+AS ?= as
+STRIP ?= strip
+
+GO := go
+GO_LDFLAGS := $(shell if $(GO) version|grep -q gccgo; then echo "-gccgoflags"; else echo "-ldflags"; fi)
+GO_GCFLAGS := $(shell if $(GO) version|grep -q gccgo; then echo "-gccgoflags"; else echo "-gcflags"; fi)
+NPROCS := $(shell nproc)
+export GO_BUILD=$(GO) build
+export GO_TEST=$(GO) test -parallel=$(NPROCS)
+RACEFLAGS ?= $(shell $(GO_TEST) -race ./pkg/dummy > /dev/null 2>&1 && echo -race)
+
+COMMIT_NO ?= $(shell git rev-parse HEAD 2> /dev/null || true)
+GIT_COMMIT ?= $(if $(shell git status --porcelain --untracked-files=no),${COMMIT_NO}-dirty,${COMMIT_NO})
+SOURCE_DATE_EPOCH ?= $(if $(shell date +%s),$(shell date +%s),$(error "date failed"))
+
+# we get GNU make 3.x in MacOS build envs, which wants # to be escaped in
+# strings, while the 4.x we have on Linux doesn't. this is the documented
+# workaround
+COMMENT := \#
+CNI_COMMIT := $(shell sed -n 's;^$(COMMENT) github.com/containernetworking/cni \([^ \n]*\).*$$;\1;p' vendor/modules.txt)
+
+SEQUOIA_SONAME_DIR =
+EXTRA_LDFLAGS ?=
+BUILDAH_LDFLAGS := $(GO_LDFLAGS) '-X main.GitCommit=$(GIT_COMMIT) -X main.buildInfo=$(SOURCE_DATE_EPOCH) -X main.cniVersion=$(CNI_COMMIT) -X go.podman.io/image/v5/signature/internal/sequoia.sequoiaLibraryDir="$(SEQUOIA_SONAME_DIR)" $(EXTRA_LDFLAGS)'
+
+# This isn't what we actually build; it's a superset, used for target
+# dependencies. Basically: all *.go and *.c files, except *_test.go,
+# and except anything in a dot subdirectory. If any of these files is
+# newer than our target (bin/buildah), a rebuild is triggered.
+SOURCES=$(shell find . -path './.*' -prune -o \( \( -name '*.go' -o -name '*.c' \) -a ! -name '*_test.go' \) -print)
+
+LINTFLAGS ?=
+
+ifeq ($(BUILDDEBUG), 1)
+ override GOGCFLAGS += -N -l
+endif
+
+# Managed by renovate.
+export GOLANGCI_LINT_VERSION := 2.1.0
+
+# make all BUILDDEBUG=1
+# Note: Uses the -N -l go compiler options to disable compiler optimizations
+# and inlining. Using these build options allows you to subsequently
+# use source debugging tools like delve.
+all: bin/buildah bin/imgtype bin/copy bin/inet bin/tutorial bin/dumpspec bin/passwd bin/crash bin/wait docs
+
+bin/buildah: $(SOURCES) internal/mkcw/embed/entrypoint_amd64.gz
+ $(GO_BUILD) $(BUILDAH_LDFLAGS) $(GO_GCFLAGS) "$(GOGCFLAGS)" -o $@ $(BUILDFLAGS) ./cmd/buildah
+ test -z "${SELINUXOPT}" || chcon --verbose -t $(SELINUXTYPE) $@
+
+ifneq ($(shell $(AS) --version | grep x86_64),)
+internal/mkcw/embed/entrypoint_amd64.gz: internal/mkcw/embed/entrypoint_amd64
+ gzip -k9nf $^
+
+internal/mkcw/embed/entrypoint_amd64: internal/mkcw/embed/entrypoint_amd64.s
+ $(AS) -o $(patsubst %.s,%.o,$^) $^
+ $(LD) -o $@ $(patsubst %.s,%.o,$^)
+ $(STRIP) $@
+endif
+
+
+.PHONY: buildah
+buildah: bin/buildah
+
+ALL_CROSS_TARGETS := $(addprefix bin/buildah.,$(subst /,.,$(shell $(GO) tool dist list)))
+LINUX_CROSS_TARGETS := $(filter-out %.loong64,$(filter bin/buildah.linux.%,$(ALL_CROSS_TARGETS)))
+DARWIN_CROSS_TARGETS := $(filter bin/buildah.darwin.%,$(ALL_CROSS_TARGETS))
+WINDOWS_CROSS_TARGETS := $(addsuffix .exe,$(filter bin/buildah.windows.%,$(ALL_CROSS_TARGETS)))
+FREEBSD_CROSS_TARGETS := $(filter bin/buildah.freebsd.%,$(ALL_CROSS_TARGETS))
+.PHONY: cross
+cross: $(LINUX_CROSS_TARGETS) $(DARWIN_CROSS_TARGETS) $(WINDOWS_CROSS_TARGETS) $(FREEBSD_CROSS_TARGETS)
+
+bin/buildah.%: $(SOURCES)
+ mkdir -p ./bin
+ GOOS=$(word 2,$(subst ., ,$@)) GOARCH=$(word 3,$(subst ., ,$@)) $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ -tags "containers_image_openpgp" ./cmd/buildah
+
+bin/crash: $(SOURCES)
+ $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/crash
+
+bin/wait: $(SOURCES)
+ $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/wait
+
+bin/dumpspec: $(SOURCES)
+ $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/dumpspec
+
+bin/imgtype: $(SOURCES)
+ $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/imgtype/imgtype.go
+
+bin/copy: $(SOURCES)
+ $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/copy/copy.go
+
+bin/tutorial: $(SOURCES)
+ $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/tutorial/tutorial.go
+
+bin/inet: tests/inet/inet.go
+ $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/inet/inet.go
+
+bin/passwd: tests/passwd/passwd.go
+ $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/passwd/passwd.go
+
+.PHONY: clean
+clean:
+ $(RM) -r bin tests/testreport/testreport tests/conformance/testdata/mount-targets/true
+ $(MAKE) -C docs clean
+
+.PHONY: docs
+docs: install.tools ## build the docs on the host
+ $(MAKE) -C docs
+
+codespell:
+ codespell -w
+
+.PHONY: validate
+validate: install.tools
+ ./tests/validate/whitespace.sh
+ ./hack/xref-helpmsgs-manpages
+ ./tests/validate/pr-should-include-tests
+
+.PHONY: install.tools
+install.tools:
+ $(MAKE) -C tests/tools
+
+.PHONY: install
+install:
+ install -d -m 755 $(DESTDIR)/$(BINDIR)
+ install -m 755 bin/buildah $(DESTDIR)/$(BINDIR)/buildah
+ $(MAKE) -C docs install
+
+.PHONY: uninstall
+uninstall:
+ rm -f $(DESTDIR)/$(BINDIR)/buildah
+ rm -f $(PREFIX)/share/man/man1/buildah*.1
+ rm -f $(DESTDIR)/$(BASHINSTALLDIR)/buildah
+
+.PHONY: install.completions
+install.completions:
+ install -m 755 -d $(DESTDIR)/$(BASHINSTALLDIR)
+ install -m 644 contrib/completions/bash/buildah $(DESTDIR)/$(BASHINSTALLDIR)/buildah
+
+.PHONY: test-conformance
+test-conformance: tests/conformance/testdata/mount-targets/true
+ $(GO_TEST) -v -tags "$(STORAGETAGS) $(SECURITYTAGS)" -cover -timeout 60m ./tests/conformance
+
+.PHONY: test-integration
+test-integration: install.tools
+ cd tests; ./test_runner.sh
+
+tests/testreport/testreport: tests/testreport/testreport.go
+ $(GO_BUILD) $(GO_LDFLAGS) "-linkmode external -extldflags -static" -tags "$(STORAGETAGS) $(SECURITYTAGS)" -o tests/testreport/testreport ./tests/testreport/testreport.go
+
+tests/conformance/testdata/mount-targets/true: tests/conformance/testdata/mount-targets/true.go
+ $(GO_BUILD) $(GO_LDFLAGS) "-linkmode external -extldflags -static" -o tests/conformance/testdata/mount-targets/true tests/conformance/testdata/mount-targets/true.go
+
+.PHONY: test-unit
+test-unit: tests/testreport/testreport
+ $(GO_TEST) -v -tags "$(STORAGETAGS) $(SECURITYTAGS)" -cover $(RACEFLAGS) $(shell $(GO) list ./... | grep -v vendor | grep -v tests | grep -v cmd | grep -v chroot | grep -v copier) -timeout 45m
+ $(GO_TEST) -v -tags "$(STORAGETAGS) $(SECURITYTAGS)" $(RACEFLAGS) ./chroot ./copier -timeout 60m
+ tmp=$(shell mktemp -d) ; \
+ mkdir -p $$tmp/root $$tmp/runroot; \
+ $(GO_TEST) -v -tags "$(STORAGETAGS) $(SECURITYTAGS)" -cover $(RACEFLAGS) ./cmd/buildah -args --root $$tmp/root --runroot $$tmp/runroot --storage-driver vfs --signature-policy $(shell pwd)/tests/policy.json --registries-conf $(shell pwd)/tests/registries.conf
+
+vendor-in-container:
+ goversion=$(shell sed -e '/^go /!d' -e '/^go /s,.* ,,g' go.mod) ; \
+ if test -d `$(GO) env GOCACHE` && test -w `$(GO) env GOCACHE` ; then \
+ podman run --privileged --rm --env HOME=/root -v `$(GO) env GOCACHE`:/root/.cache/go-build --env GOCACHE=/root/.cache/go-build -v `pwd`:/src -w /src docker.io/library/golang:$$goversion make vendor ; \
+ else \
+ podman run --privileged --rm --env HOME=/root -v `pwd`:/src -w /src docker.io/library/golang:$$goversion make vendor ; \
+ fi
+
+.PHONY: vendor
+vendor:
+ $(GO) mod tidy
+ $(GO) mod vendor
+ $(GO) mod verify
+ if test -n "$(strip $(shell $(GO) env GOTOOLCHAIN))"; then go mod edit -toolchain none ; fi
+
+.PHONY: lint
+lint: install.tools
+ ./tests/tools/build/golangci-lint run $(LINTFLAGS)
+ ./tests/tools/build/golangci-lint run --tests=false $(LINTFLAGS)
+
+# CAUTION: This is not a replacement for RPMs provided by your distro.
+# Only intended to build and test the latest unreleased changes.
+.PHONY: rpm
+rpm: ## Build rpm packages
+ $(MAKE) -C rpm
+
+# Remember that rpms install exec to /usr/bin/buildah while a `make install`
+# installs them to /usr/local/bin/buildah which is likely before. Always use
+# a full path to test installed buildah or you risk to call another executable.
+.PHONY: rpm-install
+rpm-install: package ## Install rpm packages
+ $(call err_if_empty,PKG_MANAGER) -y install rpm/RPMS/*/*.rpm
+ /usr/bin/buildah version
diff --git a/vendor/github.com/containers/buildah/OWNERS b/vendor/github.com/containers/buildah/OWNERS
new file mode 100644
index 00000000..4deab296
--- /dev/null
+++ b/vendor/github.com/containers/buildah/OWNERS
@@ -0,0 +1,25 @@
+approvers:
+ - baude
+ - flouthoc
+ - giuseppe
+ - lsm5
+ - Luap99
+ - mheon
+ - mtrmac
+ - nalind
+ - rhatdan
+ - TomSweeneyRedHat
+reviewers:
+ - ashley-cui
+ - baude
+ - flouthoc
+ - giuseppe
+ - Honny1
+ - lsm5
+ - Luap99
+ - mheon
+ - mtrmac
+ - nalind
+ - rhatdan
+ - TomSweeneyRedHat
+ - vrothberg
diff --git a/vendor/github.com/containers/buildah/README.md b/vendor/github.com/containers/buildah/README.md
new file mode 100644
index 00000000..04c79307
--- /dev/null
+++ b/vendor/github.com/containers/buildah/README.md
@@ -0,0 +1,136 @@
+
+
+
+# [Buildah](https://www.youtube.com/embed/YVk5NgSiUw8) - a tool that facilitates building [Open Container Initiative (OCI)](https://www.opencontainers.org/) container images
+
+[](https://goreportcard.com/report/github.com/containers/buildah)
+[](https://www.bestpractices.dev/projects/10579)
+
+
+The Buildah package provides a command line tool that can be used to
+* create a working container, either from scratch or using an image as a starting point
+* create an image, either from a working container or via the instructions in a Dockerfile
+* images can be built in either the OCI image format or the traditional upstream docker image format
+* mount a working container's root filesystem for manipulation
+* unmount a working container's root filesystem
+* use the updated contents of a container's root filesystem as a filesystem layer to create a new image
+* delete a working container or an image
+* rename a local container
+
+## Buildah Information for Developers
+
+For blogs, release announcements and more, please checkout the [buildah.io](https://buildah.io) website!
+
+**[Buildah Container Images](https://github.com/containers/image_build/blob/main/buildah/README.md)**
+
+**[Buildah Demos](demos)**
+
+**[Changelog](CHANGELOG.md)**
+
+**[Contributing](CONTRIBUTING.md)**
+
+**[Development Plan](developmentplan.md)**
+
+**[Installation notes](install.md)**
+
+**[Troubleshooting Guide](troubleshooting.md)**
+
+**[Tutorials](docs/tutorials)**
+
+## Buildah and Podman relationship
+
+Buildah and Podman are two complementary open-source projects that are
+available on most Linux platforms and both projects reside at
+[GitHub.com](https://github.com) with Buildah
+[here](https://github.com/containers/buildah) and Podman
+[here](https://github.com/containers/podman). Both, Buildah and Podman are
+command line tools that work on Open Container Initiative (OCI) images and
+containers. The two projects differentiate in their specialization.
+
+Buildah specializes in building OCI images. Buildah's commands replicate all
+of the commands that are found in a Dockerfile. This allows building images
+with and without Dockerfiles while not requiring any root privileges.
+Buildah’s ultimate goal is to provide a lower-level coreutils interface to
+build images. The flexibility of building images without Dockerfiles allows
+for the integration of other scripting languages into the build process.
+Buildah follows a simple fork-exec model and does not run as a daemon
+but it is based on a comprehensive API in golang, which can be vendored
+into other tools.
+
+Podman specializes in all of the commands and functions that help you to maintain and modify
+OCI images, such as pulling and tagging. It also allows you to create, run, and maintain those containers
+created from those images. For building container images via Dockerfiles, Podman uses Buildah's
+golang API and can be installed independently from Buildah.
+
+A major difference between Podman and Buildah is their concept of a container. Podman
+allows users to create "traditional containers" where the intent of these containers is
+to be long lived. While Buildah containers are really just created to allow content
+to be added back to the container image. An easy way to think of it is the
+`buildah run` command emulates the RUN command in a Dockerfile while the `podman run`
+command emulates the `docker run` command in functionality. Because of this and their underlying
+storage differences, you can not see Podman containers from within Buildah or vice versa.
+
+In short, Buildah is an efficient way to create OCI images while Podman allows
+you to manage and maintain those images and containers in a production environment using
+familiar container cli commands. For more details, see the
+[Container Tools Guide](https://github.com/containers/buildah/tree/main/docs/containertools).
+
+## Example
+
+From [`./examples/lighttpd.sh`](examples/lighttpd.sh):
+
+```bash
+$ cat > lighttpd.sh <<"EOF"
+#!/usr/bin/env bash
+
+set -x
+
+ctr1=$(buildah from "${1:-fedora}")
+
+## Get all updates and install our minimal httpd server
+buildah run "$ctr1" -- dnf update -y
+buildah run "$ctr1" -- dnf install -y lighttpd
+
+## Include some buildtime annotations
+buildah config --annotation "com.example.build.host=$(uname -n)" "$ctr1"
+
+## Run our server and expose the port
+buildah config --cmd "/usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf" "$ctr1"
+buildah config --port 80 "$ctr1"
+
+## Commit this container to an image name
+buildah commit "$ctr1" "${2:-$USER/lighttpd}"
+EOF
+
+$ chmod +x lighttpd.sh
+$ ./lighttpd.sh
+```
+
+## Commands
+| Command | Description |
+| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
+| [buildah-add(1)](/docs/buildah-add.1.md) | Add the contents of a file, URL, or a directory to the container. |
+| [buildah-build(1)](/docs/buildah-build.1.md) | Build an image using instructions from Containerfiles or Dockerfiles. |
+| [buildah-commit(1)](/docs/buildah-commit.1.md) | Create an image from a working container. |
+| [buildah-config(1)](/docs/buildah-config.1.md) | Update image configuration settings. |
+| [buildah-containers(1)](/docs/buildah-containers.1.md) | List the working containers and their base images. |
+| [buildah-copy(1)](/docs/buildah-copy.1.md) | Copies the contents of a file, URL, or directory into a container's working directory. |
+| [buildah-from(1)](/docs/buildah-from.1.md) | Creates a new working container, either from scratch or using a specified image as a starting point. |
+| [buildah-images(1)](/docs/buildah-images.1.md) | List images in local storage. |
+| [buildah-info(1)](/docs/buildah-info.1.md) | Display Buildah system information. |
+| [buildah-inspect(1)](/docs/buildah-inspect.1.md) | Inspects the configuration of a container or image. |
+| [buildah-mount(1)](/docs/buildah-mount.1.md) | Mount the working container's root filesystem. |
+| [buildah-pull(1)](/docs/buildah-pull.1.md) | Pull an image from the specified location. |
+| [buildah-push(1)](/docs/buildah-push.1.md) | Push an image from local storage to elsewhere. |
+| [buildah-rename(1)](/docs/buildah-rename.1.md) | Rename a local container. |
+| [buildah-rm(1)](/docs/buildah-rm.1.md) | Removes one or more working containers. |
+| [buildah-rmi(1)](/docs/buildah-rmi.1.md) | Removes one or more images. |
+| [buildah-run(1)](/docs/buildah-run.1.md) | Run a command inside of the container. |
+| [buildah-tag(1)](/docs/buildah-tag.1.md) | Add an additional name to a local image. |
+| [buildah-umount(1)](/docs/buildah-umount.1.md) | Unmount a working container's root file system. |
+| [buildah-unshare(1)](/docs/buildah-unshare.1.md) | Launch a command in a user namespace with modified ID mappings. |
+| [buildah-version(1)](/docs/buildah-version.1.md) | Display the Buildah Version Information |
+
+**Future goals include:**
+* more CI tests
+* additional CLI commands (?)
diff --git a/vendor/github.com/containers/buildah/ROADMAP.md b/vendor/github.com/containers/buildah/ROADMAP.md
new file mode 100644
index 00000000..ae67904d
--- /dev/null
+++ b/vendor/github.com/containers/buildah/ROADMAP.md
@@ -0,0 +1,48 @@
+
+
+
+# Buildah Roadmap
+
+The Buildah development team reviews feature requests from its various stakeholders for consideration
+quarterly along with the Podman Development team. These features are then prioritized and the top
+features are then assigned to one or more engineers.
+
+
+## Future feature considerations
+
+The following features are of general importantance to Buildah. While these features have no timeline
+associated with them yet, they will likely be on future quarterly milestones.
+
+* Ongoing work around partial pull support (zstd:chunked)
+* Improved support for the BuildKit API.
+* Performance and stability improvements.
+* Reductions to the size of the Buildah binary.
+
+## Milestones and commitments by quarter
+
+This section is a historical account of what features were prioritized by quarter. Results of the prioritization will be added at start of each quarter (Jan, Apr, July, Oct).
+
+### 2025 Q2 ####
+
+#### Releases ####
+- [ ] Buildah 1.40
+
+#### Features ####
+- [ ] Reduce binary size of Buildah
+- [ ] Additional Containerfile command options
+
+#### CNCF ####
+- [ ] Add and adhere to Governance model
+- [ ] Update Maintainers file
+
+### 2025 Q1 ####
+
+#### Releases ####
+- [x] Buildah 1.39
+
+#### Features ####
+- [x] Artifact add --options
+
+#### CNCF ####
+- [x] Create Governance documentation
+- [x] Create Maintainers file
diff --git a/vendor/github.com/containers/buildah/SECURITY.md b/vendor/github.com/containers/buildah/SECURITY.md
new file mode 100644
index 00000000..dfc531a5
--- /dev/null
+++ b/vendor/github.com/containers/buildah/SECURITY.md
@@ -0,0 +1,3 @@
+## Security and Disclosure Information Policy for the Buildah Project
+
+The Buildah Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/main/SECURITY.md) for the Containers Projects.
diff --git a/vendor/github.com/containers/buildah/add.go b/vendor/github.com/containers/buildah/add.go
new file mode 100644
index 00000000..88049015
--- /dev/null
+++ b/vendor/github.com/containers/buildah/add.go
@@ -0,0 +1,1021 @@
+package buildah
+
+import (
+ "archive/tar"
+ "context"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "os"
+ "path"
+ "path/filepath"
+ "slices"
+ "strconv"
+ "strings"
+ "sync"
+ "syscall"
+ "time"
+
+ "github.com/containers/buildah/copier"
+ "github.com/containers/buildah/define"
+ "github.com/containers/buildah/internal/tmpdir"
+ "github.com/containers/buildah/pkg/chrootuser"
+ "github.com/docker/go-connections/tlsconfig"
+ "github.com/hashicorp/go-multierror"
+ "github.com/moby/sys/userns"
+ digest "github.com/opencontainers/go-digest"
+ v1 "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/sirupsen/logrus"
+ "go.podman.io/common/pkg/retry"
+ "go.podman.io/image/v5/pkg/tlsclientconfig"
+ "go.podman.io/image/v5/types"
+ "go.podman.io/storage/pkg/fileutils"
+ "go.podman.io/storage/pkg/idtools"
+ "go.podman.io/storage/pkg/regexp"
+)
+
+// AddAndCopyOptions holds options for add and copy commands.
+type AddAndCopyOptions struct {
+ // Chmod sets the access permissions of the destination content.
+ Chmod string
+ // Chown is a spec for the user who should be given ownership over the
+ // newly-added content, potentially overriding permissions which would
+ // otherwise be set to 0:0.
+ Chown string
+ // Checksum is a standard container digest string (e.g. :)
+ // and is the expected hash of the content being copied.
+ Checksum string
+ // PreserveOwnership, if Chown is not set, tells us to avoid setting
+ // ownership of copied items to 0:0, instead using whatever ownership
+ // information is already set. Not meaningful for remote sources or
+ // local archives that we extract.
+ PreserveOwnership bool
+ // All of the data being copied will pass through Hasher, if set.
+ // If the sources are URLs or files, their contents will be passed to
+ // Hasher.
+ // If the sources include directory trees, Hasher will be passed
+ // tar-format archives of the directory trees.
+ Hasher io.Writer
+ // Excludes is the contents of the .containerignore file.
+ Excludes []string
+ // IgnoreFile is the path to the .containerignore file.
+ IgnoreFile string
+ // ContextDir is the base directory for content being copied and
+ // Excludes patterns.
+ ContextDir string
+ // ID mapping options to use when contents to be copied are part of
+ // another container, and need ownerships to be mapped from the host to
+ // that container's values before copying them into the container.
+ IDMappingOptions *define.IDMappingOptions
+ // DryRun indicates that the content should be digested, but not actually
+ // copied into the container.
+ DryRun bool
+ // Clear the setuid bit on items being copied. Has no effect on
+ // archives being extracted, where the bit is always preserved.
+ StripSetuidBit bool
+ // Clear the setgid bit on items being copied. Has no effect on
+ // archives being extracted, where the bit is always preserved.
+ StripSetgidBit bool
+ // Clear the sticky bit on items being copied. Has no effect on
+ // archives being extracted, where the bit is always preserved.
+ StripStickyBit bool
+ // If not "", a directory containing a CA certificate (ending with
+ // ".crt"), a client certificate (ending with ".cert") and a client
+ // certificate key (ending with ".key") used when downloading sources
+ // from locations protected with TLS.
+ CertPath string
+ // Allow downloading sources from HTTPS where TLS verification fails.
+ InsecureSkipTLSVerify types.OptionalBool
+ // MaxRetries is the maximum number of attempts we'll make to retrieve
+ // contents from a remote location.
+ MaxRetries int
+ // RetryDelay is how long to wait before retrying attempts to retrieve
+ // remote contents.
+ RetryDelay time.Duration
+ // Parents specifies that we should preserve either all of the parent
+ // directories of source locations, or the ones which follow "/./" in
+ // the source paths for source locations which include such a
+ // component.
+ Parents bool
+ // Timestamp is a timestamp to override on all content as it is being read.
+ Timestamp *time.Time
+ // Link, when set to true, creates an independent layer containing the copied content
+ // that sits on top of existing layers. This layer can be cached and reused
+ // separately, and is not affected by filesystem changes from previous instructions.
+ Link bool
+ // BuildMetadata is consulted only when Link is true. Contains metadata used by
+ // imagebuildah for cache evaluation of linked layers (inheritLabels, unsetAnnotations,
+ // inheritAnnotations, newAnnotations). This field is internally managed and should
+ // not be set by external API users.
+ BuildMetadata string
+}
+
+// gitURLFragmentSuffix matches fragments to use as Git reference and build
+// context from the Git repository e.g.
+//
+// github.com/containers/buildah.git
+// github.com/containers/buildah.git#main
+// github.com/containers/buildah.git#v1.35.0
+var gitURLFragmentSuffix = regexp.Delayed(`\.git(?:#.+)?$`)
+
+// sourceIsGit returns true if "source" is a git location.
+func sourceIsGit(source string) bool {
+ return isURL(source) && gitURLFragmentSuffix.MatchString(source)
+}
+
+func isURL(url string) bool {
+ return strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://")
+}
+
+// sourceIsRemote returns true if "source" is a remote location
+// and *not* a git repo. Certain github urls such as raw.github.* are allowed.
+func sourceIsRemote(source string) bool {
+ return isURL(source) && !gitURLFragmentSuffix.MatchString(source)
+}
+
+// getURL writes a tar archive containing the named content
+func getURL(src string, chown *idtools.IDPair, mountpoint, renameTarget string, writer io.Writer, chmod *os.FileMode, srcDigest digest.Digest, certPath string, insecureSkipTLSVerify types.OptionalBool, timestamp *time.Time) error {
+ url, err := url.Parse(src)
+ if err != nil {
+ return err
+ }
+ tlsClientConfig := &tls.Config{
+ // As of 2025-08, tlsconfig.ClientDefault() differs from Go 1.23 defaults only in CipherSuites;
+ // so, limit us to only using that value. If go-connections/tlsconfig changes its policy, we
+ // will want to consider that and make a decision whether to follow suit.
+ // There is some chance that eventually the Go default will be to require TLS 1.3, and that point
+ // we might want to drop the dependency on go-connections entirely.
+ CipherSuites: tlsconfig.ClientDefault().CipherSuites,
+ }
+ if err := tlsclientconfig.SetupCertificates(certPath, tlsClientConfig); err != nil {
+ return err
+ }
+ tlsClientConfig.InsecureSkipVerify = insecureSkipTLSVerify == types.OptionalBoolTrue
+
+ tr := &http.Transport{
+ TLSClientConfig: tlsClientConfig,
+ Proxy: http.ProxyFromEnvironment,
+ }
+ httpClient := &http.Client{Transport: tr}
+ response, err := httpClient.Get(src)
+ if err != nil {
+ return err
+ }
+ defer response.Body.Close()
+
+ if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusBadRequest {
+ return fmt.Errorf("invalid response status %d", response.StatusCode)
+ }
+
+ // Figure out what to name the new content.
+ name := renameTarget
+ if name == "" {
+ name = path.Base(url.Path)
+ }
+ // If there's a date on the content, use it. If not, use the Unix epoch
+ // or a specified value for compatibility.
+ date := time.Unix(0, 0).UTC()
+ if timestamp != nil {
+ date = timestamp.UTC()
+ } else {
+ lastModified := response.Header.Get("Last-Modified")
+ if lastModified != "" {
+ d, err := time.Parse(time.RFC1123, lastModified)
+ if err != nil {
+ return fmt.Errorf("parsing last-modified time %q: %w", lastModified, err)
+ }
+ date = d.UTC()
+ }
+ }
+ // Figure out the size of the content.
+ size := response.ContentLength
+ var responseBody io.Reader = response.Body
+ if size < 0 {
+ // Create a temporary file and copy the content to it, so that
+ // we can figure out how much content there is.
+ f, err := os.CreateTemp(mountpoint, "download")
+ if err != nil {
+ return fmt.Errorf("creating temporary file to hold %q: %w", src, err)
+ }
+ defer os.Remove(f.Name())
+ defer f.Close()
+ size, err = io.Copy(f, response.Body)
+ if err != nil {
+ return fmt.Errorf("writing %q to temporary file %q: %w", src, f.Name(), err)
+ }
+ _, err = f.Seek(0, io.SeekStart)
+ if err != nil {
+ return fmt.Errorf("setting up to read %q from temporary file %q: %w", src, f.Name(), err)
+ }
+ responseBody = f
+ }
+ var digester digest.Digester
+ if srcDigest != "" {
+ digester = srcDigest.Algorithm().Digester()
+ responseBody = io.TeeReader(responseBody, digester.Hash())
+ }
+ // Write the output archive. Set permissions for compatibility.
+ tw := tar.NewWriter(writer)
+ defer tw.Close()
+ uid := 0
+ gid := 0
+ if chown != nil {
+ uid = chown.UID
+ gid = chown.GID
+ }
+ var mode int64 = 0o600
+ if chmod != nil {
+ mode = int64(*chmod)
+ }
+ hdr := tar.Header{
+ Typeflag: tar.TypeReg,
+ Name: name,
+ Size: size,
+ Uid: uid,
+ Gid: gid,
+ Mode: mode,
+ ModTime: date,
+ }
+ err = tw.WriteHeader(&hdr)
+ if err != nil {
+ return fmt.Errorf("writing header: %w", err)
+ }
+
+ if _, err := io.Copy(tw, responseBody); err != nil {
+ return fmt.Errorf("writing content from %q to tar stream: %w", src, err)
+ }
+
+ if digester != nil {
+ if responseDigest := digester.Digest(); responseDigest != srcDigest {
+ return fmt.Errorf("unexpected response digest for %q: %s, want %s", src, responseDigest, srcDigest)
+ }
+ }
+
+ return nil
+}
+
+// includeDirectoryAnyway returns true if "path" is a prefix for an exception
+// known to "pm". If "path" is a directory that "pm" claims matches its list
+// of patterns, but "pm"'s list of exclusions contains a pattern for which
+// "path" is a prefix, then IncludeDirectoryAnyway() will return true.
+// This is not always correct, because it relies on the directory part of any
+// exception paths to be specified without wildcards.
+func includeDirectoryAnyway(path string, pm *fileutils.PatternMatcher) bool {
+ if !pm.Exclusions() {
+ return false
+ }
+ prefix := strings.TrimPrefix(path, string(os.PathSeparator)) + string(os.PathSeparator)
+ for _, pattern := range pm.Patterns() {
+ if !pattern.Exclusion() {
+ continue
+ }
+ spec := strings.TrimPrefix(pattern.String(), string(os.PathSeparator))
+ if strings.HasPrefix(spec, prefix) {
+ return true
+ }
+ }
+ return false
+}
+
+// globbedToGlobbable takes a pathname which might include the '[', *, or ?
+// characters, and converts it into a glob pattern that matches itself by
+// marking the '[' characters as _not_ the beginning of match ranges and
+// escaping the * and ? characters.
+func globbedToGlobbable(glob string) string {
+ result := glob
+ result = strings.ReplaceAll(result, "[", "[[]")
+ result = strings.ReplaceAll(result, "?", "\\?")
+ result = strings.ReplaceAll(result, "*", "\\*")
+ return result
+}
+
+// getParentsPrefixToRemoveAndParentsToSkip gets from the pattern the prefix before the "pivot point",
+// the location in the source path marked by the path component named "."
+// (i.e. where "/./" occurs in the path). And list of parents to skip.
+// In case "/./" is not present is returned "/".
+func getParentsPrefixToRemoveAndParentsToSkip(pattern string, contextDir string) (string, []string) {
+ prefix, _, found := strings.Cut(strings.TrimPrefix(pattern, contextDir), "/./")
+ if !found {
+ return string(filepath.Separator), []string{}
+ }
+ prefix = strings.TrimPrefix(filepath.Clean(string(filepath.Separator)+prefix), string(filepath.Separator))
+ out := []string{}
+ parentPath := prefix
+ for parentPath != "/" && parentPath != "." {
+ out = append(out, parentPath)
+ parentPath = filepath.Dir(parentPath)
+ }
+ return prefix, out
+}
+
+// Add copies the contents of the specified sources into the container's root
+// filesystem, optionally extracting contents of local files that look like
+// non-empty archives.
+func (b *Builder) Add(destination string, extract bool, options AddAndCopyOptions, sources ...string) error {
+ mountPoint, err := b.Mount(b.MountLabel)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err2 := b.Unmount(); err2 != nil {
+ logrus.Errorf("error unmounting container: %v", err2)
+ }
+ }()
+
+ contextDir := options.ContextDir
+ currentDir := options.ContextDir
+ if options.ContextDir == "" {
+ contextDir = string(os.PathSeparator)
+ currentDir, err = os.Getwd()
+ if err != nil {
+ return fmt.Errorf("determining current working directory: %w", err)
+ }
+ } else {
+ if !filepath.IsAbs(options.ContextDir) {
+ contextDir, err = filepath.Abs(options.ContextDir)
+ if err != nil {
+ return fmt.Errorf("converting context directory path %q to an absolute path: %w", options.ContextDir, err)
+ }
+ }
+ }
+
+ // Figure out what sorts of sources we have.
+ var localSources, remoteSources, gitSources []string
+ for i, src := range sources {
+ if src == "" {
+ return errors.New("empty source location")
+ }
+ if sourceIsRemote(src) {
+ remoteSources = append(remoteSources, src)
+ continue
+ }
+ if sourceIsGit(src) {
+ gitSources = append(gitSources, src)
+ continue
+ }
+ if !filepath.IsAbs(src) && options.ContextDir == "" {
+ sources[i] = filepath.Join(currentDir, src)
+ }
+ localSources = append(localSources, sources[i])
+ }
+
+ // Treat git sources as a subset of remote sources
+ // differentiating only in how we fetch the two later on.
+ if len(gitSources) > 0 {
+ remoteSources = append(remoteSources, gitSources...)
+ }
+
+ // Check how many items our local source specs matched. Each spec
+ // should have matched at least one item, otherwise we consider it an
+ // error.
+ var localSourceStats []*copier.StatsForGlob
+ if len(localSources) > 0 {
+ statOptions := copier.StatOptions{
+ CheckForArchives: extract,
+ }
+ localSourceStats, err = copier.Stat(contextDir, contextDir, statOptions, localSources)
+ if err != nil {
+ return fmt.Errorf("checking on sources under %q: %w", contextDir, err)
+ }
+ }
+ numLocalSourceItems := 0
+ for _, localSourceStat := range localSourceStats {
+ if localSourceStat.Error != "" {
+ errorText := localSourceStat.Error
+ rel, err := filepath.Rel(contextDir, localSourceStat.Glob)
+ if err != nil {
+ errorText = fmt.Sprintf("%v; %s", err, errorText)
+ }
+ if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
+ errorText = fmt.Sprintf("possible escaping context directory error: %s", errorText)
+ }
+ return fmt.Errorf("checking on sources under %q: %v", contextDir, errorText)
+ }
+ if len(localSourceStat.Globbed) == 0 {
+ return fmt.Errorf("checking source under %q: no glob matches: %w", contextDir, syscall.ENOENT)
+ }
+ numLocalSourceItems += len(localSourceStat.Globbed)
+ }
+ if numLocalSourceItems+len(remoteSources)+len(gitSources) == 0 {
+ return fmt.Errorf("no sources %v found: %w", sources, syscall.ENOENT)
+ }
+
+ // Find out which user (and group) the destination should belong to.
+ var chownDirs, chownFiles *idtools.IDPair
+ var userUID, userGID uint32
+ if options.Chown != "" {
+ userUID, userGID, err = b.userForCopy(mountPoint, options.Chown)
+ if err != nil {
+ return fmt.Errorf("looking up UID/GID for %q: %w", options.Chown, err)
+ }
+ }
+ var chmodDirsFiles *os.FileMode
+ if options.Chmod != "" {
+ p, err := strconv.ParseUint(options.Chmod, 8, 32)
+ if err != nil {
+ return fmt.Errorf("parsing chmod %q: %w", options.Chmod, err)
+ }
+ perm := os.FileMode(p)
+ chmodDirsFiles = &perm
+ }
+
+ chownDirs = &idtools.IDPair{UID: int(userUID), GID: int(userGID)}
+ chownFiles = &idtools.IDPair{UID: int(userUID), GID: int(userGID)}
+ if options.Chown == "" && options.PreserveOwnership {
+ chownDirs = nil
+ chownFiles = nil
+ }
+
+ // If we have a single source archive to extract, or more than one
+ // source item, or the destination has a path separator at the end of
+ // it, and it's not a remote URL, the destination needs to be a
+ // directory.
+ destMustBeDirectory := strings.HasSuffix(destination, string(os.PathSeparator)) || strings.HasSuffix(destination, string(os.PathSeparator)+".") // keep this in sync with github.com/openshift/imagebuilder.hasSlash()
+ destMustBeDirectory = destMustBeDirectory || destination == "" || (len(sources) > 1)
+ if destination == "" || !filepath.IsAbs(destination) {
+ tmpDestination := filepath.Join(string(os.PathSeparator)+b.WorkDir(), destination)
+ if destMustBeDirectory {
+ destination = tmpDestination + string(os.PathSeparator)
+ } else {
+ destination = tmpDestination
+ }
+ }
+ destMustBeDirectory = destMustBeDirectory || (filepath.Clean(destination) == filepath.Clean(b.WorkDir()))
+ destCanBeFile := false
+ if len(sources) == 1 {
+ if len(remoteSources) == 1 {
+ destCanBeFile = sourceIsRemote(sources[0])
+ }
+ if len(localSources) == 1 {
+ item := localSourceStats[0].Results[localSourceStats[0].Globbed[0]]
+ if item.IsDir || (item.IsArchive && extract) {
+ destMustBeDirectory = true
+ }
+ if item.IsRegular {
+ destCanBeFile = true
+ }
+ }
+ if len(gitSources) > 0 {
+ destMustBeDirectory = true
+ }
+ }
+
+ // We care if the destination either doesn't exist, or exists and is a
+ // file. If the source can be a single file, for those cases we treat
+ // the destination as a file rather than as a directory tree.
+ renameTarget := ""
+ extractDirectory := filepath.Join(mountPoint, destination)
+ statOptions := copier.StatOptions{
+ CheckForArchives: extract,
+ }
+ destStats, err := copier.Stat(mountPoint, filepath.Join(mountPoint, b.WorkDir()), statOptions, []string{extractDirectory})
+ if err != nil {
+ return fmt.Errorf("checking on destination %v: %w", extractDirectory, err)
+ }
+ if (len(destStats) == 0 || len(destStats[0].Globbed) == 0) && !destMustBeDirectory && destCanBeFile {
+ // destination doesn't exist - extract to parent and rename the incoming file to the destination's name
+ renameTarget = filepath.Base(extractDirectory)
+ extractDirectory = filepath.Dir(extractDirectory)
+ }
+
+ // if the destination is a directory that doesn't yet exist, let's copy it.
+ newDestDirFound := (len(destStats) == 1 || len(destStats[0].Globbed) == 0) && destMustBeDirectory && !destCanBeFile
+
+ if len(destStats) == 1 && len(destStats[0].Globbed) == 1 && destStats[0].Results[destStats[0].Globbed[0]].IsRegular {
+ if destMustBeDirectory {
+ return fmt.Errorf("destination %v already exists but is not a directory", destination)
+ }
+ // destination exists - it's a file, we need to extract to parent and rename the incoming file to the destination's name
+ renameTarget = filepath.Base(extractDirectory)
+ extractDirectory = filepath.Dir(extractDirectory)
+ }
+
+ pm, err := fileutils.NewPatternMatcher(options.Excludes)
+ if err != nil {
+ return fmt.Errorf("processing excludes list %v: %w", options.Excludes, err)
+ }
+
+ // Make sure that, if it's a symlink, we'll chroot to the target of the link;
+ // knowing that target requires that we resolve it within the chroot.
+ evalOptions := copier.EvalOptions{}
+ evaluated, err := copier.Eval(mountPoint, extractDirectory, evalOptions)
+ if err != nil {
+ return fmt.Errorf("checking on destination %v: %w", extractDirectory, err)
+ }
+ extractDirectory = evaluated
+
+ // Set up ID maps.
+ var srcUIDMap, srcGIDMap []idtools.IDMap
+ if options.IDMappingOptions != nil {
+ srcUIDMap, srcGIDMap = convertRuntimeIDMaps(options.IDMappingOptions.UIDMap, options.IDMappingOptions.GIDMap)
+ }
+ destUIDMap, destGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)
+
+ var putRoot, putDir, stagingDir string
+ var createdDirs []string
+ var latestTimestamp time.Time
+
+ mkdirOptions := copier.MkdirOptions{
+ UIDMap: destUIDMap,
+ GIDMap: destGIDMap,
+ ChownNew: chownDirs,
+ }
+
+ // If --link is specified, we create a staging directory to hold the content
+ // that will then become an independent layer
+ if options.Link {
+ containerDir, err := b.store.ContainerDirectory(b.ContainerID)
+ if err != nil {
+ return fmt.Errorf("getting container directory for %q: %w", b.ContainerID, err)
+ }
+
+ stagingDir, err = os.MkdirTemp(containerDir, "link-stage-")
+ if err != nil {
+ return fmt.Errorf("creating staging directory for link %q: %w", b.ContainerID, err)
+ }
+
+ putRoot = stagingDir
+
+ cleanDest := filepath.Clean(destination)
+
+ if strings.Contains(cleanDest, "..") {
+ return fmt.Errorf("invalid destination path %q: contains path traversal", destination)
+ }
+
+ if renameTarget != "" {
+ putDir = filepath.Dir(filepath.Join(stagingDir, cleanDest))
+ } else {
+ putDir = filepath.Join(stagingDir, cleanDest)
+ }
+
+ putDirAbs, err := filepath.Abs(putDir)
+ if err != nil {
+ return fmt.Errorf("failed to resolve absolute path: %w", err)
+ }
+
+ stagingDirAbs, err := filepath.Abs(stagingDir)
+ if err != nil {
+ return fmt.Errorf("failed to resolve staging directory absolute path: %w", err)
+ }
+
+ if !strings.HasPrefix(putDirAbs, stagingDirAbs+string(os.PathSeparator)) && putDirAbs != stagingDirAbs {
+ return fmt.Errorf("destination path %q escapes staging directory", destination)
+ }
+ if err := copier.Mkdir(putRoot, putDirAbs, mkdirOptions); err != nil {
+ return fmt.Errorf("ensuring target directory exists: %w", err)
+ }
+ tempPath := putDir
+ for tempPath != stagingDir && tempPath != filepath.Dir(tempPath) {
+ if _, err := os.Stat(tempPath); err == nil {
+ createdDirs = append(createdDirs, tempPath)
+ }
+ tempPath = filepath.Dir(tempPath)
+ }
+ } else {
+ if err := copier.Mkdir(mountPoint, extractDirectory, mkdirOptions); err != nil {
+ return fmt.Errorf("ensuring target directory exists: %w", err)
+ }
+
+ putRoot = extractDirectory
+ putDir = extractDirectory
+ }
+
+ // Copy each source in turn.
+ for _, src := range sources {
+ var multiErr *multierror.Error
+ var getErr, closeErr, renameErr, putErr error
+ var wg sync.WaitGroup
+ if sourceIsRemote(src) || sourceIsGit(src) {
+ pipeReader, pipeWriter := io.Pipe()
+ var srcDigest digest.Digest
+ if options.Checksum != "" {
+ srcDigest, err = digest.Parse(options.Checksum)
+ if err != nil {
+ return fmt.Errorf("invalid checksum flag: %w", err)
+ }
+ }
+
+ wg.Add(1)
+ if sourceIsGit(src) {
+ go func() {
+ defer wg.Done()
+ defer pipeWriter.Close()
+ var cloneDir, subdir string
+ cloneDir, subdir, getErr = define.TempDirForURL(tmpdir.GetTempDir(), "", src)
+ if getErr != nil {
+ return
+ }
+ getOptions := copier.GetOptions{
+ UIDMap: srcUIDMap,
+ GIDMap: srcGIDMap,
+ Excludes: options.Excludes,
+ ExpandArchives: extract,
+ ChownDirs: chownDirs,
+ ChmodDirs: chmodDirsFiles,
+ ChownFiles: chownFiles,
+ ChmodFiles: chmodDirsFiles,
+ StripSetuidBit: options.StripSetuidBit,
+ StripSetgidBit: options.StripSetgidBit,
+ StripStickyBit: options.StripStickyBit,
+ Timestamp: options.Timestamp,
+ }
+ writer := io.WriteCloser(pipeWriter)
+ repositoryDir := filepath.Join(cloneDir, subdir)
+ getErr = copier.Get(repositoryDir, repositoryDir, getOptions, []string{"."}, writer)
+ }()
+ } else {
+ go func() {
+ getErr = retry.IfNecessary(context.TODO(), func() error {
+ return getURL(src, chownFiles, mountPoint, renameTarget, pipeWriter, chmodDirsFiles, srcDigest, options.CertPath, options.InsecureSkipTLSVerify, options.Timestamp)
+ }, &retry.Options{
+ MaxRetry: options.MaxRetries,
+ Delay: options.RetryDelay,
+ })
+ pipeWriter.Close()
+ wg.Done()
+ }()
+ }
+
+ wg.Add(1)
+ go func() {
+ b.ContentDigester.Start("")
+ hashCloser := b.ContentDigester.Hash()
+ hasher := io.Writer(hashCloser)
+ if options.Hasher != nil {
+ hasher = io.MultiWriter(hasher, options.Hasher)
+ }
+ if options.DryRun {
+ _, putErr = io.Copy(hasher, pipeReader)
+ } else {
+ putOptions := copier.PutOptions{
+ UIDMap: destUIDMap,
+ GIDMap: destGIDMap,
+ ChownDirs: nil,
+ ChmodDirs: nil,
+ ChownFiles: nil,
+ ChmodFiles: nil,
+ IgnoreDevices: userns.RunningInUserNS(),
+ }
+ putErr = copier.Put(putRoot, putDir, putOptions, io.TeeReader(pipeReader, hasher))
+ }
+ hashCloser.Close()
+ pipeReader.Close()
+ wg.Done()
+ }()
+ wg.Wait()
+ if getErr != nil {
+ getErr = fmt.Errorf("reading %q: %w", src, getErr)
+ }
+ if putErr != nil {
+ putErr = fmt.Errorf("storing %q: %w", src, putErr)
+ }
+ multiErr = multierror.Append(getErr, putErr)
+ if multiErr != nil && multiErr.ErrorOrNil() != nil {
+ if len(multiErr.Errors) > 1 {
+ return multiErr.ErrorOrNil()
+ }
+ return multiErr.Errors[0]
+ }
+ continue
+ }
+
+ if options.Checksum != "" {
+ return fmt.Errorf("checksum flag is not supported for local sources")
+ }
+
+ // Dig out the result of running glob+stat on this source spec.
+ var localSourceStat *copier.StatsForGlob
+ for _, st := range localSourceStats {
+ if st.Glob == src {
+ localSourceStat = st
+ break
+ }
+ }
+ if localSourceStat == nil {
+ continue
+ }
+ // Iterate through every item that matched the glob.
+ itemsCopied := 0
+ for _, globbed := range localSourceStat.Globbed {
+ rel := globbed
+ if filepath.IsAbs(globbed) {
+ if rel, err = filepath.Rel(contextDir, globbed); err != nil {
+ return fmt.Errorf("computing path of %q relative to %q: %w", globbed, contextDir, err)
+ }
+ }
+ if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
+ return fmt.Errorf("possible escaping context directory error: %q is outside of %q", globbed, contextDir)
+ }
+ // Check for dockerignore-style exclusion of this item.
+ if rel != "." {
+ excluded, err := pm.Matches(filepath.ToSlash(rel)) //nolint:staticcheck
+ if err != nil {
+ return fmt.Errorf("checking if %q(%q) is excluded: %w", globbed, rel, err)
+ }
+ if excluded {
+ // non-directories that are excluded are excluded, no question, but
+ // directories can only be skipped if we don't have to allow for the
+ // possibility of finding things to include under them
+ globInfo := localSourceStat.Results[globbed]
+ if !globInfo.IsDir || !includeDirectoryAnyway(rel, pm) {
+ continue
+ }
+ } else {
+ // if the destination is a directory that doesn't yet exist, and is not excluded, let's copy it.
+ if newDestDirFound {
+ itemsCopied++
+ }
+ }
+ } else {
+ // Make sure we don't trigger a "copied nothing" error for an empty context
+ // directory if we were told to copy the context directory itself. We won't
+ // actually copy it, but we need to make sure that we don't produce an error
+ // due to potentially not having anything in the tarstream that we passed.
+ itemsCopied++
+ }
+ st := localSourceStat.Results[globbed]
+ if options.Link && st.ModTime.After(latestTimestamp) {
+ latestTimestamp = st.ModTime
+ }
+ pipeReader, pipeWriter := io.Pipe()
+ wg.Add(1)
+ go func() {
+ renamedItems := 0
+ writer := io.WriteCloser(pipeWriter)
+ if renameTarget != "" {
+ writer = newTarFilterer(writer, func(hdr *tar.Header) (bool, bool, io.Reader) {
+ hdr.Name = renameTarget
+ renamedItems++
+ return false, false, nil
+ })
+ }
+
+ if options.Parents {
+ parentsPrefixToRemove, parentsToSkip := getParentsPrefixToRemoveAndParentsToSkip(src, options.ContextDir)
+ writer = newTarFilterer(writer, func(hdr *tar.Header) (bool, bool, io.Reader) {
+ if slices.Contains(parentsToSkip, hdr.Name) && hdr.Typeflag == tar.TypeDir {
+ return true, false, nil
+ }
+ hdr.Name = strings.TrimPrefix(hdr.Name, parentsPrefixToRemove)
+ hdr.Name = strings.TrimPrefix(hdr.Name, "/")
+ if hdr.Typeflag == tar.TypeLink {
+ hdr.Linkname = strings.TrimPrefix(hdr.Linkname, parentsPrefixToRemove)
+ hdr.Linkname = strings.TrimPrefix(hdr.Linkname, "/")
+ }
+ if hdr.Name == "" {
+ return true, false, nil
+ }
+ return false, false, nil
+ })
+ }
+ writer = newTarFilterer(writer, func(_ *tar.Header) (bool, bool, io.Reader) {
+ itemsCopied++
+ return false, false, nil
+ })
+ getOptions := copier.GetOptions{
+ UIDMap: srcUIDMap,
+ GIDMap: srcGIDMap,
+ Excludes: options.Excludes,
+ ExpandArchives: extract,
+ ChownDirs: chownDirs,
+ ChmodDirs: chmodDirsFiles,
+ ChownFiles: chownFiles,
+ ChmodFiles: chmodDirsFiles,
+ StripSetuidBit: options.StripSetuidBit,
+ StripSetgidBit: options.StripSetgidBit,
+ StripStickyBit: options.StripStickyBit,
+ Parents: options.Parents,
+ Timestamp: options.Timestamp,
+ }
+ getErr = copier.Get(contextDir, contextDir, getOptions, []string{globbedToGlobbable(globbed)}, writer)
+ closeErr = writer.Close()
+ if renameTarget != "" && renamedItems > 1 {
+ renameErr = fmt.Errorf("internal error: renamed %d items when we expected to only rename 1", renamedItems)
+ }
+ wg.Done()
+ }()
+ wg.Add(1)
+ go func() {
+ if st.IsDir {
+ b.ContentDigester.Start("dir")
+ } else {
+ b.ContentDigester.Start("file")
+ }
+ hashCloser := b.ContentDigester.Hash()
+ hasher := io.Writer(hashCloser)
+ if options.Hasher != nil {
+ hasher = io.MultiWriter(hasher, options.Hasher)
+ }
+ if options.DryRun {
+ _, putErr = io.Copy(hasher, pipeReader)
+ } else {
+ putOptions := copier.PutOptions{
+ UIDMap: destUIDMap,
+ GIDMap: destGIDMap,
+ DefaultDirOwner: chownDirs,
+ DefaultDirMode: nil,
+ ChownDirs: nil,
+ ChmodDirs: nil,
+ ChownFiles: nil,
+ ChmodFiles: nil,
+ IgnoreDevices: userns.RunningInUserNS(),
+ }
+ putErr = copier.Put(putRoot, putDir, putOptions, io.TeeReader(pipeReader, hasher))
+ }
+ hashCloser.Close()
+ pipeReader.Close()
+ wg.Done()
+ }()
+
+ wg.Wait()
+ if getErr != nil {
+ getErr = fmt.Errorf("reading %q: %w", src, getErr)
+ }
+ if closeErr != nil {
+ closeErr = fmt.Errorf("closing %q: %w", src, closeErr)
+ }
+ if renameErr != nil {
+ renameErr = fmt.Errorf("renaming %q: %w", src, renameErr)
+ }
+ if putErr != nil {
+ putErr = fmt.Errorf("storing %q: %w", src, putErr)
+ }
+ multiErr = multierror.Append(getErr, closeErr, renameErr, putErr)
+ if multiErr != nil && multiErr.ErrorOrNil() != nil {
+ if len(multiErr.Errors) > 1 {
+ return multiErr.ErrorOrNil()
+ }
+ return multiErr.Errors[0]
+ }
+ }
+ if itemsCopied == 0 {
+ excludesFile := ""
+ if options.IgnoreFile != "" {
+ excludesFile = " using " + options.IgnoreFile
+ }
+ return fmt.Errorf("no items matching glob %q copied (%d filtered out%s): %w", localSourceStat.Glob, len(localSourceStat.Globbed), excludesFile, syscall.ENOENT)
+ }
+ }
+
+ if options.Link {
+ if !latestTimestamp.IsZero() {
+ for _, dir := range createdDirs {
+ if err := os.Chtimes(dir, latestTimestamp, latestTimestamp); err != nil {
+ logrus.Warnf("failed to set timestamp on directory %q: %v", dir, err)
+ }
+ }
+ }
+ var created time.Time
+ if options.Timestamp != nil {
+ created = *options.Timestamp
+ } else if !latestTimestamp.IsZero() {
+ created = latestTimestamp
+ } else {
+ created = time.Unix(0, 0).UTC()
+ }
+
+ command := "ADD"
+ if !extract {
+ command = "COPY"
+ }
+
+ contentType, digest := b.ContentDigester.Digest()
+ summary := contentType
+ if digest != "" {
+ if summary != "" {
+ summary = summary + ":"
+ }
+ summary = summary + digest.Encoded()
+ logrus.Debugf("added content from --link %s", summary)
+ }
+
+ createdBy := "/bin/sh -c #(nop) " + command + " --link " + summary + " in " + destination + " " + options.BuildMetadata
+ history := v1.History{
+ Created: &created,
+ CreatedBy: createdBy,
+ Comment: b.HistoryComment(),
+ }
+
+ linkedLayer := LinkedLayer{
+ History: history,
+ BlobPath: stagingDir,
+ }
+
+ b.AppendedLinkedLayers = append(b.AppendedLinkedLayers, linkedLayer)
+
+ if err := b.Save(); err != nil {
+ return fmt.Errorf("saving builder state after queuing linked layer: %w", err)
+ }
+ }
+
+ return nil
+}
+
+// userForRun returns the user (and group) information which we should use for
+// running commands
+func (b *Builder) userForRun(mountPoint string, userspec string) (specs.User, string, error) {
+ if userspec == "" {
+ userspec = b.User()
+ }
+
+ uid, gid, homeDir, err := chrootuser.GetUser(mountPoint, userspec)
+ u := specs.User{
+ UID: uid,
+ GID: gid,
+ Username: userspec,
+ }
+ if !strings.Contains(userspec, ":") {
+ groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID))
+ if err2 != nil {
+ if !errors.Is(err2, chrootuser.ErrNoSuchUser) && err == nil {
+ err = err2
+ }
+ } else {
+ u.AdditionalGids = groups
+ }
+ }
+ return u, homeDir, err
+}
+
+// userForCopy returns the user (and group) information which we should use for
+// setting ownership of contents being copied. It's just like what
+// userForRun() does, except for the case where we're passed a single numeric
+// value, where we need to use that value for both the UID and the GID.
+func (b *Builder) userForCopy(mountPoint string, userspec string) (uint32, uint32, error) {
+ var (
+ user, group string
+ uid, gid uint64
+ err error
+ )
+
+ split := strings.SplitN(userspec, ":", 2)
+ user = split[0]
+ if len(split) > 1 {
+ group = split[1]
+ }
+
+ // If userspec did not specify any values for user or group, then fail
+ if user == "" && group == "" {
+ return 0, 0, fmt.Errorf("can't find uid for user %s", userspec)
+ }
+
+ // If userspec specifies values for user or group, check for numeric values
+ // and return early. If not, then translate username/groupname
+ if user != "" {
+ uid, err = strconv.ParseUint(user, 10, 32)
+ }
+ if err == nil {
+ // default gid to uid
+ gid = uid
+ if group != "" {
+ gid, err = strconv.ParseUint(group, 10, 32)
+ }
+ }
+ // If err != nil, then user or group not numeric, check filesystem
+ if err == nil {
+ return uint32(uid), uint32(gid), nil
+ }
+
+ owner, _, err := b.userForRun(mountPoint, userspec)
+ if err != nil {
+ return 0xffffffff, 0xffffffff, err
+ }
+ return owner.UID, owner.GID, nil
+}
+
+// EnsureContainerPathAs creates the specified directory if it doesn't exist,
+// setting a newly-created directory's owner to USER and its permissions to MODE.
+func (b *Builder) EnsureContainerPathAs(path, user string, mode *os.FileMode) error {
+ mountPoint, err := b.Mount(b.MountLabel)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err2 := b.Unmount(); err2 != nil {
+ logrus.Errorf("error unmounting container: %v", err2)
+ }
+ }()
+
+ uid, gid := uint32(0), uint32(0)
+ if user != "" {
+ if uidForCopy, gidForCopy, err := b.userForCopy(mountPoint, user); err == nil {
+ uid = uidForCopy
+ gid = gidForCopy
+ }
+ }
+
+ destUIDMap, destGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)
+
+ idPair := &idtools.IDPair{UID: int(uid), GID: int(gid)}
+ opts := copier.MkdirOptions{
+ ChmodNew: mode,
+ ChownNew: idPair,
+ UIDMap: destUIDMap,
+ GIDMap: destGIDMap,
+ }
+ return copier.Mkdir(mountPoint, filepath.Join(mountPoint, path), opts)
+}
diff --git a/vendor/github.com/containers/buildah/bind/mount.go b/vendor/github.com/containers/buildah/bind/mount.go
new file mode 100644
index 00000000..5153dfc5
--- /dev/null
+++ b/vendor/github.com/containers/buildah/bind/mount.go
@@ -0,0 +1,303 @@
+//go:build linux
+
+package bind
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "slices"
+ "syscall"
+
+ "github.com/containers/buildah/util"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/sirupsen/logrus"
+ "go.podman.io/storage/pkg/idtools"
+ "go.podman.io/storage/pkg/mount"
+ "golang.org/x/sys/unix"
+)
+
+// SetupIntermediateMountNamespace creates a new mount namespace and bind
+// mounts all bind-mount sources into a subdirectory of bundlePath that can
+// only be reached by the root user of the container's user namespace, except
+// for Mounts which include the NoBindOption option in their options list. The
+// NoBindOption will then merely be removed.
+func SetupIntermediateMountNamespace(spec *specs.Spec, bundlePath string) (unmountAll func() error, err error) {
+ defer stripNoBindOption(spec)
+
+ // We expect a root directory to be defined.
+ if spec.Root == nil {
+ return nil, errors.New("configuration has no root filesystem?")
+ }
+ rootPath := spec.Root.Path
+
+ // Create a new mount namespace in which to do the things we're doing.
+ if err := unix.Unshare(unix.CLONE_NEWNS); err != nil {
+ return nil, fmt.Errorf("creating new mount namespace for %v: %w", spec.Process.Args, err)
+ }
+
+ // Make all of our mounts private to our namespace.
+ if err := mount.MakeRPrivate("/"); err != nil {
+ return nil, fmt.Errorf("making mounts private to mount namespace for %v: %w", spec.Process.Args, err)
+ }
+
+ // Make sure the bundle directory is searchable. We created it with
+ // TempDir(), so it should have started with permissions set to 0700.
+ info, err := os.Stat(bundlePath)
+ if err != nil {
+ return nil, fmt.Errorf("checking permissions on %q: %w", bundlePath, err)
+ }
+ if err = os.Chmod(bundlePath, info.Mode()|0o111); err != nil {
+ return nil, fmt.Errorf("loosening permissions on %q: %w", bundlePath, err)
+ }
+
+ // Figure out who needs to be able to reach these bind mounts in order
+ // for the container to be started.
+ rootUID, rootGID, err := util.GetHostRootIDs(spec)
+ if err != nil {
+ return nil, err
+ }
+
+ // Hand back a callback that the caller can use to clean up everything
+ // we're doing here.
+ unmount := []string{}
+ unmountAll = func() (err error) {
+ for _, mountpoint := range unmount {
+ // Unmount it and anything under it.
+ if err2 := UnmountMountpoints(mountpoint, nil); err2 != nil {
+ logrus.Warnf("pkg/bind: error unmounting %q: %v", mountpoint, err2)
+ if err == nil {
+ err = err2
+ }
+ }
+ if err2 := unix.Unmount(mountpoint, unix.MNT_DETACH); err2 != nil {
+ if errno, ok := err2.(syscall.Errno); !ok || errno != syscall.EINVAL {
+ logrus.Warnf("pkg/bind: error detaching %q: %v", mountpoint, err2)
+ if err == nil {
+ err = err2
+ }
+ }
+ }
+ // Remove just the mountpoint.
+ retry := 10
+ remove := unix.Unlink
+ err2 := remove(mountpoint)
+ for err2 != nil && retry > 0 {
+ if errno, ok := err2.(syscall.Errno); ok {
+ switch errno {
+ default:
+ retry = 0
+ continue
+ case syscall.EISDIR:
+ remove = unix.Rmdir
+ err2 = remove(mountpoint)
+ case syscall.EBUSY:
+ if err3 := unix.Unmount(mountpoint, unix.MNT_DETACH); err3 == nil {
+ err2 = remove(mountpoint)
+ }
+ }
+ retry--
+ }
+ }
+ if err2 != nil {
+ logrus.Warnf("pkg/bind: error removing %q: %v", mountpoint, err2)
+ if err == nil {
+ err = err2
+ }
+ }
+ }
+ return err
+ }
+
+ // Create a top-level directory that the "root" user will be able to
+ // access, that "root" from containers which use different mappings, or
+ // other unprivileged users outside of containers, shouldn't be able to
+ // access.
+ mnt := filepath.Join(bundlePath, "mnt")
+ if err = idtools.MkdirAndChown(mnt, 0o100, idtools.IDPair{UID: int(rootUID), GID: int(rootGID)}); err != nil {
+ return unmountAll, fmt.Errorf("creating %q owned by the container's root user: %w", mnt, err)
+ }
+
+ // Make that directory private, and add it to the list of locations we
+ // unmount at cleanup time.
+ if err = mount.MakeRPrivate(mnt); err != nil {
+ return unmountAll, fmt.Errorf("marking filesystem at %q as private: %w", mnt, err)
+ }
+ unmount = append([]string{mnt}, unmount...)
+
+ // Create a bind mount for the root filesystem and add it to the list.
+ rootfs := filepath.Join(mnt, "rootfs")
+ if err = os.Mkdir(rootfs, 0o000); err != nil {
+ return unmountAll, fmt.Errorf("creating directory %q: %w", rootfs, err)
+ }
+ if err = unix.Mount(rootPath, rootfs, "", unix.MS_BIND|unix.MS_REC|unix.MS_PRIVATE, ""); err != nil {
+ return unmountAll, fmt.Errorf("bind mounting root filesystem from %q to %q: %w", rootPath, rootfs, err)
+ }
+ logrus.Debugf("bind mounted %q to %q", rootPath, rootfs)
+ unmount = append([]string{rootfs}, unmount...)
+ spec.Root.Path = rootfs
+
+ // Do the same for everything we're binding in.
+ mounts := make([]specs.Mount, 0, len(spec.Mounts))
+ for i := range spec.Mounts {
+ // If we're not using an intermediate, leave it in the list.
+ if leaveBindMountAlone(spec.Mounts[i]) {
+ mounts = append(mounts, spec.Mounts[i])
+ continue
+ }
+ // Check if the source is a directory or something else.
+ info, err := os.Stat(spec.Mounts[i].Source)
+ if err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ logrus.Warnf("couldn't find %q on host to bind mount into container", spec.Mounts[i].Source)
+ continue
+ }
+ return unmountAll, fmt.Errorf("checking if %q is a directory: %w", spec.Mounts[i].Source, err)
+ }
+ stage := filepath.Join(mnt, fmt.Sprintf("buildah-bind-target-%d", i))
+ if info.IsDir() {
+ // If the source is a directory, make one to use as the
+ // mount target.
+ if err = os.Mkdir(stage, 0o000); err != nil {
+ return unmountAll, fmt.Errorf("creating directory %q: %w", stage, err)
+ }
+ } else {
+ // If the source is not a directory, create an empty
+ // file to use as the mount target.
+ file, err := os.OpenFile(stage, os.O_WRONLY|os.O_CREATE, 0o000)
+ if err != nil {
+ return unmountAll, fmt.Errorf("creating file %q: %w", stage, err)
+ }
+ file.Close()
+ }
+ // Bind mount the source from wherever it is to a place where
+ // we know the runtime helper will be able to get to it...
+ if err = unix.Mount(spec.Mounts[i].Source, stage, "", unix.MS_BIND|unix.MS_REC|unix.MS_PRIVATE, ""); err != nil {
+ return unmountAll, fmt.Errorf("bind mounting bind object from %q to %q: %w", spec.Mounts[i].Source, stage, err)
+ }
+ logrus.Debugf("bind mounted %q to %q", spec.Mounts[i].Source, stage)
+ spec.Mounts[i].Source = stage
+ // ... and update the source location that we'll pass to the
+ // runtime to our intermediate location.
+ mounts = append(mounts, spec.Mounts[i])
+ unmount = append([]string{stage}, unmount...)
+ }
+ spec.Mounts = mounts
+
+ return unmountAll, nil
+}
+
+// Decide if the mount should not be redirected to an intermediate location first.
+func leaveBindMountAlone(mount specs.Mount) bool {
+ // If we know we shouldn't do a redirection for this mount, skip it.
+ if slices.Contains(mount.Options, NoBindOption) {
+ return true
+ }
+ // If we're not bind mounting it in, we don't need to do anything for it.
+ if mount.Type != "bind" && !slices.Contains(mount.Options, "bind") && !slices.Contains(mount.Options, "rbind") {
+ return true
+ }
+ return false
+}
+
+// UnmountMountpoints unmounts the given mountpoints and anything that's hanging
+// off of them, rather aggressively. If a mountpoint also appears in the
+// mountpointsToRemove slice, the mountpoints are removed after they are
+// unmounted.
+func UnmountMountpoints(mountpoint string, mountpointsToRemove []string) error {
+ mounts, err := mount.GetMounts()
+ if err != nil {
+ return fmt.Errorf("retrieving list of mounts: %w", err)
+ }
+ // getChildren returns the list of mount IDs that hang off of the
+ // specified ID.
+ getChildren := func(id int) []int {
+ var list []int
+ for _, info := range mounts {
+ if info.Parent == id {
+ list = append(list, info.ID)
+ }
+ }
+ return list
+ }
+ // getTree returns the list of mount IDs that hang off of the specified
+ // ID, and off of those mount IDs, etc.
+ getTree := func(id int) []int {
+ mounts := []int{id}
+ i := 0
+ for i < len(mounts) {
+ children := getChildren(mounts[i])
+ mounts = append(mounts, children...)
+ i++
+ }
+ return mounts
+ }
+ // getMountByID looks up the mount info with the specified ID
+ getMountByID := func(id int) *mount.Info {
+ for i := range mounts {
+ if mounts[i].ID == id {
+ return mounts[i]
+ }
+ }
+ return nil
+ }
+ // getMountByPoint looks up the mount info with the specified mountpoint
+ getMountByPoint := func(mountpoint string) *mount.Info {
+ for i := range mounts {
+ if mounts[i].Mountpoint == mountpoint {
+ return mounts[i]
+ }
+ }
+ return nil
+ }
+ // find the top of the tree we're unmounting
+ top := getMountByPoint(mountpoint)
+ if top == nil {
+ if err != nil {
+ return fmt.Errorf("%q is not mounted: %w", mountpoint, err)
+ }
+ return nil
+ }
+ // add all of the mounts that are hanging off of it
+ tree := getTree(top.ID)
+ // unmount each mountpoint, working from the end of the list (leaf nodes) to the top
+ for i := range tree {
+ var st unix.Stat_t
+ id := tree[len(tree)-i-1]
+ mount := getMountByID(id)
+ // check if this mountpoint is mounted
+ if err := unix.Lstat(mount.Mountpoint, &st); err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ logrus.Debugf("mountpoint %q is not present(?), skipping", mount.Mountpoint)
+ continue
+ }
+ return fmt.Errorf("checking if %q is mounted: %w", mount.Mountpoint, err)
+ }
+ if uint64(mount.Major) != uint64(st.Dev) || uint64(mount.Minor) != uint64(st.Dev) { //nolint:unconvert // (required for some OS/arch combinations)
+ logrus.Debugf("%q is apparently not really mounted, skipping", mount.Mountpoint)
+ continue
+ }
+ // do the unmount
+ if err := unix.Unmount(mount.Mountpoint, 0); err != nil {
+ // if it was busy, detach it
+ if errno, ok := err.(syscall.Errno); ok && errno == syscall.EBUSY {
+ err = unix.Unmount(mount.Mountpoint, unix.MNT_DETACH)
+ }
+ if err != nil {
+ // if it was invalid (not mounted), hide the error, else return it
+ if errno, ok := err.(syscall.Errno); !ok || errno != syscall.EINVAL {
+ logrus.Warnf("error unmounting %q: %v", mount.Mountpoint, err)
+ continue
+ }
+ }
+ }
+ // if we're also supposed to remove this thing, do that, too
+ if slices.Contains(mountpointsToRemove, mount.Mountpoint) {
+ if err := os.Remove(mount.Mountpoint); err != nil {
+ return fmt.Errorf("removing %q: %w", mount.Mountpoint, err)
+ }
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/containers/buildah/bind/mount_unsupported.go b/vendor/github.com/containers/buildah/bind/mount_unsupported.go
new file mode 100644
index 00000000..11cee0a4
--- /dev/null
+++ b/vendor/github.com/containers/buildah/bind/mount_unsupported.go
@@ -0,0 +1,13 @@
+//go:build !linux
+
+package bind
+
+import (
+ "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+// SetupIntermediateMountNamespace returns a no-op unmountAll() and no error.
+func SetupIntermediateMountNamespace(spec *specs.Spec, bundlePath string) (unmountAll func() error, err error) {
+ stripNoBindOption(spec)
+ return func() error { return nil }, nil
+}
diff --git a/vendor/github.com/containers/buildah/bind/util.go b/vendor/github.com/containers/buildah/bind/util.go
new file mode 100644
index 00000000..c139dcbc
--- /dev/null
+++ b/vendor/github.com/containers/buildah/bind/util.go
@@ -0,0 +1,28 @@
+package bind
+
+import (
+ "slices"
+
+ "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+const (
+ // NoBindOption is an option which, if present in a Mount structure's
+ // options list, will cause SetupIntermediateMountNamespace to not
+ // redirect it through a bind mount.
+ NoBindOption = "nobuildahbind"
+)
+
+func stripNoBindOption(spec *specs.Spec) {
+ for i := range spec.Mounts {
+ if slices.Contains(spec.Mounts[i].Options, NoBindOption) {
+ prunedOptions := make([]string, 0, len(spec.Mounts[i].Options))
+ for _, option := range spec.Mounts[i].Options {
+ if option != NoBindOption {
+ prunedOptions = append(prunedOptions, option)
+ }
+ }
+ spec.Mounts[i].Options = prunedOptions
+ }
+ }
+}
diff --git a/vendor/github.com/containers/buildah/btrfs_installed_tag.sh b/vendor/github.com/containers/buildah/btrfs_installed_tag.sh
new file mode 100644
index 00000000..f2f2b33c
--- /dev/null
+++ b/vendor/github.com/containers/buildah/btrfs_installed_tag.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+${CPP:-${CC:-cc} -E} ${CPPFLAGS} - > /dev/null 2> /dev/null << EOF
+#include
+EOF
+if test $? -ne 0 ; then
+ echo exclude_graphdriver_btrfs
+fi
diff --git a/vendor/github.com/containers/buildah/buildah.go b/vendor/github.com/containers/buildah/buildah.go
new file mode 100644
index 00000000..27d67b63
--- /dev/null
+++ b/vendor/github.com/containers/buildah/buildah.go
@@ -0,0 +1,583 @@
+package buildah
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+ "time"
+
+ "github.com/containers/buildah/define"
+ "github.com/containers/buildah/docker"
+ encconfig "github.com/containers/ocicrypt/config"
+ v1 "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/sirupsen/logrus"
+ nettypes "go.podman.io/common/libnetwork/types"
+ "go.podman.io/image/v5/types"
+ "go.podman.io/storage"
+ "go.podman.io/storage/pkg/ioutils"
+)
+
+const (
+ // Package is the name of this package, used in help output and to
+ // identify working containers.
+ Package = define.Package
+ // Version for the Package.
+ Version = define.Version
+ // The value we use to identify what type of information, currently a
+ // serialized Builder structure, we are using as per-container state.
+ // This should only be changed when we make incompatible changes to
+ // that data structure, as it's used to distinguish containers which
+ // are "ours" from ones that aren't.
+ containerType = Package + " 0.0.1"
+ // The file in the per-container directory which we use to store our
+ // per-container state. If it isn't there, then the container isn't
+ // one of our build containers.
+ stateFile = Package + ".json"
+)
+
+// PullPolicy takes the value PullIfMissing, PullAlways, PullIfNewer, or PullNever.
+type PullPolicy = define.PullPolicy
+
+const (
+ // PullIfMissing is one of the values that BuilderOptions.PullPolicy
+ // can take, signalling that the source image should be pulled from a
+ // registry if a local copy of it is not already present.
+ PullIfMissing = define.PullIfMissing
+ // PullAlways is one of the values that BuilderOptions.PullPolicy can
+ // take, signalling that a fresh, possibly updated, copy of the image
+ // should be pulled from a registry before the build proceeds.
+ PullAlways = define.PullAlways
+ // PullIfNewer is one of the values that BuilderOptions.PullPolicy
+ // can take, signalling that the source image should only be pulled
+ // from a registry if a local copy is not already present or if a
+ // newer version the image is present on the repository.
+ PullIfNewer = define.PullIfNewer
+ // PullNever is one of the values that BuilderOptions.PullPolicy can
+ // take, signalling that the source image should not be pulled from a
+ // registry if a local copy of it is not already present.
+ PullNever = define.PullNever
+)
+
+// NetworkConfigurationPolicy takes the value NetworkDefault, NetworkDisabled,
+// or NetworkEnabled.
+type NetworkConfigurationPolicy = define.NetworkConfigurationPolicy
+
+const (
+ // NetworkDefault is one of the values that BuilderOptions.ConfigureNetwork
+ // can take, signalling that the default behavior should be used.
+ NetworkDefault = define.NetworkDefault
+ // NetworkDisabled is one of the values that BuilderOptions.ConfigureNetwork
+ // can take, signalling that network interfaces should NOT be configured for
+ // newly-created network namespaces.
+ NetworkDisabled = define.NetworkDisabled
+ // NetworkEnabled is one of the values that BuilderOptions.ConfigureNetwork
+ // can take, signalling that network interfaces should be configured for
+ // newly-created network namespaces.
+ NetworkEnabled = define.NetworkEnabled
+)
+
+// Builder objects are used to represent containers which are being used to
+// build images. They also carry potential updates which will be applied to
+// the image's configuration when the container's contents are used to build an
+// image.
+type Builder struct {
+ store storage.Store
+
+ // Logger is the logrus logger to write log messages with
+ Logger *logrus.Logger `json:"-"`
+
+ // Args define variables that users can pass at build-time to the builder.
+ Args map[string]string
+ // Type is used to help identify a build container's metadata. It
+ // should not be modified.
+ Type string `json:"type"`
+ // FromImage is the name of the source image which was used to create
+ // the container, if one was used. It should not be modified.
+ FromImage string `json:"image,omitempty"`
+ // FromImageID is the ID of the source image which was used to create
+ // the container, if one was used. It should not be modified.
+ FromImageID string `json:"image-id"`
+ // FromImageDigest is the digest of the source image which was used to
+ // create the container, if one was used. It should not be modified.
+ FromImageDigest string `json:"image-digest"`
+ // Config is the source image's configuration. It should not be
+ // modified.
+ Config []byte `json:"config,omitempty"`
+ // Manifest is the source image's manifest. It should not be modified.
+ Manifest []byte `json:"manifest,omitempty"`
+
+ // Container is the name of the build container. It should not be modified.
+ Container string `json:"container-name,omitempty"`
+ // ContainerID is the ID of the build container. It should not be modified.
+ ContainerID string `json:"container-id,omitempty"`
+ // MountPoint is the last location where the container's root
+ // filesystem was mounted. It should not be modified.
+ MountPoint string `json:"mountpoint,omitempty"`
+ // ProcessLabel is the SELinux process label to use during subsequent Run() calls.
+ ProcessLabel string `json:"process-label,omitempty"`
+ // MountLabel is the SELinux mount label associated with the container
+ MountLabel string `json:"mount-label,omitempty"`
+
+ // ImageAnnotations is a set of key-value pairs which is stored in the
+ // image's manifest.
+ ImageAnnotations map[string]string `json:"annotations,omitempty"`
+ // ImageCreatedBy is a description of how this container was built.
+ ImageCreatedBy string `json:"created-by,omitempty"`
+ // ImageHistoryComment is a description of how our added layers were built.
+ ImageHistoryComment string `json:"history-comment,omitempty"`
+
+ // Image metadata and runtime settings, in multiple formats.
+ OCIv1 v1.Image `json:"ociv1"`
+ Docker docker.V2Image `json:"docker"`
+ // DefaultMountsFilePath is the file path holding the mounts to be mounted in "host-path:container-path" format.
+ DefaultMountsFilePath string `json:"defaultMountsFilePath,omitempty"`
+
+ // Isolation controls how we handle "RUN" statements and the Run() method.
+ Isolation define.Isolation
+ // NamespaceOptions controls how we set up the namespaces for processes that we Run().
+ NamespaceOptions define.NamespaceOptions
+ // ConfigureNetwork controls whether or not network interfaces and
+ // routing are configured for a new network namespace (i.e., when not
+ // joining another's namespace and not just using the host's
+ // namespace), effectively deciding whether or not the process has a
+ // usable network.
+ ConfigureNetwork define.NetworkConfigurationPolicy
+ // CNIPluginPath is the location of CNI plugin helpers, if they should be
+ // run from a location other than the default location.
+ CNIPluginPath string
+ // CNIConfigDir is the location of CNI configuration files, if the files in
+ // the default configuration directory shouldn't be used.
+ CNIConfigDir string
+
+ // NetworkInterface is the libnetwork network interface used to setup CNI or netavark networks.
+ NetworkInterface nettypes.ContainerNetwork `json:"-"`
+
+ // GroupAdd is a list of groups to add to the primary process when Run() is
+ // called. The magic 'keep-groups' value indicates that the process should
+ // be allowed to inherit the current set of supplementary groups.
+ GroupAdd []string
+ // ID mapping options to use when running processes with non-host user namespaces.
+ IDMappingOptions define.IDMappingOptions
+ // Capabilities is a list of capabilities to use when running commands in the container.
+ Capabilities []string
+ // PrependedEmptyLayers are history entries that we'll add to a
+ // committed image, after any history items that we inherit from a base
+ // image, but before the history item for the layer that we're
+ // committing.
+ PrependedEmptyLayers []v1.History
+ // AppendedEmptyLayers are history entries that we'll add to a
+ // committed image after the history item for the layer that we're
+ // committing.
+ AppendedEmptyLayers []v1.History
+ CommonBuildOpts *define.CommonBuildOptions
+ // TopLayer is the top layer of the image
+ TopLayer string
+ // Format to use for a container image we eventually commit, when we do.
+ Format string
+ // TempVolumes are temporary mount points created during Run() calls.
+ // Deprecated: do not use.
+ TempVolumes map[string]bool
+ // ContentDigester counts the digest of all Add()ed content since it was
+ // last restarted.
+ ContentDigester CompositeDigester
+ // Devices are parsed additional devices to provide to Run() calls.
+ Devices define.ContainerDevices
+ // DeviceSpecs are unparsed additional devices to provide to Run() calls.
+ DeviceSpecs []string
+ // CDIConfigDir is the location of CDI configuration files, if the files in
+ // the default configuration locations shouldn't be used.
+ CDIConfigDir string
+ // PrependedLinkedLayers and AppendedLinkedLayers are combinations of
+ // history entries and locations of either directory trees (if
+ // directories, per os.Stat()) or uncompressed layer blobs which should
+ // be added to the image at commit-time. The order of these relative
+ // to PrependedEmptyLayers and AppendedEmptyLayers in the committed
+ // image is not guaranteed.
+ PrependedLinkedLayers, AppendedLinkedLayers []LinkedLayer
+}
+
+// BuilderInfo are used as objects to display container information
+type BuilderInfo struct {
+ Type string
+ FromImage string
+ FromImageID string
+ FromImageDigest string
+ GroupAdd []string
+ Config string
+ Manifest string
+ Container string
+ ContainerID string
+ MountPoint string
+ ProcessLabel string
+ MountLabel string
+ ImageAnnotations map[string]string
+ ImageCreatedBy string
+ OCIv1 v1.Image
+ Docker docker.V2Image
+ DefaultMountsFilePath string
+ Isolation string
+ NamespaceOptions define.NamespaceOptions
+ Capabilities []string
+ ConfigureNetwork string
+ CNIPluginPath string
+ CNIConfigDir string
+ IDMappingOptions define.IDMappingOptions
+ History []v1.History
+ Devices define.ContainerDevices
+ DeviceSpecs []string
+ CDIConfigDir string
+}
+
+// GetBuildInfo gets a pointer to a Builder object and returns a BuilderInfo object from it.
+// This is used in the inspect command to display Manifest and Config as string and not []byte.
+func GetBuildInfo(b *Builder) BuilderInfo {
+ history := copyHistory(b.OCIv1.History)
+ history = append(history, copyHistory(b.PrependedEmptyLayers)...)
+ history = append(history, copyHistory(b.AppendedEmptyLayers)...)
+ sort.Strings(b.Capabilities)
+ return BuilderInfo{
+ Type: b.Type,
+ FromImage: b.FromImage,
+ FromImageID: b.FromImageID,
+ FromImageDigest: b.FromImageDigest,
+ Config: string(b.Config),
+ Manifest: string(b.Manifest),
+ Container: b.Container,
+ ContainerID: b.ContainerID,
+ GroupAdd: b.GroupAdd,
+ MountPoint: b.MountPoint,
+ ProcessLabel: b.ProcessLabel,
+ MountLabel: b.MountLabel,
+ ImageAnnotations: b.ImageAnnotations,
+ ImageCreatedBy: b.ImageCreatedBy,
+ OCIv1: b.OCIv1,
+ Docker: b.Docker,
+ DefaultMountsFilePath: b.DefaultMountsFilePath,
+ Isolation: b.Isolation.String(),
+ NamespaceOptions: b.NamespaceOptions,
+ ConfigureNetwork: fmt.Sprintf("%v", b.ConfigureNetwork),
+ CNIPluginPath: b.CNIPluginPath,
+ CNIConfigDir: b.CNIConfigDir,
+ IDMappingOptions: b.IDMappingOptions,
+ Capabilities: b.Capabilities,
+ History: history,
+ Devices: b.Devices,
+ DeviceSpecs: b.DeviceSpecs,
+ CDIConfigDir: b.CDIConfigDir,
+ }
+}
+
+// CommonBuildOptions are resources that can be defined by flags for both buildah from and build
+type CommonBuildOptions = define.CommonBuildOptions
+
+// BuilderOptions are used to initialize a new Builder.
+type BuilderOptions struct {
+ // Args define variables that users can pass at build-time to the builder
+ Args map[string]string
+ // FromImage is the name of the image which should be used as the
+ // starting point for the container. It can be set to an empty value
+ // or "scratch" to indicate that the container should not be based on
+ // an image.
+ FromImage string
+ // ContainerSuffix is the suffix to add for generated container names
+ ContainerSuffix string
+ // Container is a desired name for the build container.
+ Container string
+ // PullPolicy decides whether or not we should pull the image that
+ // we're using as a base image. It should be PullIfMissing,
+ // PullAlways, or PullNever.
+ PullPolicy define.PullPolicy
+ // Registry is a value which is prepended to the image's name, if it
+ // needs to be pulled and the image name alone can not be resolved to a
+ // reference to a source image. No separator is implicitly added.
+ Registry string
+ // BlobDirectory is the name of a directory in which we'll attempt
+ // to store copies of layer blobs that we pull down, if any. It should
+ // already exist.
+ BlobDirectory string
+ GroupAdd []string
+ // Logger is the logrus logger to write log messages with
+ Logger *logrus.Logger `json:"-"`
+ // Mount signals to NewBuilder() that the container should be mounted
+ // immediately.
+ Mount bool
+ // SignaturePolicyPath specifies an override location for the signature
+ // policy which should be used for verifying the new image as it is
+ // being written. Except in specific circumstances, no value should be
+ // specified, indicating that the shared, system-wide default policy
+ // should be used.
+ SignaturePolicyPath string
+ // ReportWriter is an io.Writer which will be used to log the reading
+ // of the source image from a registry, if we end up pulling the image.
+ ReportWriter io.Writer
+ // github.com/containers/image/types SystemContext to hold credentials
+ // and other authentication/authorization information.
+ SystemContext *types.SystemContext
+ // DefaultMountsFilePath is the file path holding the mounts to be
+ // mounted in "host-path:container-path" format
+ DefaultMountsFilePath string
+ // Isolation controls how we handle "RUN" statements and the Run()
+ // method.
+ Isolation define.Isolation
+ // NamespaceOptions controls how we set up namespaces for processes that
+ // we might need to run using the container's root filesystem.
+ NamespaceOptions define.NamespaceOptions
+ // ConfigureNetwork controls whether or not network interfaces and
+ // routing are configured for a new network namespace (i.e., when not
+ // joining another's namespace and not just using the host's
+ // namespace), effectively deciding whether or not the process has a
+ // usable network.
+ ConfigureNetwork define.NetworkConfigurationPolicy
+ // CNIPluginPath is the location of CNI plugin helpers, if they should be
+ // run from a location other than the default location.
+ CNIPluginPath string
+ // CNIConfigDir is the location of CNI configuration files, if the files in
+ // the default configuration directory shouldn't be used.
+ CNIConfigDir string
+
+ // NetworkInterface is the libnetwork network interface used to setup CNI or netavark networks.
+ NetworkInterface nettypes.ContainerNetwork `json:"-"`
+
+ // ID mapping options to use if we're setting up our own user namespace.
+ IDMappingOptions *define.IDMappingOptions
+ // Capabilities is a list of capabilities to use when
+ // running commands for Run().
+ Capabilities []string
+ CommonBuildOpts *define.CommonBuildOptions
+ // Format to use for a container image we eventually commit, when we do.
+ Format string
+ // Devices are additional parsed devices to provide for Run() calls.
+ Devices define.ContainerDevices
+ // DeviceSpecs are additional unparsed devices to provide for Run() calls.
+ DeviceSpecs []string
+ // DefaultEnv is deprecated and ignored.
+ DefaultEnv []string
+ // MaxPullRetries is the maximum number of attempts we'll make to pull
+ // any one image from the external registry if the first attempt fails.
+ MaxPullRetries int
+ // PullRetryDelay is how long to wait before retrying a pull attempt.
+ PullRetryDelay time.Duration
+ // OciDecryptConfig contains the config that can be used to decrypt an image if it is
+ // encrypted if non-nil. If nil, it does not attempt to decrypt an image.
+ OciDecryptConfig *encconfig.DecryptConfig
+ // ProcessLabel is the SELinux process label associated with commands we Run()
+ ProcessLabel string
+ // MountLabel is the SELinux mount label associated with the working container
+ MountLabel string
+ // PreserveBaseImageAnns indicates that we should preserve base
+ // image information (Annotations) that are present in our base image,
+ // rather than overwriting them with information about the base image
+ // itself. Useful as an internal implementation detail of multistage
+ // builds, and does not need to be set by most callers.
+ PreserveBaseImageAnns bool
+ // CDIConfigDir is the location of CDI configuration files, if the files in
+ // the default configuration locations shouldn't be used.
+ CDIConfigDir string
+ // CompatScratchConfig controls whether a "scratch" image is created
+ // with a truly empty configuration, as would have happened in the past
+ // (when set to true), or with a minimal initial configuration which
+ // has a working directory set in it.
+ CompatScratchConfig types.OptionalBool
+}
+
+// ImportOptions are used to initialize a Builder from an existing container
+// which was created elsewhere.
+type ImportOptions struct {
+ // Container is the name of the build container.
+ Container string
+ // SignaturePolicyPath specifies an override location for the signature
+ // policy which should be used for verifying the new image as it is
+ // being written. Except in specific circumstances, no value should be
+ // specified, indicating that the shared, system-wide default policy
+ // should be used.
+ SignaturePolicyPath string
+}
+
+// ImportFromImageOptions are used to initialize a Builder from an image.
+type ImportFromImageOptions struct {
+ // Image is the name or ID of the image we'd like to examine.
+ Image string
+ // SignaturePolicyPath specifies an override location for the signature
+ // policy which should be used for verifying the new image as it is
+ // being written. Except in specific circumstances, no value should be
+ // specified, indicating that the shared, system-wide default policy
+ // should be used.
+ SignaturePolicyPath string
+ // github.com/containers/image/types SystemContext to hold information
+ // about which registries we should check for completing image names
+ // that don't include a domain portion.
+ SystemContext *types.SystemContext
+}
+
+// ConfidentialWorkloadOptions encapsulates options which control whether or not
+// we output an image whose rootfs contains a LUKS-compatibly-encrypted disk image
+// instead of the usual rootfs contents.
+type ConfidentialWorkloadOptions = define.ConfidentialWorkloadOptions
+
+// SBOMScanOptions encapsulates options which control whether or not we run a
+// scanner on the rootfs that we're about to commit, and how.
+type SBOMScanOptions = define.SBOMScanOptions
+
+// NewBuilder creates a new build container.
+func NewBuilder(ctx context.Context, store storage.Store, options BuilderOptions) (*Builder, error) {
+ if options.CommonBuildOpts == nil {
+ options.CommonBuildOpts = &CommonBuildOptions{}
+ }
+ return newBuilder(ctx, store, options)
+}
+
+// ImportBuilder creates a new build configuration using an already-present
+// container.
+func ImportBuilder(ctx context.Context, store storage.Store, options ImportOptions) (*Builder, error) {
+ return importBuilder(ctx, store, options)
+}
+
+// ImportBuilderFromImage creates a new builder configuration using an image.
+// The returned object can be modified and examined, but it can not be saved
+// or committed because it is not associated with a working container.
+func ImportBuilderFromImage(ctx context.Context, store storage.Store, options ImportFromImageOptions) (*Builder, error) {
+ return importBuilderFromImage(ctx, store, options)
+}
+
+// OpenBuilder loads information about a build container given its name or ID.
+func OpenBuilder(store storage.Store, container string) (*Builder, error) {
+ cdir, err := store.ContainerDirectory(container)
+ if err != nil {
+ return nil, err
+ }
+ buildstate, err := os.ReadFile(filepath.Join(cdir, stateFile))
+ if err != nil {
+ return nil, err
+ }
+ b := &Builder{}
+ if err = json.Unmarshal(buildstate, &b); err != nil {
+ return nil, fmt.Errorf("parsing %q, read from %q: %w", string(buildstate), filepath.Join(cdir, stateFile), err)
+ }
+ if b.Type != containerType {
+ return nil, fmt.Errorf("container %q is not a %s container (is a %q container)", container, define.Package, b.Type)
+ }
+
+ netInt, err := getNetworkInterface(store, b.CNIConfigDir, b.CNIPluginPath)
+ if err != nil {
+ return nil, err
+ }
+ b.NetworkInterface = netInt
+ b.store = store
+ b.fixupConfig(nil)
+ b.setupLogger()
+ if b.CommonBuildOpts == nil {
+ b.CommonBuildOpts = &CommonBuildOptions{}
+ }
+ return b, nil
+}
+
+// OpenBuilderByPath loads information about a build container given a
+// path to the container's root filesystem
+func OpenBuilderByPath(store storage.Store, path string) (*Builder, error) {
+ containers, err := store.Containers()
+ if err != nil {
+ return nil, err
+ }
+ abs, err := filepath.Abs(path)
+ if err != nil {
+ return nil, err
+ }
+ builderMatchesPath := func(b *Builder, path string) bool {
+ return (b.MountPoint == path)
+ }
+ for _, container := range containers {
+ cdir, err := store.ContainerDirectory(container.ID)
+ if err != nil {
+ return nil, err
+ }
+ buildstate, err := os.ReadFile(filepath.Join(cdir, stateFile))
+ if err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ logrus.Debugf("error reading %q: %v, ignoring container %q", filepath.Join(cdir, stateFile), err, container.ID)
+ continue
+ }
+ return nil, err
+ }
+ b := &Builder{}
+ err = json.Unmarshal(buildstate, &b)
+ if err == nil && b.Type == containerType && builderMatchesPath(b, abs) {
+ b.store = store
+ b.fixupConfig(nil)
+ b.setupLogger()
+ if b.CommonBuildOpts == nil {
+ b.CommonBuildOpts = &CommonBuildOptions{}
+ }
+ return b, nil
+ }
+ if err != nil {
+ logrus.Debugf("error parsing %q, read from %q: %v", string(buildstate), filepath.Join(cdir, stateFile), err)
+ } else if b.Type != containerType {
+ logrus.Debugf("container %q is not a %s container (is a %q container)", container.ID, define.Package, b.Type)
+ }
+ }
+ return nil, storage.ErrContainerUnknown
+}
+
+// OpenAllBuilders loads all containers which have a state file that we use in
+// their data directory, typically so that they can be listed.
+func OpenAllBuilders(store storage.Store) (builders []*Builder, err error) {
+ containers, err := store.Containers()
+ if err != nil {
+ return nil, err
+ }
+ for _, container := range containers {
+ cdir, err := store.ContainerDirectory(container.ID)
+ if err != nil {
+ return nil, err
+ }
+ buildstate, err := os.ReadFile(filepath.Join(cdir, stateFile))
+ if err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ logrus.Debugf("%v, ignoring container %q", err, container.ID)
+ continue
+ }
+ return nil, err
+ }
+ b := &Builder{}
+ err = json.Unmarshal(buildstate, &b)
+ if err == nil && b.Type == containerType {
+ b.store = store
+ b.setupLogger()
+ b.fixupConfig(nil)
+ if b.CommonBuildOpts == nil {
+ b.CommonBuildOpts = &CommonBuildOptions{}
+ }
+ builders = append(builders, b)
+ continue
+ }
+ if err != nil {
+ logrus.Debugf("error parsing %q, read from %q: %v", string(buildstate), filepath.Join(cdir, stateFile), err)
+ } else if b.Type != containerType {
+ logrus.Debugf("container %q is not a %s container (is a %q container)", container.ID, define.Package, b.Type)
+ }
+ }
+ return builders, nil
+}
+
+// Save saves the builder's current state to the build container's metadata.
+// This should not need to be called directly, as other methods of the Builder
+// object take care of saving their state.
+func (b *Builder) Save() error {
+ buildstate, err := json.Marshal(b)
+ if err != nil {
+ return err
+ }
+ cdir, err := b.store.ContainerDirectory(b.ContainerID)
+ if err != nil {
+ return err
+ }
+ if err = ioutils.AtomicWriteFile(filepath.Join(cdir, stateFile), buildstate, 0o600); err != nil {
+ return fmt.Errorf("saving builder state to %q: %w", filepath.Join(cdir, stateFile), err)
+ }
+ return nil
+}
diff --git a/vendor/github.com/containers/buildah/changelog.txt b/vendor/github.com/containers/buildah/changelog.txt
new file mode 100644
index 00000000..02857a42
--- /dev/null
+++ b/vendor/github.com/containers/buildah/changelog.txt
@@ -0,0 +1,3721 @@
+- Changelog for v1.42.2 (2025-12-02)
+ * [release-1.42] Bump runc to v1.3.4
+
+- Changelog for v1.42.1 (2025-11-06)
+ * [release-1.42] bump runc to v1.3.3 - CVE-2025-52881
+ * RPM: build with sequoia on F43+
+
+- Changelog for v1.42.0 (2025-10-17)
+ * Bump to storage v1.61.0, image v5.38.0, common v0.66.0
+ * fix(deps): update module github.com/openshift/imagebuilder to v1.2.19
+ * fix(deps): update module github.com/openshift/imagebuilder to v1.2.18
+ * copier: ignore user.overlay.* xattrs
+ * commit: always return the config digest as the image ID
+ * fix(deps): update module golang.org/x/crypto to v0.43.0
+ * fix(deps): update module golang.org/x/sys to v0.37.0
+ * fix(deps): update module github.com/docker/docker to v28.5.1+incompatible
+ * fix(deps): update module github.com/moby/buildkit to v0.25.1
+ * fix(deps): update module github.com/opencontainers/runc to v1.3.2
+ * fix(deps): update module github.com/docker/docker to v28.5.0+incompatible
+ * fix(deps): update module github.com/moby/buildkit to v0.25.0
+ * fix(deps): update github.com/containers/luksy digest to 2cf5bc9
+ * Make some test files different from each other
+ * Revert "fix(deps): update module github.com/cyphar/filepath-securejoin to v0.5.0"
+ * Also run integration tests with the Sequoia backend
+ * Allow users to build against podman-sequoia in non-default locations
+ * fix(deps): update module github.com/cyphar/filepath-securejoin to v0.5.0
+ * .cirrus.yml: Test Vendoring bump golang
+ * vendor: bump go.podman.io/{common,image,storage} to main
+ * fix(deps): update module golang.org/x/crypto to v0.42.0
+ * fix(deps): update module github.com/docker/docker to v28.4.0+incompatible
+ * fix(deps): update module github.com/moby/buildkit to v0.24.0
+ * fix(deps): update module github.com/spf13/pflag to v1.0.10
+ * fix(deps): update module github.com/fsouza/go-dockerclient to v1.12.2
+ * fix(deps): update module github.com/opencontainers/runc to v1.3.1
+ * fix(deps): update module github.com/opencontainers/cgroups to v0.0.5
+ * fix(deps): update module golang.org/x/sync to v0.17.0
+ * tests/run.bats: "run masks" test: accept "unreadable" masked directories
+ * Run: create parent directories of mount targets with mode 0755
+ * tests/run.bats: "run masks" test: accept "unreadable" masked directories
+ * New VM images
+ * Suppress a linter warning
+ * modernize: JSON doesn't do "omitempty" structs, so stop asking
+ * modernize: use maps.Copy() instead of iterating over a map to copy it
+ * modernize: use strings.CutPrefix/SplitSeq/FieldsSeq
+ * Update expected/minimum version of Go to 1.24
+ * chroot: use $PATH when finding commands
+ * [skip-ci] Update actions/stale action to v10
+ * Update module github.com/ulikunitz/xz to v0.5.15 [SECURITY]
+ * Update go.sum
+ * New VM images
+ * Update module github.com/openshift/imagebuilder to v1
+ * Update module github.com/spf13/cobra to v1.10.1
+ * Switch common, storage and image to monorepo.
+ * Update module github.com/stretchr/testify to v1.11.1
+ * Update module go.etcd.io/bbolt to v1.4.3
+ * Handle tagged+digested references when processing --all-platforms
+ * Update module github.com/stretchr/testify to v1.11.0
+ * Add --transient-store global option
+ * Support "--imagestore" global flags
+ * Commit: don't depend on MountImage(), because .imagestore
+ * Adding mohanboddu as community manager to MAINTAINERS.md
+ * Rework how we decide what to filter out of layer diffs
+ * Note that we have to build `true` first for the sake of its tests
+ * copier.Stat(): return owner UID and GID if available
+ * copier.Get(): ensure that directory entries end in "/"
+ * copier.Get(): strip user and group names from entries
+ * imagebuildah.Executor/StageExecutor: check numeric --from= values
+ * Losen the dependency on go-connections/tlsconfig
+ * fix(deps): update module golang.org/x/crypto to v0.41.0
+ * fix(deps): update module golang.org/x/term to v0.34.0
+ * fix(deps): update module github.com/docker/go-connections to v0.6.0
+ * fix(deps): update module golang.org/x/sys to v0.35.0
+ * copy: assume a destination with basename "." is a directory
+ * generatePathChecksum: ignore ModTime, AccessTime and ChangeTime
+ * fix(deps): update module github.com/seccomp/libseccomp-golang to v0.11.1
+ * fix(deps): update module github.com/containers/common to v0.64.1
+ * History should note unset-label, timestamp, and rewrite-timestamp
+ * pkg/cli.GenBuildOptions(): don't hardwire optional bools
+ * fix(deps): update module github.com/containers/image/v5 to v5.36.1
+ * imagebuildah.StageExecutor.Execute: commit more "no instructions" cases
+ * fix(deps): update module github.com/containers/storage to v1.59.1
+ * Only suppress "noted" items when not squashing
+ * Reap stray processes
+ * fix(deps): update github.com/containers/luksy digest to 8fccf78
+ * fix(deps): update module github.com/docker/docker to v28.3.3+incompatible
+ * Restore the default meaning of `--pull` (should be `always`).
+ * Test that pulled up parent directories are excluded at commit
+ * Exclude pulled up parent directories at commit-time
+ * copier.Ensure(): also return parent directories
+ * copier.MkdirOptions: add ModTimeNew
+ * fix(deps): update module github.com/containers/common to v0.64.0
+ * Bump to Buildah v1.42.0-dev
+ * fix(deps): update module github.com/spf13/pflag to v1.0.7
+ * CI: make runc tests non-blocking
+ * build,add: add support for corporate proxies
+
+- Changelog for v1.41.0 (2025-07-16)
+ * Bump to c/storage v1.59.0, c/image v5.36.0, ... c/common v0.64.0
+ * stage_executor: check platform of cache candidates
+ * fix(deps): update module golang.org/x/crypto to v0.40.0
+ * fix(deps): update module golang.org/x/term to v0.33.0
+ * fix(deps): update module golang.org/x/sync to v0.16.0
+ * fix(deps): update module github.com/docker/docker to v28.3.2+incompatible
+ * ADD/COPY --link support added
+ * RPM/TMT: account for passwd binary moving to tests
+ * buildah: move passwd command to tests
+ * Update "bud with --cpu-shares" test, and rename it
+ * Remove BUILDTAG btrfs_noversion as no longer effective
+ * fix(deps): update module github.com/docker/docker to v28.3.1+incompatible
+ * fix(deps): update module github.com/moby/buildkit to v0.23.2
+ * fix(deps): update github.com/containers/luksy digest to bc60f96
+ * chore(typos): fix typos
+ * vendor: update c/{common,image,storage} to main
+ * chore(deps): update module github.com/go-viper/mapstructure/v2 to v2.3.0 [security]
+ * fix(deps): update module go.etcd.io/bbolt to v1.4.2
+ * Update Neil Smith's GitHub username in MAINTAINERS.md
+ * Accept SOURCE_DATE_EPOCH as a build-arg
+ * fix(deps): update module github.com/docker/docker to v28.3.0+incompatible
+ * Add conditional release-checking system test
+ * info,inspect: use the "formats" package to get some builtins
+ * Use containers/common's formats package instead of our own
+ * build, commit: set the OCI ...created annotation on OCI images
+ * commit: exclude parents of mount targets, too
+ * run: clean up parents of mount targets, too
+ * tarFilterer: always flush after writing
+ * Builder: drop the TempVolumes field
+ * Update module github.com/moby/buildkit to v0.23.1
+ * Update module github.com/opencontainers/cgroups to v0.0.3
+ * Add CommitOptions.OmitLayerHistoryEntry, for skipping the new bits
+ * Update module github.com/fsouza/go-dockerclient to v1.12.1
+ * conformance: use mirrored frontend and base images
+ * commit-with-extra-files test: use $TEST_SCRATCH_DIR
+ * fix(deps): update module github.com/moby/buildkit to v0.23.0
+ * "root fs only mounted once" test: accept root with only the rw option
+ * Run with --device /dev/fuse and not just -v /dev/fuse:/dev/fuse
+ * CI: pass $BUILDAH_RUNTIME through to in-container test runs
+ * CI: ensure rootless groups aren't duplicates
+ * build: add support for --inherit-annotations
+ * CI: give the rootless test user some supplemental groups
+ * bud,run: runc does not support keep-groups
+ * Fix lint issue in TestCommitCompression
+ * Add a unit test for compression types in OCI images
+ * Support zstd compression in image commit
+ * fix(deps): update module go.etcd.io/bbolt to v1.4.1
+ * rpm: build rpm with libsqlite3 tag
+ * Makefile: use libsqlite3 build when possible
+ * commit,build: --source-date-epoch/--timestamp omit identity label
+ * docs: add --setopt "*.countme=false" to dnf examples
+ * Builder.sbomScan(): don't break non-root scanners
+ * build: --source-date-epoch/--timestamp use static hostname/cid
+ * fix(deps): update module golang.org/x/crypto to v0.39.0
+ * fix(deps): update module golang.org/x/sync to v0.15.0
+ * build: add --source-date-epoch and --rewrite-timestamp flags
+ * build,config: add support for --unsetannotation
+ * commit: add --source-date-epoch and --rewrite-timestamp flags
+ * fix(deps): update module github.com/openshift/imagebuilder to v1.2.16
+ * vendor latest c/{common,image,storage}
+ * Tweak our handling of variant values, again
+ * Don't BuildRequires: ostree-devel
+ * parse, validateExtraHost: honor Hostgateway in format
+ * remove static nix build
+ * Ensure extendedGlob returns paths in lexical order
+ * CI: run integration tests on Fedora with both crun and runc
+ * buildah-build(1): clarify that --cgroup-parent affects RUN instructions
+ * runUsingRuntime: use named constants for runtime states
+ * Add a dummy "runtime" that just dumps its config file
+ * run: handle relabeling bind mounts ourselves
+ * fix link to Maintainers file
+ * Update to avoid deprecated types
+ * fix(deps): update module github.com/docker/docker to v28.2.0+incompatible
+ * [skip-ci] Packit: cleanup redundant targets and unused anchors
+ * [skip-ci] Packit: set fedora-all after F40 EOL
+ * Use Fedora 42 instead of 41 in that one conformance test
+ * [CI:DOCS] README.md: add openssf passing badge
+ * fix(deps): update module github.com/moby/buildkit to v0.22.0
+ * copier: add Ensure and ConditionalRemove
+ * [CI:DOCS] update a couple of lists in the build man page
+ * build: allow --output to be specified multiple times
+ * add: add a new --timestamp flag
+ * tests/helpers.bash: add some helpers for parsing images
+ * pkg/parse.GetBuildOutput(): use strings.Cut()
+ * [skip-ci] Packit: Disable osh_diff_scan
+ * internal/util.SetHas(): handle maps of [generic]generic
+ * Refactor NewImageSource to add a manifest type abstraction (#5743)
+ * [skip-ci] Packit: Ignore ELN and CentOS Stream jobs
+ * imagebuildah: select most recent layer for cache
+ * [CI:DOCS] Add CNCF roadmap, touchup other CNCF files
+ * fix(deps): update module golang.org/x/crypto to v0.38.0
+ * Fix typo in comment (#6167)
+ * Support label_users in buildah
+ * fix(deps): update module golang.org/x/sync to v0.14.0
+ * fix(deps): update github.com/containers/luksy digest to 4bb4c3f
+ * test/serve: fix a descriptor leak, add preliminary directory support
+ * fix(deps): update module github.com/opencontainers/cgroups to v0.0.2
+ * fix(deps): update module github.com/moby/buildkit to v0.21.1
+ * Update to avoid deprecated types
+ * fix(deps): update module github.com/opencontainers/runc to v1.3.0
+ * Only filter if containerImageRef.created != nil
+ * Drop superfluous cast
+ * Remove UID/GID scrubbing.
+ * fix(deps): update module github.com/seccomp/libseccomp-golang to v0.11.0
+ * cirrus: turn prior fedora testing back on
+ * chore(deps): update dependency containers/automation_images to v20250422
+ * fix(deps): update module github.com/docker/docker to v28.1.1+incompatible
+ * Bump to Buildah v1.41.0-dev
+ * CI vendor_task: pin to go 1.23.3 for now
+ * fix(deps): update module github.com/containers/common to v0.63.0
+
+- Changelog for v1.40.0 (2025-04-17)
+ * Bump c/storage to v1.58.0, c/image v5.35.0, c/common v0.63.0
+ * fix(deps): update module github.com/docker/docker to v28.1.0+incompatible
+ * fix(deps): update module github.com/containers/storage to v1.58.0
+ * cirrus: make Total Success wait for rootless integration
+ * chroot: use symbolic names when complaining about mount() errors
+ * cli: hide the `completion` command instead of disabling it outright
+ * Document rw and src options for --mount flag in buildah-run(1)
+ * fix(deps): update module github.com/moby/buildkit to v0.21.0
+ * build: add support for inherit-labels
+ * chore(deps): update dependency golangci/golangci-lint to v2.1.0
+ * .github: check_cirrus_cron work around github bug
+ * stage_executor,getCreatedBy: expand buildArgs for sources correctly
+ * Add a link to project governance and MAINTAINERS file
+ * fix(deps): update github.com/containers/storage digest to b1d1b45
+ * generateHostname: simplify
+ * Use maps.Copy
+ * Use slices.Concat
+ * Use slices.Clone
+ * Use slices.Contains
+ * Use for range over integers
+ * tests/testreport: don't copy os.Environ
+ * Use any instead of interface{}
+ * ci: add golangci-lint run with --tests=false
+ * ci: add nolintlint, fix found issues
+ * copier: rm nolint:unparam annotation
+ * .golangci.yml: add unused linter
+ * chroot: fix unused warnings
+ * copier: fix unused warnings
+ * tests/conformance: fix unused warning
+ * ci: switch to golangci-lint v2
+ * internal/mkcw: disable ST1003 warnings
+ * tests/conformance: do not double import (fix ST1019)
+ * cmd/buildah: don't double import (fix ST1019)
+ * Do not capitalize error strings
+ * cmd/buildah: do not capitalize error strings
+ * tests/conformance: fix QF1012 warnings
+ * tests/serve: fix QF1012 warning
+ * Use strings.ReplaceAll to fix QF1004 warnings
+ * Use switch to fix QF1003 warnings
+ * Apply De Morgan's law to fix QF1001 warnings
+ * Fix QF1007 staticcheck warnings
+ * imagebuildah: fix revive warning
+ * Rename max variable
+ * tests/tools: install lint from binary, use renovate
+ * fix(deps): update module github.com/containernetworking/cni to v1.3.0
+ * Update Buildah issue template to new version and support podman build
+ * fix(deps): update module golang.org/x/crypto to v0.37.0
+ * stage_executor: reset platform in systemcontext for stages
+ * fix(deps): update github.com/opencontainers/runtime-tools digest to 260e151
+ * cmd/buildah: rm unused containerOutputUsingTemplate
+ * cmd/buildah: rm unused getDateAndDigestAndSize
+ * build: return ExecErrorCodeGeneric when git operation fails
+ * add: report error while creating dir for URL source.
+ * createPlatformContainer: drop MS_REMOUNT|MS_BIND
+ * fix(deps): update module github.com/docker/docker to v28.0.3+incompatible
+ * fix: bats won't fail on ! without cleverness
+ * feat: use HistoryTimestamp, if set, for oci-archive entries
+ * Allow extendedGlob to work with Windows paths
+ * fix(deps): update module github.com/moby/buildkit to v0.20.2
+ * fix(deps): update github.com/openshift/imagebuilder digest to e87e4e1
+ * fix(deps): update module github.com/docker/docker to v28.0.2+incompatible
+ * fix(deps): update module tags.cncf.io/container-device-interface to v1.0.1
+ * chore(deps): update dependency containers/automation_images to v20250324
+ * vendor: update github.com/opencontainers/selinux to v1.12.0
+ * replace deprecated selinux/label calls
+ * vendor: bump c/common to dbeb17e40c80
+ * Use builtin arg defaults from imagebuilder
+ * linux: accept unmask paths as glob values
+ * vendor: update containers/common
+ * Add --parents option for COPY in Dockerfiles
+ * fix(deps): update module github.com/opencontainers/runc to v1.2.6
+ * update go.sum from the previous commit
+ * fix(deps): update module tags.cncf.io/container-device-interface to v1
+ * chore(deps): update module golang.org/x/net to v0.36.0 [security]
+ * packit: remove f40 from copr builds
+ * cirrus: update to go 1.23 image
+ * vendor bump to golang.org/x/crypto v0.36.0
+ * cirrus: update PRIOR_FEDORA comment
+ * github: remove cirrus rerun action
+ * fix(deps): update module github.com/containers/common to v0.62.2
+ * fix(deps): update module github.com/containers/image/v5 to v5.34.2
+ * fix: close files properly when BuildDockerfiles exits
+ * fix(deps): update module github.com/containers/storage to v1.57.2
+ * stage_executor: history should include heredoc summary correctly
+ * fix(deps): update module github.com/containers/common to v0.62.1
+ * github: disable cron rerun action
+ * fix(deps): update module github.com/moby/buildkit to v0.20.1
+ * internal/mkcw.Archive(): use github.com/containers/storage/pkg/ioutils
+ * [skip-ci] TMT: system tests
+ * buildah-build.1.md: secret examples
+ * fix(deps): update github.com/containers/luksy digest to 40bd943
+ * fix(deps): update module github.com/opencontainers/image-spec to v1.1.1
+ * fix(deps): update module github.com/containers/image/v5 to v5.34.1
+ * Use UnparsedInstance.Manifest instead of ImageSource.GetManifest
+ * fix(deps): update module github.com/opencontainers/runtime-spec to v1.2.1
+ * tests/conformance/testdata/Dockerfile.add: update some URLs
+ * Vendor imagebuilder
+ * Fix source of OS, architecture and variant
+ * chore(deps): update module github.com/go-jose/go-jose/v4 to v4.0.5 [security]
+ * fix(deps): update module tags.cncf.io/container-device-interface to v0.8.1
+ * fix(deps): update module github.com/moby/buildkit to v0.20.0
+ * chroot createPlatformContainer: use MS_REMOUNT
+ * conformance: make TestCommit and TestConformance parallel
+ * cirrus: reduce task timeout
+ * mkcw: mkcw_check_image use bats run_with_log
+ * test: use /tmp as TMPDIR
+ * heredoc: create temp subdirs for each build
+ * test: heredoc remove python dependency from test
+ * Support the containers.conf container_name_as_hostname option
+ * fix(deps): update module github.com/opencontainers/runc to v1.2.5
+ * fix(deps): update module github.com/spf13/cobra to v1.9.0
+ * .cirrus: use more cores for smoke
+ * Switch to the CNCF Code of Conduct
+ * .cirrus: bump ci resources
+ * fix(deps): update module golang.org/x/crypto to v0.33.0
+ * Distinguish --mount=type=cache locations by ownership, too
+ * fix(deps): update module golang.org/x/term to v0.29.0
+ * .cirrus: run -race only on non-PR branch
+ * unit: deparallize some tests
+ * .cirrus: use multiple cpu for unit tests
+ * Makefile: use -parallel for go test
+ * unit_test: use Parallel test where possible
+ * Update module golang.org/x/sys to v0.30.0
+ * Update module golang.org/x/sync to v0.11.0
+ * Update dependency containers/automation_images to v20250131
+ * Bump to Buildah v1.40.0-dev
+
+- Changelog for v1.39.0 (2025-01-31)
+ * Bump c/storage v1.57.1, c/image 5.34.0, c/common v0.62.0
+ * Update module github.com/containers/storage to v1.57.0
+ * CI, .cirrus: parallelize containerized integration
+ * ed's comment: cleanup
+ * use seperate blobinfocache for flaky test
+ * bump CI VMs to 4 CPUs (was: 2) for integration tests
+ * cleanup, debug, and disable parallel in blobcache tests
+ * bats tests - parallelize
+ * pkg/overlay: cleanups
+ * RPM: include check section to silence rpmlint
+ * RPM: use default gobuild macro on RHEL
+ * tests: remove masked /sys/dev/block check
+ * vendor to latest c/{common,image,storage}
+ * build, run: record hash or digest in image history
+ * Accept image names as sources for cache mounts
+ * Run(): always clean up options.ExternalImageMounts
+ * refactor: replace golang.org/x/exp with stdlib
+ * Update to c/image @main
+ * fix broken doc link
+ * run_freebsd.go: only import runtime-spec once
+ * fix(deps): update module github.com/docker/docker to v27.5.1+incompatible
+ * bump github.com/vbatts/tar-split
+ * Add more checks to the --mount flag parsing logic
+ * chroot mount flags integration test: copy binaries
+ * fix(deps): update module github.com/moby/buildkit to v0.19.0
+ * relabel(): correct a misleading parameter name
+ * Fix TOCTOU error when bind and cache mounts use "src" values
+ * define.TempDirForURL(): always use an intermediate subdirectory
+ * internal/volume.GetBindMount(): discard writes in bind mounts
+ * pkg/overlay: add a MountLabel flag to Options
+ * pkg/overlay: add a ForceMount flag to Options
+ * Add internal/volumes.bindFromChroot()
+ * Add an internal/open package
+ * fix(deps): update module github.com/containers/common to v0.61.1
+ * fix(deps): update module github.com/containers/image/v5 to v5.33.1
+ * [CI:DOCS] Touch up changelogs
+ * fix(deps): update module github.com/docker/docker to v27.5.0+incompatible
+ * copy-preserving-extended-attributes: use a different base image
+ * fix(deps): update github.com/containers/luksy digest to a3a812d
+ * chore(deps): update module golang.org/x/net to v0.33.0 [security]
+ * fix(deps): update module golang.org/x/crypto to v0.32.0
+ * New VM Images
+ * fix(deps): update module github.com/opencontainers/runc to v1.2.4
+ * fix(deps): update module github.com/docker/docker to v27.4.1+incompatible
+ * fix(deps): update module github.com/containers/ocicrypt to v1.2.1
+ * Add support for --security-opt mask and unmask
+ * Allow cache mounts to be stages or additional build contexts
+ * [skip-ci] RPM: cleanup changelog conditionals
+ * fix(deps): update module github.com/cyphar/filepath-securejoin to v0.3.6
+ * fix(deps): update module github.com/moby/buildkit to v0.18.2
+ * Fix an error message in the chroot unit test
+ * copier: use .PAXRecords instead of .Xattrs
+ * chroot: on Linux, try to pivot_root before falling back to chroot
+ * manifest add: add --artifact-annotation
+ * Add context to an error message
+ * Update module golang.org/x/crypto to v0.31.0
+ * Update module github.com/opencontainers/runc to v1.2.3
+ * Update module github.com/docker/docker to v27.4.0+incompatible
+ * Update module github.com/cyphar/filepath-securejoin to v0.3.5
+ * CI: don't build a binary in the unit tests task
+ * CI: use /tmp for $GOCACHE
+ * CI: remove dependencies on the cross-build task
+ * CI: run cross-compile task with make -j
+ * Update module github.com/docker/docker to v27.4.0-rc.4+incompatible
+ * Update module github.com/moby/buildkit to v0.18.1
+ * Update module golang.org/x/crypto to v0.30.0
+ * Update golang.org/x/exp digest to 2d47ceb
+ * Update github.com/opencontainers/runtime-tools digest to f7e3563
+ * [skip-ci] Packit: remove rhel copr build jobs
+ * [skip-ci] Packit: switch to fedora-all for copr
+ * Update module github.com/stretchr/testify to v1.10.0
+ * Update module github.com/moby/buildkit to v0.17.2
+ * Makefile: use `find` to detect source files
+ * Tests: make _prefetch() parallel-safe
+ * Update module github.com/opencontainers/runc to v1.2.2
+ * executor: allow to specify --no-pivot-root
+ * Update module github.com/moby/sys/capability to v0.4.0
+ * Makefile: mv codespell config to .codespellrc
+ * Fix some codespell errors
+ * Makefile,install.md: rm gopath stuff
+ * Makefile: rm targets working on ..
+ * build: rm exclude_graphdriver_devicemapper tag
+ * Makefile: rm unused var
+ * Finish updating to go 1.22
+ * CI VMs: bump again
+ * Bump to Buidah v1.39.0-dev
+ * stage_executor: set avoidLookingCache only if mounting stage
+ * imagebuildah: additionalContext is not a local built stage
+
+- Changelog for v1.38.0 (2024-11-08)
+ * Bump to c/common v0.61.0, c/image v5.33.0, c/storage v1.56.0
+ * fix(deps): update module golang.org/x/crypto to v0.29.0
+ * fix(deps): update module github.com/moby/buildkit to v0.17.1
+ * fix(deps): update module github.com/containers/storage to v1.56.0
+ * tests: skip two ulimit tests
+ * CI VMs: bump f40 -> f41
+ * tests/tools: rebuild tools when we change versions
+ * tests/tools: update golangci-lint to v1.61.0
+ * fix(deps): update module github.com/moby/buildkit to v0.17.0
+ * Handle RUN --mount with relative targets and no configured workdir
+ * tests: bud: make parallel-safe
+ * fix(deps): update module github.com/opencontainers/runc to v1.2.1
+ * fix(deps): update golang.org/x/exp digest to f66d83c
+ * fix(deps): update github.com/opencontainers/runtime-tools digest to 6c9570a
+ * tests: blobcache: use unique image name
+ * tests: sbom: never write to cwd
+ * tests: mkcw: bug fixes, refactor
+ * deps: bump runc to v1.2.0
+ * deps: switch to moby/sys/userns
+ * tests/test_runner.sh: remove some redundancies
+ * Integration tests: run git daemon on a random-but-bind()able port
+ * fix(deps): update module github.com/opencontainers/selinux to v1.11.1
+ * go.mod: remove unnecessary replace
+ * Document more buildah build --secret options
+ * Add support for COPY --exclude and ADD --exclude options
+ * fix(deps): update github.com/containers/luksy digest to e2530d6
+ * chore(deps): update dependency containers/automation_images to v20241010
+ * fix(deps): update module github.com/cyphar/filepath-securejoin to v0.3.4
+ * Properly validate cache IDs and sources
+ * [skip-ci] Packit: constrain koji job to fedora package to avoid dupes
+ * Audit and tidy OWNERS
+ * fix(deps): update module golang.org/x/crypto to v0.28.0
+ * tests: add quotes to names
+ * vendor: update c/common to latest
+ * CVE-2024-9407: validate "bind-propagation" flag settings
+ * vendor: switch to moby/sys/capability
+ * Don't set ambient capabilities
+ * Document that zstd:chunked is downgraded to zstd when encrypting
+ * fix(deps): update module github.com/cyphar/filepath-securejoin to v0.3.3
+ * buildah-manifest-create.1: Fix manpage section
+ * chore(deps): update dependency ubuntu to v24
+ * Make `buildah manifest push --all` true by default
+ * chroot: add newlines at the end of printed error messages
+ * Do not error on trying to write IMA xattr as rootless
+ * fix: remove duplicate conditions
+ * fix(deps): update module github.com/moby/buildkit to v0.16.0
+ * fix(deps): update module github.com/cyphar/filepath-securejoin to v0.3.2
+ * Document how entrypoint is configured in buildah config
+ * In a container, try to register binfmt_misc
+ * imagebuildah.StageExecutor: clean up volumes/volumeCache
+ * build: fall back to parsing a TARGETPLATFORM build-arg
+ * `manifest add --artifact`: handle multiple values
+ * Packit: split out ELN jobs and reuse fedora downstream targets
+ * Packit: Enable sidetags for bodhi updates
+ * fix(deps): update module github.com/docker/docker to v27.2.1+incompatible
+ * tests/bud.bats: add git source
+ * add: add support for git source
+ * Add support for the new c/common pasta options
+ * vendor latest c/common
+ * fix(deps): update module golang.org/x/term to v0.24.0
+ * fix(deps): update module github.com/fsouza/go-dockerclient to v1.12.0
+ * packit: update fedora and epel targets
+ * cirrus: disable f39 testing
+ * cirrus: fix fedora names
+ * update to go 1.22
+ * Vendor c/common:9d025e4cb348
+ * copier: handle globbing with "**" path components
+ * fix(deps): update golang.org/x/exp digest to 9b4947d
+ * fix(deps): update github.com/containers/luksy digest to 2e7307c
+ * imagebuildah: make scratch config handling toggleable
+ * fix(deps): update module github.com/docker/docker to v27.2.0+incompatible
+ * Add a validation script for Makefile $(SOURCES)
+ * fix(deps): update module github.com/openshift/imagebuilder to v1.2.15
+ * New VMs
+ * Update some godocs, use 0o to prefix an octal in a comment
+ * buildah-build.1.md: expand the --layer-label description
+ * fix(deps): update module github.com/containers/common to v0.60.2
+ * run: fix a nil pointer dereference on FreeBSD
+ * CI: enable the whitespace linter
+ * Fix some govet linter warnings
+ * Commit(): retry committing to local storage on storage.LayerUnknown
+ * CI: enable the gofumpt linter
+ * conformance: move weirdly-named files out of the repository
+ * fix(deps): update module github.com/docker/docker to v27.1.2+incompatible
+ * fix(deps): update module github.com/containers/common to v0.60.1
+ * *: use gofmt -s, add gofmt linter
+ * *: fix build tags
+ * fix(deps): update module github.com/containers/image/v5 to v5.32.1
+ * Add(): re-escape any globbed items that included escapes
+ * conformance tests: use mirror.gcr.io for most images
+ * unit tests: use test-specific policy.json and registries.conf
+ * fix(deps): update module golang.org/x/sys to v0.24.0
+ * Update to spun-out "github.com/containerd/platforms"
+ * Bump github.com/containerd/containerd
+ * test/tools/Makefile: duplicate the vendor-in-container target
+ * linters: unchecked error
+ * linters: don't end loop iterations with "else" when "then" would
+ * linters: unused arguments shouldn't have names
+ * linters: rename checkIdsGreaterThan5() to checkIDsGreaterThan5()
+ * linters: don't name variables "cap"
+ * `make lint`: use --timeout instead of --deadline
+ * Drop the e2e test suite
+ * fix(deps): update module golang.org/x/crypto to v0.26.0
+ * fix(deps): update module github.com/onsi/gomega to v1.34.1
+ * `make vendor-in-container`: use the caller's Go cache if it exists
+ * fix(deps): fix test/tools ginkgo typo
+ * fix(deps): update module github.com/onsi/ginkgo/v2 to v2.19.1
+ * Update to keep up with API changes in storage
+ * fix(deps): update github.com/containers/luksy digest to 1f482a9
+ * install: On Debian/Ubuntu, add installation of libbtrfs-dev
+ * fix(deps): update module golang.org/x/sys to v0.23.0
+ * fix(deps): update golang.org/x/exp digest to 8a7402a
+ * fix(deps): update module github.com/fsouza/go-dockerclient to v1.11.2
+ * Use Epoch: 2 and respect the epoch in dependencies.
+ * Bump to Buildah v1.38.0-dev
+ * AddAndCopyOptions: add CertPath, InsecureSkipTLSVerify, Retry fields
+ * Add PrependedLinkedLayers/AppendedLinkedLayers to CommitOptions
+ * integration tests: teach starthttpd() about TLS and pid files
+
+- Changelog for v1.37.0 (2024-07-26)
+ * Bump c/storage, c/image, c/common for v1.37.0
+ * "build with basename resolving user arg" tests: correct ARG use
+ * bud-multiple-platform-no-run test: correct ARG use
+ * imagebuildah: always have default values for $TARGET... args ready
+ * bump github.com/openshift/imagebuilder to v1.2.14
+ * fix(deps): update module github.com/docker/docker to v27.1.1+incompatible
+ * fix(deps): update module github.com/cyphar/filepath-securejoin to v0.3.1
+ * fix(deps): update module github.com/docker/docker to v27.1.0+incompatible
+ * CI: use local registry, part 2 of 2
+ * CI: use local registry, part 1 of 2
+ * fix(deps): update module github.com/fsouza/go-dockerclient to v1.11.1
+ * Revert "fix(deps): update github.com/containers/image/v5 to v5.31.1"
+ * Replace libimage.LookupReferenceFunc with the manifests version
+ * conformance tests: enable testing CompatVolumes
+ * conformance tests: add a test that tries to chown a volume
+ * imagebuildah: make traditional volume handling not the default
+ * StageExecutor.prepare(): mark base image volumes for preservation
+ * fix(deps): update module github.com/containers/image/v5 to v5.31.1
+ * Vendor in latest containers/(common, storage, image)
+ * fix(deps): update module golang.org/x/term to v0.22.0
+ * fix(deps): update module golang.org/x/sys to v0.22.0
+ * fix(deps): update golang.org/x/exp digest to 7f521ea
+ * fix(deps): update github.com/containers/luksy digest to a8846e2
+ * imagebuildah.StageExecutor.Copy(): reject new flags for now
+ * bump github.com/openshift/imagebuilder to v1.2.11
+ * Rework parsing of --pull flags
+ * fix(deps): update module github.com/containers/image/v5 to v5.31.1
+ * imagebuildah.StageExecutor.prepare(): log the --platform flag
+ * CI VMs: bump
+ * buildah copy: preserve owner info with --from= a container or image
+ * conformance tests: enable testing CompatSetParent
+ * containerImageRef.NewImageSource(): move the FROM comment to first
+ * commit: set "parent" for docker format only when requested
+ * Update godoc for Builder.EnsureContainerPathAs
+ * fix(deps): update module github.com/spf13/cobra to v1.8.1
+ * fix(deps): update module github.com/containernetworking/cni to v1.2.0
+ * fix(deps): update module github.com/opencontainers/runc to v1.1.13
+ * Change default for podman build to --pull missing
+ * fix(deps): update module github.com/containers/common to v0.59.1
+ * Clarify definition of --pull options
+ * buildah: fix a nil pointer reference on FreeBSD
+ * Use /var/tmp for $TMPDIR for vfs conformance jobs
+ * Cirrus: run `df` during job setup
+ * conformance: use quay.io/libpod/centos:7 instead of centos:8
+ * Stop setting "parent" in docker format
+ * conformance: check if workdir trims path separator suffixes
+ * push integration test: pass password to docker login via stdin
+ * Re-enable the "copy with chown" conformance test
+ * healthcheck: Add support for `--start-interval`
+ * fix(deps): update module github.com/docker/docker to v26.1.4+incompatible
+ * fix(deps): update module github.com/containerd/containerd to v1.7.18
+ * tests: set _CONTAINERS_USERNS_CONFIGURED=done for libnetwork
+ * Cross-build on Fedora
+ * Drop copyStringSlice() and copyStringStringMap()
+ * fix(deps): update module golang.org/x/crypto to v0.24.0
+ * fix(deps): update module github.com/openshift/imagebuilder to v1.2.10
+ * Provide an uptime_netbsd.go
+ * Spell unix as "!windows"
+ * Add netbsd to lists-of-OSes
+ * fix(deps): update golang.org/x/exp digest to fd00a4e
+ * [skip-ci] Packit: enable c10s downstream sync
+ * CI VMs: bump, to debian with cgroups v2
+ * Document when BlobDirectory is overridden
+ * fix secret mounts for env vars when using chroot isolation
+ * Change to take a types.ImageReference arg
+ * imagebuildah: Support custom image reference lookup for cache push/pull
+ * fix(deps): update module github.com/onsi/ginkgo/v2 to v2.19.0
+ * Bump to v1.37.0-dev
+ * CI: Clarify Debian use for conformance tests
+
+- Changelog for v1.36.0 (2024-05-23)
+ * build: be more selective about specifying the default OS
+ * Bump to c/common v0.59.0
+ * Fix buildah prune --help showing the same example twice
+ * fix(deps): update module github.com/onsi/ginkgo/v2 to v2.18.0
+ * fix(deps): update module github.com/containers/image/v5 to v5.31.0
+ * bud tests: fix breakage when vendoring into podman
+ * Integration tests: fake up a replacement for nixery.dev/shell
+ * copierWithSubprocess(): try to capture stderr on io.ErrClosedPipe
+ * Don't expand RUN heredocs ourselves, let the shell do it
+ * Don't leak temp files on failures
+ * Add release note template to split dependency chores
+ * fix CentOS/RHEL build - no BATS there
+ * fix(deps): update module github.com/containers/luksy to v0.0.0-20240506205542-84b50f50f3ee
+ * Address CVE-2024-3727
+ * chore(deps): update module github.com/opencontainers/runtime-spec to v1.2.0
+ * Builder.cdiSetupDevicesInSpecdefConfig(): use configured CDI dirs
+ * Setting --arch should set the TARGETARCH build arg
+ * fix(deps): update module golang.org/x/exp to v0.0.0-20240416160154-fe59bbe5cc7f
+ * [CI:DOCS] Add link to Buildah image page to README.md
+ * Don't set GOTOOLCHAIN=local
+ * fix(deps): update module github.com/cyphar/filepath-securejoin to v0.2.5
+ * Makefile: set GOTOOLCHAIN=local
+ * Integration tests: switch some base images
+ * containerImageRef.NewImageSource: merge the tar filters
+ * fix(deps): update module github.com/onsi/ginkgo/v2 to v2.17.2
+ * fix(deps): update module github.com/containers/luksy to v0.0.0-20240408185936-afd8e7619947
+ * Disable packit builds for centos-stream+epel-next-8
+ * Makefile: add missing files to $(SOURCES)
+ * CI VMs: bump to new versions with tmpfs /tmp
+ * chore(deps): update module golang.org/x/net to v0.23.0 [security]
+ * integration test: handle new labels in "bud and test --unsetlabel"
+ * Switch packit configuration to use epel-9-$arch ...
+ * Give unit tests a bit more time
+ * Integration tests: remove a couple of duplicated tests
+ * Integration tests: whitespace tweaks
+ * Integration tests: don't remove images at start or end of test
+ * Integration tests: use cached images more
+ * Integration tests _prefetch: use registry configs
+ * internal: use fileutils.(Le|E)xists
+ * pkg/parse: use fileutils.(Le|E)xists
+ * buildah: use fileutils.(Le|E)xists
+ * chroot: use fileutils.(Le|E)xists
+ * vendor: update containers/(common|storage)
+ * Fix issue/pr lock workflow
+ * [CI:DOCS] Add golang 1.21 update warning
+ * heredoc: honor inline COPY irrespective of ignorefiles
+ * Update install.md
+ * source-push: add support for --digestfile
+ * Fix caching when mounting a cached stage with COPY/ADD
+ * fix(deps): update github.com/containers/luksy digest to 3d2cf0e
+ * Makefile: softcode `strip`, use it from env var
+ * Man page updates
+ * Add support for passing CDI specs to --device
+ * Update comments on some API objects
+ * pkg/parse.DeviceFromPath(): dereference src symlinks
+ * fix(deps): update module github.com/onsi/ginkgo/v2 to v2.17.1
+
+- Changelog for v1.35.0 (2024-03-06)
+ * fix(deps): update module github.com/stretchr/testify to v1.9.0
+ * cgroups: reuse version check from c/common
+ * Update vendor of containers/(common,image)
+ * fix(deps): update github.com/containers/storage digest to eadc620
+ * fix(deps): update github.com/containers/luksy digest to ceb12d4
+ * fix(deps): update github.com/containers/image/v5 digest to cdc6802
+ * manifest add: complain if we get artifact flags without --artifact
+ * Use retry logic from containers/common
+ * Vendor in containers/(storage,image,common)
+ * Update module golang.org/x/crypto to v0.20.0
+ * Add comment re: Total Success task name
+ * tests: skip_if_no_unshare(): check for --setuid
+ * Properly handle build --pull=false
+ * [skip-ci] Update tim-actions/get-pr-commits action to v1.3.1
+ * Update module go.etcd.io/bbolt to v1.3.9
+ * Revert "Reduce official image size"
+ * Update module github.com/opencontainers/image-spec to v1.1.0
+ * Reduce official image size
+ * Build with CNI support on FreeBSD
+ * build --all-platforms: skip some base "image" platforms
+ * Bump main to v1.35.0-dev
+ * Vendor in latest containers/(storage,image,common)
+ * Split up error messages for missing --sbom related flags
+ * `buildah manifest`: add artifact-related options
+ * cmd/buildah/manifest.go: lock lists before adding/annotating/pushing
+ * cmd/buildah/manifest.go: don't make struct declarations aliases
+ * Use golang.org/x/exp/slices.Contains
+ * Disable loong64 again
+ * Fix a couple of typos in one-line comments
+ * egrep is obsolescent; use grep -E
+ * Try Cirrus with a newer VM version
+ * Set CONTAINERS_CONF in the chroot-mount-flags integration test
+ * Update to match dependency API update
+ * Update github.com/openshift/imagebuilder and containers/common
+ * docs: correct default authfile path
+ * fix(deps): update module github.com/containerd/containerd to v1.7.13
+ * tests: retrofit test for heredoc summary
+ * build, heredoc: show heredoc summary in build output
+ * manifest, push: add support for --retry and --retry-delay
+ * fix(deps): update github.com/openshift/imagebuilder digest to b767bc3
+ * imagebuildah: fix crash with empty RUN
+ * fix(deps): update github.com/containers/luksy digest to b62d551
+ * fix(deps): update module github.com/opencontainers/runc to v1.1.12 [security]
+ * fix(deps): update module github.com/moby/buildkit to v0.12.5 [security]
+ * Make buildah match podman for handling of ulimits
+ * docs: move footnotes to where they're applicable
+ * Allow users to specify no-dereference
+ * Run codespell on code
+ * Fix FreeBSD version parsing
+ * Fix a build break on FreeBSD
+ * Remove a bad FROM line
+ * fix(deps): update module github.com/onsi/gomega to v1.31.1
+ * fix(deps): update module github.com/opencontainers/image-spec to v1.1.0-rc6
+ * docs: use reversed logo for dark theme in README
+ * build,commit: add --sbom to scan and produce SBOMs when committing
+ * commit: force omitHistory if the parent has layers but no history
+ * docs: fix a couple of typos
+ * internal/mkcw.Archive(): handle extra image content
+ * stage_executor,heredoc: honor interpreter in heredoc
+ * stage_executor,layers: burst cache if heredoc content is changed
+ * fix(deps): update module golang.org/x/crypto to v0.18.0
+ * Replace map[K]bool with map[K]struct{} where it makes sense
+ * fix(deps): update module golang.org/x/sync to v0.6.0
+ * fix(deps): update module golang.org/x/term to v0.16.0
+ * Bump CI VMs
+ * Replace strings.SplitN with strings.Cut
+ * fix(deps): update github.com/containers/storage digest to ef81e9b
+ * fix(deps): update github.com/containers/image/v5 digest to 1b221d4
+ * fix(deps): update module github.com/fsouza/go-dockerclient to v1.10.1
+ * Document use of containers-transports values in buildah
+ * fix(deps): update module golang.org/x/crypto to v0.17.0 [security]
+ * chore(deps): update dependency containers/automation_images to v20231208
+ * manifest: addCompression use default from containers.conf
+ * commit: add a --add-file flag
+ * mkcw: populate the rootfs using an overlay
+ * chore(deps): update dependency containers/automation_images to v20230517
+ * [skip-ci] Update actions/stale action to v9
+ * fix(deps): update module github.com/containernetworking/plugins to v1.4.0
+ * fix(deps): update github.com/containers/image/v5 digest to 7a40fee
+ * Bump to v1.34.1-dev
+ * Ignore errors if label.Relabel returns ENOSUP
+
+- Changelog for v1.34.0 (2023-12-11)
+ * vendor: update c/{common,image,storage}
+ * run: Allow using just one jail per container on FreeBSD
+ * Remove makefile targets entrypoint{,.gz} for non x86_64
+
+- Changelog for v1.33.2 (2023-11-22)
+ * Update minimum to golang 1.20
+ * fix(deps): update module github.com/fsouza/go-dockerclient to v1.10.0
+ * fix(deps): update module github.com/moby/buildkit to v0.12.3
+ * Bump to v1.33.2-dev
+
+- Changelog for v1.33.1 (2023-11-18)
+ * fix(deps): update module github.com/moby/buildkit to v0.11.4 [security]
+ * test,heredoc: use fedora instead of docker.io/library/python:latest
+ * Bump to v1.33.1-dev
+
+- Changelog for v1.33.0 (2023-11-17)
+ * Never omit layers for emptyLayer instructions when squashing/cwing
+ * Add OverrideChanges and OverrideConfig to CommitOptions
+ * buildah: add heredoc support for RUN, COPY and ADD
+ * vendor: bump imagebuilder to v1.2.6-0.20231110114814-35a50d57f722
+ * conformance tests: archive the context directory as 0:0 (#5171)
+ * blobcacheinfo,test: blobs must be resued when pushing across registry
+ * Bump c/storage v1.51.0, c/image v5.29.0, c/common v0.57.0
+ * pkg/util.MirrorToTempFileIfPathIsDescriptor(): don't leak an fd
+ * StageExecutor.Execute: force a commit for --unsetenv, too
+ * Increase a copier+chroot test timeout
+ * Add support for --compat-auth-file in login/logout
+ * Update existing tests for error message change
+ * Update c/image and c/common to latest
+ * fix(deps): update module github.com/containerd/containerd to v1.7.9
+ * build: downgrade to go 1.20
+ * Add godoc for pkg/parse.GetTempDir
+ * conformance tests: use go-dockerclient for BuildKit builds
+ * Make TEE types case-insensitive
+ * fix(deps): update module golang.org/x/crypto to v0.15.0
+ * Tweak some help descriptions
+ * Stop using DefaultNetworkSysctl and use containers.conf only
+ * Implement ADD checksum flag #5135
+ * vendor of openshift/imagebuilder #5135
+ * Pass secrets from the host down to internal podman containers
+ * Update cirrus and version of golang
+ * image: replace GetStoreImage with ResolveReference
+ * vendor: bump c/image to 373c52a9466f
+ * pkg/parse.Platform(): minor simplification
+ * createConfigsAndManifests: clear history before cw-specific logic
+ * Use a constant definition instead of "scratch"
+ * conformance: use require.NoErrorf() more
+ * fix(deps): update module golang.org/x/term to v0.14.0
+ * fix(deps): update module golang.org/x/sync to v0.5.0
+ * fix(deps): update module github.com/spf13/cobra to v1.8.0
+ * fix(deps): update module golang.org/x/sys to v0.14.0
+ * fix(deps): update github.com/containers/common digest to 8354404
+ * fix(deps): update module github.com/opencontainers/runc to v1.1.10
+ * fix(deps): update github.com/containers/luksy digest to b5a7f79
+ * Log the platform for build errors during multi-platform builds
+ * Use mask definitions from containers/common
+ * Vendor in latest containers/common
+ * fix(deps): update module github.com/containerd/containerd to v1.7.8
+ * fix(deps): update module go.etcd.io/bbolt to v1.3.8
+ * container.conf: support attributed string slices
+ * fix(deps): update module sigs.k8s.io/yaml to v1.4.0
+ * Use cutil.StringInSlice rather then contains
+ * Add --no-hostname option to buildah containers
+ * vendor c/common: appendable containers.conf strings, Part 1
+ * fix(deps): update module github.com/onsi/gomega to v1.28.1
+ * chroot.setupChrootBindMounts: pay more attention to flags
+ * chore(deps): update dependency containers/automation_images to v20231004
+ * Vendor containers/common
+ * chore(deps): update module golang.org/x/net to v0.17.0 [security]
+ * run: use internal.GetTempDir with os.MkdirTemp
+ * fix(deps): update module github.com/containerd/containerd to v1.7.7
+ * imagebuildah,multi-stage: do not remove base images
+ * gitignore: add mkcw binary
+ * mkcw: remove entrypoint binaries
+ * fix(deps): update module golang.org/x/crypto to v0.14.0
+ * fix(deps): update module golang.org/x/sys to v0.13.0
+ * fix(deps): update module golang.org/x/sync to v0.4.0
+ * Update some comments related to confidential workload
+ * Use the parent's image ID in the config that we pass to imagebuilder
+ * fix(deps): update github.com/containers/common digest to 8892536
+ * fix(deps): update github.com/containers/luksy digest to 6df88cb
+ * bug: Ensure the mount type is always BindMount by default
+ * Protocol can be specified with --port. Ex. --port 514/udp
+ * fix(deps): update module github.com/onsi/gomega to v1.28.0
+ * build,config: add support for --unsetlabel
+ * tests/bud: add tests
+ * [CI:BUILD] Packit: tag @containers/packit-build on copr build failures
+ * stage_executor: allow images without layers
+ * vendor of containers/common
+ * Removing selinux_tag.sh as no longer needed after 580356f [NO NEW TESTS NEEDED]
+ * add/copy: make sure we handle relative path names correctly
+ * fix(deps): update module github.com/opencontainers/image-spec to v1.1.0-rc5
+ * Bump to v1.33.0-dev
+ * imagebuildah: consider ignorefile with --build-context
+
+- Changelog for v1.32.0 (2023-09-14)
+ * GetTmpDir is not using ImageCopyTmpdir correctly
+ * Run codespell on code
+ * Bump vendor containers/(common, storage, image)
+ * Cirrus: Remove multi-arch buildah image builds
+ * fix(deps): update module github.com/containerd/containerd to v1.7.6
+ * Split GetTempDir from internal/util
+ * Move most of internal/parse to internal/volumes
+ * copier: remove libimage dependency via util package
+ * Add some docs for `build --cw`, `commit --cw`, and `mkcw`
+ * Add `buildah mkcw`, add `--cw` to `buildah commit` and `buildah build`
+ * Make sure that pathnames picked up from the environment are absolute
+ * fix(deps): update module github.com/cyphar/filepath-securejoin to v0.2.4
+ * fix(deps): update module github.com/docker/docker to v24.0.6+incompatible
+ * Don't try to look up names when committing images
+ * fix(deps): update module golang.org/x/crypto to v0.13.0
+ * docs: use valid github repo
+ * fix(deps): update module golang.org/x/sys to v0.12.0
+ * vendor containers/common@12405381ff45
+ * push: --force-compression should be true with --compression-format
+ * Update module github.com/containerd/containerd to v1.7.5
+ * [skip-ci] Update tim-actions/commit-message-checker-with-regex action to v0.3.2
+ * docs: add reference to oci-hooks
+ * Support passing of ULimits as -1 to mean max
+ * GHA: Attempt to fix discussion_lock workflow
+ * Fixing the owner of the storage.conf.
+ * pkg/chrootuser: Ignore comments when parsing /etc/group on FreeBSD
+ * Use buildah repo rather then podman repo
+ * GHA: Closed issue/PR comment-lock test
+ * fix(deps): update module github.com/containers/storage to v1.49.0
+ * chore(deps): update dependency containers/automation_images to v20230816
+ * Replace troff code with markdown in buildah-{copy,add}.1.md
+ * [CI:BUILD] rpm: spdx compatible license field
+ * executor: build-arg warnings must honor global args
+ * fix(deps): update module github.com/containers/ocicrypt to v1.1.8
+ * chroot: `setSeccomp` add support for `ArchPARISC(64)` and `ArchRISCV64`
+ * make,cross: restore loong64
+ * Clear CommonBuildOpts when loading Builder status
+ * buildah/push/manifest-push: add support for --force-compression
+ * vendor: bump c/common to v0.55.1-0.20230811093040-524b4d5c12f9
+ * chore(deps): update dependency containers/automation_images to v20230809
+ * [CI:BUILD] RPM: fix buildtags
+ * fix(deps): update module github.com/opencontainers/runc to v1.1.9
+ * chore(deps): update dependency ubuntu to v22
+ * chore(deps): update dependency containers/automation_images to v20230807
+ * [CI:BUILD] Packit: add fedora-eln targets
+ * [CI:BUILD] RPM: build docs with vendored go-md2man
+ * packit: Build PRs into default packit COPRs
+ * Update install.md
+ * Update install.md changes current Debian stable version name
+ * fix(deps): update module golang.org/x/term to v0.11.0
+ * fix(deps): update module golang.org/x/crypto to v0.12.0
+ * tests: fix layer-label tests
+ * buildah: add --layer-label for setting labels on layers
+ * Cirrus: container/rootless env. var. passthrough
+ * Cirrus: Remove duplicate env. var. definitions
+ * fix(deps): update github.com/containers/storage digest to c3da76f
+ * Add a missing .Close() call on an ImageSource
+ * Create only a reference when that's all we need
+ * Add a missing .Close() call on an ImageDestination
+ * CI:BUILD] RPM: define gobuild macro for rhel/centos stream
+ * manifest/push: add support for --add-compression
+ * manifest/inspect: add support for tls-verify and authfile
+ * vendor: bump c/common to v0.55.1-0.20230727095721-647ed1d4d79a
+ * vendor: bump c/image to v5.26.1-0.20230726142307-8c387a14f4ac
+ * fix(deps): update module github.com/containerd/containerd to v1.7.3
+ * fix(deps): update module github.com/onsi/gomega to v1.27.10
+ * fix(deps): update module github.com/docker/docker to v24.0.5+incompatible
+ * fix(deps): update module github.com/containers/image/v5 to v5.26.1
+ * fix(deps): update module github.com/opencontainers/runtime-spec to v1.1.0
+ * Update vendor of containers/(storage,image,common)
+ * fix(deps): update module github.com/opencontainers/runc to v1.1.8
+ * [CI:BUILD] Packit: remove pre-sync action
+ * fix(deps): update module github.com/containers/common to v0.55.2
+ * [CI:BUILD] Packit: downstream task script needs GOPATH
+ * Vendor in containers/(common, image, storage)
+ * fix(deps): update module golang.org/x/term to v0.10.0
+ * [CI:BUILD] Packit: fix pre-sync action for downstream tasks
+ * contrib/buildahimage: set config correctly for rootless build user
+ * fix(deps): update module github.com/opencontainers/image-spec to v1.1.0-rc4
+ * Bump to v1.32.0-dev
+ * Update debian install instructions
+ * pkg/overlay: add limited support for FreeBSD
+
+- Changelog for v1.31.0 (2023-06-30)
+ * Bump c/common to 0.55.1 and c/image to 5.26.1
+ * Bump c/image to 5.26.0 and c/common to 0.54.0
+ * vendor: update c/{common,image,storage} to latest
+ * chore: pkg imported more than once
+ * buildah: add pasta(1) support
+ * use slirp4netns package from c/common
+ * update c/common to latest
+ * add hostname to /etc/hosts when running with host network
+ * vendor: update c/common to latest
+ * [CI:BUILD] Packit: add jobs for downstream Fedora package builds
+ * fix(deps): update module golang.org/x/sync to v0.3.0
+ * fix(deps): update module golang.org/x/crypto to v0.10.0
+ * Add smoke tests for encryption CLI helpers
+ * fix(deps): update module golang.org/x/term to v0.9.0
+ * fix(deps): update module github.com/opencontainers/runtime-spec to v1.1.0-rc.3
+ * Remove device mapper support
+ * Remove use of deprecated tar.TypeRegA
+ * Update tooling to support newer golangci-lint
+ * Make cli.EncryptConfig,DecryptConfig, GetFormat public
+ * Don't decrypt images by default
+ * fix(deps): update module github.com/onsi/gomega to v1.27.8
+ * fix(deps): update github.com/containers/storage digest to 3f3fb2f
+ * Renovate: Don't touch fragile test stuffs
+ * [CI:DOCS] Update comment to remove ambiguity
+ * fix(deps): update github.com/containers/image/v5 digest to abe5133
+ * fix(deps): update module github.com/sirupsen/logrus to v1.9.3
+ * fix(deps): update module github.com/containerd/containerd to v1.7.2
+ * Explicitly ref. quay images for CI
+ * At startup, log the effective capabilities for debugging
+ * parse: use GetTempDir from internal utils
+ * GetTmpDir: honor image_copy_tmp_dir from containers.conf
+ * docs/Makefile: don't show sed invocations
+ * CI: Support testing w/ podman-next COPR packages
+ * intermediate-images inherit-label test: make it debuggable
+ * fix(deps): update github.com/containers/common digest to 462ccdd
+ * Add a warning to `--secret` docs
+ * vendor: bump c/storage to v1.46.2-0.20230526114421-55ee2d19292f
+ * executor: apply label to only final stage
+ * remove registry.centos.org
+ * Go back to setting SysProcAttr.Pdeathsig for child processes
+ * Fix auth.json path (validated on Fedora 38) wq Signed-off-by: Andreas Mack
+ * fix(deps): update module github.com/stretchr/testify to v1.8.3
+ * CI: fix test broken by renovatebot
+ * chore(deps): update quay.io/libpod/testimage docker tag to v20221018
+ * fix(deps): update module github.com/onsi/gomega to v1.27.7
+ * test: use debian instead of docker.io/library/debian:testing-slim
+ * vendor: bump logrus to 1.9.2
+ * [skip-ci] Update tim-actions/get-pr-commits action to v1.3.0
+ * Revert "Proof of concept: nightly dependency treadmill"
+ * fix(deps): update module github.com/sirupsen/logrus to v1.9.1
+ * vendor in containers/(common,storage,image)
+ * fix(deps): update module github.com/docker/distribution to v2.8.2+incompatible
+ * run: drop Pdeathsig
+ * chroot: lock thread before setPdeathsig
+ * tests: add a case for required=false
+ * fix(deps): update module github.com/openshift/imagebuilder to v1.2.5
+ * build: validate volumes on backend
+ * secret: accept required flag w/o value
+ * fix(deps): update module github.com/containerd/containerd to v1.7.1
+ * fix(deps): update module golang.org/x/crypto to v0.9.0
+ * Update the demos README file to fix minor typos
+ * fix(deps): update module golang.org/x/sync to v0.2.0
+ * fix(deps): update module golang.org/x/term to v0.8.0
+ * manifest, push: use source as destination if not specified
+ * run,mount: remove path only if they didnt pre-exist
+ * Cirrus: Fix meta task failing to find commit
+ * parse: filter edge-case for podman-remote
+ * fix(deps): update module github.com/opencontainers/runc to v1.1.7
+ * fix(deps): update module github.com/docker/docker to v23.0.5+incompatible
+ * build: --platform must accept only arch
+ * fix(deps): update module github.com/containers/common to v0.53.0
+ * makefile: increase conformance timeout
+ * Cap suffixDigitsModulo to a 9-digits suffix.
+ * Rename conflict to suffixDigitsModulo
+ * fix(deps): update module github.com/opencontainers/runtime-spec to v1.1.0-rc.2
+ * fix(deps): update module github.com/opencontainers/runc to v1.1.6
+ * chore(deps): update centos docker tag to v8
+ * Clarify the need for qemu-user-static package
+ * chore(deps): update quay.io/centos/centos docker tag to v8
+ * Renovate: Ensure test/tools/go.mod is managed
+ * Revert "buildah image should not enable fuse-overlayfs for rootful mode"
+ * Bump to v1.31.0-dev
+ * parse: add support for relabel bind mount option
+
+- Changelog for v1.30.0 (2023-04-06)
+ * fix(deps): update module github.com/opencontainers/runc to v1.1.5
+ * fix(deps): update module github.com/fsouza/go-dockerclient to v1.9.7
+ * buildah image should not enable fuse-overlayfs for rootful mode
+ * stage_executor: inline network add default string
+ * fix(deps): update module github.com/containers/common to v0.51.2
+ * chore(deps): update dependency containers/automation_images to v20230330
+ * fix(deps): update module github.com/docker/docker to v23.0.2+incompatible
+ * chore(deps): update dependency containers/automation_images to v20230320
+ * fix(deps): update module github.com/onsi/gomega to v1.27.6
+ * fix(deps): update github.com/opencontainers/runtime-tools digest to e931285
+ * [skip-ci] Update actions/stale action to v8
+ * test: don't allow to override io.buildah.version
+ * executor: only apply label on the final stage
+ * Update docs/buildah-build.1.md
+ * update build instruction for Ubuntu
+ * code review
+ * build: accept arguments from file with --build-arg-file
+ * run_linux: Update heuristic for mounting /sys
+ * [CI:BUILD] Packit: Enable Copr builds on PR and commit to main
+ * fix(deps): update module github.com/fsouza/go-dockerclient to v1.9.6
+ * Update to Go 1.18
+ * Disable dependabot in favor of renovate
+ * chore(deps): update dependency containers/automation_images to v20230314
+ * Fix requiring tests on Makefile changes
+ * Vendor in latest containers/(storage, common, image)
+ * imagebuildah: set len(short_image_id) to 12
+ * Re-enable conformance tests
+ * Skip conformance test failures with Docker 23.0.1
+ * Cirrus: Replace Ubuntu -> Debian SID
+ * run: add support for inline --network in RUN stmt
+ * vendor: bump imagebuilder to a3c3f8358ca31b1e4daa6
+ * stage_executor: attempt to push cache only when cacheKey is valid
+ * Add "ifnewer" as option in help message for pull command
+ * build: document behaviour of buildah's distributed cache
+ * fix(deps): update module golang.org/x/term to v0.6.0
+ * Add default list of capabilities required to run buildah in a container
+ * executor,copy: honor default ARG value while eval stage
+ * sshagent: use ExtendedAgent instead of Agent
+ * tests/bud: remove unwated test
+ * executor: do not warn on builtin default args
+ * executor: don't warn about unused TARGETARCH,TARGETOS,TARGETPLATFORM
+ * Fix tutorial for rootless mode
+ * Vendor in latest containers/(common, storage, image)
+ * Ignore the base image's base image annotations
+ * fix(deps): update module github.com/fsouza/go-dockerclient to v1.9.5
+ * build(deps): bump github.com/containers/storage from 1.45.3 to 1.45.4
+ * Vendor in latest containers/common
+ * docs/tutorials/04: add defaults for Run()
+ * imagebuildah.StageExecutor: suppress bogus "Pushing cache []:..."
+ * executor: also add stage with no children to cleanupStages
+ * [CI:BUILD] copr: fix el8 builds
+ * Fix documentation on which Capabilities are allowed by default
+ * Skip subject-length validation for renovate PRs
+ * Temporarily hard-skip bud-multiple-platform-values test
+ * fix(deps): update github.com/openshift/imagebuilder digest to 86828bf
+ * build(deps): bump github.com/containerd/containerd from 1.6.16 to 1.6.17
+ * build(deps): bump tim-actions/get-pr-commits from 1.1.0 to 1.2.0
+ * build(deps): bump github.com/containers/image/v5 from 5.24.0 to 5.24.1
+ * [skip-ci] Update tim-actions/get-pr-commits digest to 55b867b
+ * build(deps): bump github.com/opencontainers/selinux
+ * build(deps): bump golang.org/x/crypto from 0.5.0 to 0.6.0
+ * Add renovate configuration
+ * Run codespell on codebase
+ * login: support interspersed args for password
+ * conformance: use scratch for minimal test
+ * pkg/parse: expose public CleanCacheMount API
+ * build(deps): bump go.etcd.io/bbolt from 1.3.6 to 1.3.7
+ * build(deps): bump github.com/containerd/containerd from 1.6.15 to 1.6.16
+ * docs: specify order preference for FROM
+ * Bump to v1.30.0-dev
+
+- Changelog for v1.29.0 (2023-01-25)
+ * tests: improve build-with-network-test
+ * Bump c/storagev1.45.3, c/imagev5.24.0, c/commonv0.51.0
+ * build(deps): bump github.com/onsi/gomega from 1.25.0 to 1.26.0
+ * Flake 3710 has been closed. Reenable the test.
+ * [CI:DOCS] Fix two diversity issues in a tutorial
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.9.2 to 1.9.3
+ * vendor in latests containers/(storage, common, image)
+ * fix bud-multiple-platform-with-base-as-default-arg flake
+ * stage_executor: while mounting stages use freshly built stage
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.9.0 to 1.9.2
+ * build(deps): bump github.com/onsi/gomega from 1.24.2 to 1.25.0
+ * vendor in latests containers/(storage, common, image, ocicyrpt)
+ * [Itests: change the runtime-flag test for crun
+ * [CI:DOCS] README: drop sudo
+ * Fix multi-arch manifest-list build timeouts
+ * Cirrus: Update VM Images
+ * bud: Consolidate multiple synthetic LABEL instructions
+ * build, secret: allow realtive mountpoints wrt to work dir
+ * fixed squash documentation
+ * build(deps): bump github.com/containerd/containerd from 1.6.14 to 1.6.15
+ * Correct minor comment
+ * Vendor in latest containers/(common, image, storage)
+ * system tests: remove unhelpful assertions
+ * buildah: add prune command and expose CleanCacheMount API
+ * vendor: bump c/storage to a747b27
+ * Add support for --group-add to buildah from
+ * build(deps): bump actions/stale from 6 to 7
+ * Add documentation for buildah build --pull=missing
+ * build(deps): bump github.com/containerd/containerd from 1.6.12 to 1.6.14
+ * build(deps): bump github.com/docker/docker
+ * parse: default ignorefile must not point to symlink outside context
+ * buildah: wrap network setup errors
+ * build, mount: allow realtive mountpoints wrt to work dir
+ * Update to F37 CI VM Images, re-enable prior-fedora
+ * Update vendor or containers/(image, storage, common)
+ * build(deps): bump golang.org/x/crypto from 0.3.0 to 0.4.0
+ * Update contact information
+ * build(deps): bump golang.org/x/term from 0.2.0 to 0.3.0
+ * Replace io/ioutil calls with os calls
+ * [skip-ci] GHA/Cirrus-cron: Fix execution order
+ * Vendor in containers/common
+ * build(deps): bump golang.org/x/sys from 0.2.0 to 0.3.0
+ * remote-cache: support multiple sources and destinations
+ * Update c/storage after https://github.com/containers/storage/pull/1436
+ * util.SortMounts(): make the returned order more stable
+ * version: Bump to 1.29.0-dev
+ * [CI:BUILD] Cirrus: Migrate OSX task to M1
+ * Update vendor of containers/(common, storage, image)
+ * mount=type=cache: seperate cache parent on host for each user
+ * Fix installation instructions for Gentoo Linux
+ * build(deps): bump github.com/containerd/containerd from 1.6.9 to 1.6.10
+ * GHA: Reuse both cirrus rerun and check workflows
+ * Vendor in latest containers/(common,image,storage)
+ * build(deps): bump github.com/onsi/gomega from 1.24.0 to 1.24.1
+ * copier.Put(): clear up os/syscall mode bit confusion
+ * build(deps): bump golang.org/x/sys from 0.1.0 to 0.2.0
+ * Use TypeBind consistently to name bind/nullfs mounts
+ * Add no-new-privileges flag
+ * Update vendor of containers/(common, image, storage)
+ * imagebuildah:build with --all-platforms must honor args for base images
+ * codespell code
+ * Expand args and env when using --all-platforms
+ * build(deps): bump github.com/onsi/gomega from 1.23.0 to 1.24.0
+ * GHA: Simplify Cirrus-Cron check slightly
+ * Stop using ubi8
+ * remove unnecessary (hence misleading) rmi
+ * chroot: fix mounting of ro bind mounts
+ * executor: honor default ARG value while eval base name
+ * userns: add arbitrary steps/stage to --userns=auto test
+ * Don't set allow.mount in the vnet jail on Freebsd
+ * copier: Preserve file flags when copying archives on FreeBSD
+ * Remove quiet flag, so that it works in podman-remote
+ * test: fix preserve rootfs with --mount for podman-remote
+ * test: fix prune logic for cache-from after adding content summary
+ * vendor in latest containers/(storage, common, image)
+ * Fix RUN --mount=type=bind,from= not preserving rootfs of stage
+ * Define and use a safe, reliable test image
+ * Fix word missing in Container Tools Guide
+ * Makefile: Use $(MAKE) to start sub-makes in install.tools
+ * imagebuildah: pull cache from remote repo after adding content summary
+ * Makefile: Fix install on FreeBSD
+ * Ensure the cache volume locks are unlocked on all paths
+ * Vendor in latest containers/(common,storage)
+ * Simplify the interface of GetCacheMount and getCacheMount
+ * Fix cache locks with multiple mounts
+ * Remove calls to Lockfile.Locked()
+ * Maintain cache mount locks as lock objects instead of paths
+ * test: cleaning cache must not clean lockfiles
+ * run: honor lockfiles for multiple --mount instruction
+ * mount,cache: lockfiles must not be part of users cache content
+ * Update vendor containers/(common,image,storage)
+ * [CI:BUILD] copr: buildah rpm should depend on containers-common-extra
+ * pr-should-include-tests: allow specfile, golangci
+ * build(deps): bump dawidd6/action-send-mail from 3.7.0 to 3.7.1
+ * build(deps): bump github.com/docker/docker
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.8.3 to 1.9.0
+ * Update vendor containers/(common,image,storage)
+ * build(deps): bump actions/upload-artifact from 2 to 3
+ * build(deps): bump actions/checkout from 2 to 3
+ * build(deps): bump actions/stale from 1 to 6
+ * build(deps): bump dawidd6/action-send-mail from 2.2.2 to 3.7.0
+ * build(deps): bump tim-actions/get-pr-commits from 1.1.0 to 1.2.0
+ * sshagent: LockOSThread before setting SocketLabel
+ * Update tests for error message changes
+ * Update c/image after https://github.com/containers/image/pull/1299
+ * Fix ident for dependabot gha block
+ * build(deps): bump github.com/containers/ocicrypt from 1.1.5 to 1.1.6
+ * Fix man pages to match latest cobra settings
+ * build(deps): bump github.com/spf13/cobra from 1.5.0 to 1.6.0
+ * build(deps): bump github.com/onsi/gomega from 1.20.2 to 1.22.1
+ * test: retrofit 'bud with undefined build arg directory'
+ * imagebuildah: warnOnUnsetBuildArgs while processing stages from executor
+ * Update contrib/buildahimage/Containerfile
+ * Cirrus CI add flavor parameter
+ * Correction - `FLAVOR` not `FLAVOUR`
+ * Changed build argument from `RELEASE` to `FLAVOUR`
+ * Combine buildahimage Containerfiles
+ * bud.bats refactoring: $TEST_SCRATCH_DIR, part 2 of 2
+ * bud.bats refactoring: $TEST_SCRATCH_DIR, part 1 of 2
+ * System test cleanup: document, clarify, fix
+ * test: removing unneeded/expensive COPY
+ * test: warning behaviour for unset/set TARGETOS,TARGETARCH,TARGETPLATFORM
+ * Bump to v1.28.1-dev
+
+- Changelog for v1.28.0 (2022-09-30)
+ * Update vendor containers/(common,image)
+ * [CI:DOCS] Add quay-description update reminder
+ * vendor: bump c/common to v0.49.2-0.20220929111928-2d1b45ae2423
+ * build(deps): bump github.com/opencontainers/selinux
+ * Vendor in latest containers/storage
+ * Changing shell list operators from `;` to `&&`
+ * Fix buildahimage container.conf permissions regression
+ * Set sysctls from containers.conf
+ * refactor: stop using Normalize directly from containerd package
+ * config,builder: process variant while populating image spec
+ * Proof of concept: nightly dependency treadmill
+ * Run codespell on code
+ * Check for unset build args after TARGET args
+ * pkg/cli: improve completion test
+ * vendor in latest containers/(common,storage,image)
+ * copier: work around freebsd bug for "mkdir /"
+ * vendor: update c/image
+ * test: run in the host cgroup namespace
+ * vendor: update c/storage
+ * vendor: update c/common
+ * cmd: check for user UID instead of privileges
+ * run,build: conflict --isolation=chroot and --network
+ * Fix broken dns test (from merge collision)
+ * Fix stutters
+ * Fix broken command completion
+ * buildah bud --network=none should have no network
+ * build: support --skip-unused-stages for multi-stage builds
+ * Prevent use of --dns* options with --net=none
+ * buildah: make --cache-ttl=0s equivalent to --no-cache
+ * parse: make processing flags in --mount order agnostic
+ * Minor test fix for podman-remote
+ * build: honor .containerignore as ignore file
+ * Update install.md: Debian 11 (Bullseye) is stable
+ * build(deps): bump github.com/docker/docker
+ * Use constants from containers/common for finding seccomp.json
+ * Don't call os.Exit(1) from manifest exist
+ * manifest: add support for buildah manifest exists
+ * Buildah should ignore /etc/crio/seccomp.json
+ * chroot: Fix cross build break
+ * chroot: Move isDevNull to run_common.go
+ * chroot: Fix setRlimit build on FreeBSD
+ * chroot: Move parseRLimits and setRlimits to run_common.go
+ * chroot: Fix runUsingChrootExecMain on FreeBSD
+ * chroot: Move runUsingChrootExecMain to run_common.go
+ * chroot: Factor out Linux-specific unshare options from runUsingChroot
+ * chroot: Move runUsingChroot to run_common.go
+ * chroot: Move RunUsingChroot and runUsingChrootMain to run_common.go
+ * chroot: Factor out /dev/ptmx pty implementation
+ * chroot: Add FreeBSD support for run with chroot isolation
+ * build(deps): bump github.com/docker/go-units from 0.4.0 to 0.5.0
+ * Replace k8s.gcr.io/pause in tests with registry.k8s.io/pause
+ * build(deps): bump github.com/onsi/gomega from 1.20.0 to 1.20.1
+ * Cirrus: use image with fewer downloaded dependencies
+ * build(deps): bump github.com/opencontainers/runc from 1.1.3 to 1.1.4
+ * run: add container gid to additional groups
+ * buildah: support for --retry and --retry-delay for push/pull failures
+ * Makefile: always call $(GO) instead of `go`
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.8.2 to 1.8.3
+ * test: use `T.TempDir` to create temporary test directory
+ * mount,cache: enable SElinux shared content label option by default
+ * commit: use race-free RemoveNames instead of SetNames
+ * Drop util/util.Cause()
+ * cmd/buildah: add "manifest create --amend"
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.8.1 to 1.8.2
+ * docs: specify git protocol is not supported for github hosted repo
+ * Scrub user and group names from layer diffs
+ * build(deps): bump github.com/containerd/containerd from 1.6.6 to 1.6.8
+ * version: bump to 1.28.0-dev
+
+- Changelog for v1.27.0 (2022-08-01)
+ * build: support filtering cache by duration using `--cache-ttl`.
+ * build: support building from commit when using git repo as build context.
+ * build: clean up git repos correctly when using subdirs.
+ * build: add support for distributing cache to remote sources using `--cache-to` and `--cache-from`.
+ * imagebuildah: optimize cache hits for `COPY` and `ADD` instructions.
+ * build: support OCI hooks for ephemeral build containers.
+ * build: add support for `--userns=auto`.
+ * copier: add NoOverwriteNonDirDir option .
+ * add initial support for building images using Buildah on FreeBSD.
+ * multistage: this now skips the computing of unwanted stages to improve performance.
+ * multiarch: support splitting build logs for `--platform` using `--logsplit`.
+ * build: add support for building images where the base image has no history.
+ * commit: allow disabling image history with `--omit-history`.
+ * build: add support for renaming a device in rootless setups.
+ * build: now supports additionalBuildContext in builds via the `--build-context` option.
+ * build: `--output` produces artifacts even if the build container is not committed.
+ * build: now accepts `-cpp-flag`, allowing users to pass in CPP flags when processing a Containerfile with C Preprocessor-like syntax.
+ * build: now accepts a branch and a subdirectory when the build context is a git repository.
+ * build: output now shows a progress bar while pushing and pulling images
+ * build: now errors out if the path to Containerfile is a directory.
+ * build: support building container images on environments that are rootless and without any valid login sessions.
+ * fix: `--output` now generates artifacts even if the entire build is cached.
+ * fix: `--output` generates artifacts only for the target stage in multi-stage builds.
+ * fix,add: now fails on a bad HTTP response instead of writing to container
+ * fix,squash: never use build cache when computing the last step of the last stage
+ * fix,build,run: allow reusing secret more than once in different RUN steps
+ * fix: compatibility with Docker build by making its --label and --annotate options set empty labels and annotations when given a name but no `=` or label value.
+
+- Changelog for v1.26.0 (2022-05-04)
+ * imagebuildah,build: move deepcopy of args before we spawn goroutine
+ * Vendor in containers/storage v1.40.2
+ * buildah.BuilderOptions.DefaultEnv is ignored, so mark it as deprecated
+ * help output: get more consistent about option usage text
+ * Handle OS version and features flags
+ * buildah build: --annotation and --label should remove values
+ * buildah build: add a --env
+ * buildah: deep copy options.Args before performing concurrent build/stage
+ * test: inline platform and builtinargs behaviour
+ * vendor: bump imagebuilder to master/009dbc6
+ * build: automatically set correct TARGETPLATFORM where expected
+ * build(deps): bump github.com/fsouza/go-dockerclient
+ * Vendor in containers/(common, storage, image)
+ * imagebuildah, executor: process arg variables while populating baseMap
+ * buildkit: add support for custom build output with --output
+ * Cirrus: Update CI VMs to F36
+ * fix staticcheck linter warning for deprecated function
+ * Fix docs build on FreeBSD
+ * build(deps): bump github.com/containernetworking/cni from 1.0.1 to 1.1.0
+ * copier.unwrapError(): update for Go 1.16
+ * copier.PutOptions: add StripSetuidBit/StripSetgidBit/StripStickyBit
+ * copier.Put(): write to read-only directories
+ * build(deps): bump github.com/cpuguy83/go-md2man/v2 in /tests/tools
+ * Rename $TESTSDIR (the plural one), step 4 of 3
+ * Rename $TESTSDIR (the plural one), step 3 of 3
+ * Rename $TESTSDIR (the plural one), step 2 of 3
+ * Rename $TESTSDIR (the plural one), step 1 of 3
+ * build(deps): bump github.com/containerd/containerd from 1.6.2 to 1.6.3
+ * Ed's periodic test cleanup
+ * using consistent lowercase 'invalid' word in returned err msg
+ * Update vendor of containers/(common,storage,image)
+ * use etchosts package from c/common
+ * run: set actual hostname in /etc/hostname to match docker parity
+ * update c/common to latest main
+ * Update vendor of containers/(common,storage,image)
+ * Stop littering
+ * manifest-create: allow creating manifest list from local image
+ * Update vendor of storage,common,image
+ * Bump golang.org/x/crypto to 7b82a4e
+ * Initialize network backend before first pull
+ * oci spec: change special mount points for namespaces
+ * tests/helpers.bash: assert handle corner cases correctly
+ * buildah: actually use containers.conf settings
+ * integration tests: learn to start a dummy registry
+ * Fix error check to work on Podman
+ * buildah build should accept at most one arg
+ * tests: reduce concurrency for flaky bud-multiple-platform-no-run
+ * vendor in latest containers/common,image,storage
+ * manifest-add: allow override arch,variant while adding image
+ * Remove a stray `\` from .containerenv
+ * Vendor in latest opencontainers/selinux v1.10.1
+ * build, commit: allow removing default identity labels
+ * Create shorter names for containers based on image IDs
+ * test: skip rootless on cgroupv2 in root env
+ * fix hang when oci runtime fails
+ * Set permissions for GitHub actions
+ * copier test: use correct UID/GID in test archives
+ * run: set parent-death signals and forward SIGHUP/SIGINT/SIGTERM
+ * Bump back to v1.26.0-dev
+ * build(deps): bump github.com/opencontainers/runc from 1.1.0 to 1.1.1
+ * Included the URL to check the SHA
+
+- Changelog for v1.25.1 (2022-03-30)
+ * buildah: create WORKDIR with USER permissions
+ * vendor: update github.com/openshift/imagebuilder
+ * copier: attempt to open the dir before adding it
+ * Updated dependabot to get updates for GitHub actions.
+ * Switch most calls to filepath.Walk to filepath.WalkDir
+ * build: allow --no-cache and --layers so build cache can be overrided
+ * build(deps): bump github.com/onsi/gomega from 1.18.1 to 1.19.0
+ * Bump to v1.26.0-dev
+ * build(deps): bump github.com/golangci/golangci-lint in /tests/tools
+
+- Changelog for v1.25.0 (2022-03-25)
+ * install: drop RHEL/CentOS 7 doc
+ * build(deps): bump github.com/containers/common from 0.47.4 to 0.47.5
+ * Bump c/storage to v1.39.0 in main
+ * Add a test for CVE-2022-27651
+ * build(deps): bump github.com/docker/docker
+ * Bump github.com/prometheus/client_golang to v1.11.1
+ * [CI:DOCS] man pages: sort flags, and keep them that way
+ * build(deps): bump github.com/containerd/containerd from 1.6.1 to 1.6.2
+ * Don't pollute
+ * network setup: increase timeout to 4 minutes
+ * do not set the inheritable capabilities
+ * build(deps): bump github.com/golangci/golangci-lint in /tests/tools
+ * build(deps): bump github.com/containers/ocicrypt from 1.1.2 to 1.1.3
+ * parse: convert exposed GetVolumes to internal only
+ * buildkit: mount=type=cache support locking external cache store
+ * .in support: improve error message when cpp is not installed
+ * buildah image: install cpp
+ * build(deps): bump github.com/stretchr/testify from 1.7.0 to 1.7.1
+ * build(deps): bump github.com/spf13/cobra from 1.3.0 to 1.4.0
+ * build(deps): bump github.com/docker/docker
+ * Add --no-hosts flag to eliminate use of /etc/hosts within containers
+ * test: remove skips for rootless users
+ * test: unshare mount/umount if test is_rootless
+ * tests/copy: read correct containers.conf
+ * build(deps): bump github.com/docker/distribution
+ * cirrus: add seperate task and matrix for rootless
+ * tests: skip tests for rootless which need unshare
+ * buildah: test rootless integration
+ * vendor: bump c/storage to main/93ce26691863
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.7.9 to 1.7.10
+ * tests/copy: initialize the network, too
+ * [CI:DOCS] remove references to Kubic for CentOS and Ubuntu
+ * build(deps): bump github.com/containerd/containerd from 1.6.0 to 1.6.1
+ * use c/image/pkg/blobcache
+ * vendor c/image/v5@v5.20.0
+ * add: ensure the context directory is an absolute path
+ * executor: docker builds must inherit healthconfig from base if any
+ * docs: Remove Containerfile and containeringore
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.7.8 to 1.7.9
+ * helpers.bash: Use correct syntax
+ * speed up combination-namespaces test
+ * build(deps): bump github.com/golangci/golangci-lint in /tests/tools
+ * Bump back to 1.25.0-dev
+ * build(deps): bump github.com/containerd/containerd from 1.5.9 to 1.6.0
+
+- Changelog for v1.24.2 (2022-02-16)
+ * Increase subuid/subgid to 65535
+ * history: only add proxy vars to history if specified
+ * run_linux: use --systemd-cgroup
+ * buildah: new global option --cgroup-manager
+ * Makefile: build with systemd when available
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.7.7 to 1.7.8
+ * Bump c/common to v0.47.4
+ * Cirrus: Use updated VM images
+ * conformance: add a few "replace-directory-with-symlink" tests
+ * Bump back to v1.25.0-dev
+
+- Changelog for v1.24.1 (2022-02-03)
+ * executor: Add support for inline --platform within Dockerfile
+ * caps: fix buildah run --cap-add=all
+ * Update vendor of openshift/imagebuilder
+ * Bump version of containers/image and containers/common
+ * Update vendor of containers/common
+ * System tests: fix accidental vandalism of source dir
+ * build(deps): bump github.com/containers/storage from 1.38.1 to 1.38.2
+ * imagebuildah.BuildDockerfiles(): create the jobs semaphore
+ * build(deps): bump github.com/onsi/gomega from 1.18.0 to 1.18.1
+ * overlay: always honor mountProgram
+ * overlay: move mount program invocation to separate function
+ * overlay: move mount program lookup to separate function
+ * Bump to v1.25.0-dev [NO TESTS NEEDED]
+
+- Changelog for v1.24.0 (2022-01-26)
+ * Update vendor of containers/common
+ * build(deps): bump github.com/golangci/golangci-lint in /tests/tools
+ * Github-workflow: Report both failures and errors.
+ * build(deps): bump github.com/containers/image/v5 from 5.18.0 to 5.19.0
+ * Update docs/buildah-build.1.md
+ * [CI:DOCS] Fix typos and improve language
+ * buildah bud --network add support for custom networks
+ * Make pull commands be consistent
+ * docs/buildah-build.1.md: don't imply that -v isn't just a RUN thing
+ * build(deps): bump github.com/onsi/gomega from 1.17.0 to 1.18.0
+ * Vendor in latest containers/image
+ * Run codespell on code
+ * .github/dependabot.yml: add tests/tools go.mod
+ * CI: rm git-validation, add GHA job to validate PRs
+ * tests/tools: bump go-md2man to v2.0.1
+ * tests/tools/Makefile: simplify
+ * tests/tools: bump onsi/ginkgo to v1.16.5
+ * vendor: bump c/common and others
+ * mount: add support for custom upper and workdir with overlay mounts
+ * linux: fix lookup for runtime
+ * overlay: add MountWithOptions to API which extends support for advanced overlay
+ * Allow processing of SystemContext from FlagSet
+ * .golangci.yml: enable unparam linter
+ * util/resolveName: rm bool return
+ * tests/tools: bump golangci-lint
+ * .gitignore: fixups
+ * all: fix capabilities.NewPid deprecation warnings
+ * bind/mount.go: fix linter comment
+ * all: fix gosimple warning S1039
+ * tests/e2e/buildah_suite_test.go: fix gosimple warnings
+ * imagebuildah/executor.go: fix gosimple warning
+ * util.go: fix gosimple warning
+ * build(deps): bump github.com/opencontainers/runc from 1.0.3 to 1.1.0
+ * Enable git-daemon tests
+ * Allow processing of id options from FlagSet
+ * Cirrus: Re-order tasks for more parallelism
+ * Cirrus: Freshen VM images
+ * Fix platform handling for empty os/arch values
+ * Allow processing of network options from FlagSet
+ * Fix permissions on secrets directory
+ * Update containers/image and containers/common
+ * bud.bats: use a local git daemon for the git protocol test
+ * Allow processing of common options from FlagSet
+ * Cirrus: Run int. tests in parallel with unit
+ * vendor c/common
+ * Fix default CNI paths
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.7.6 to 1.7.7
+ * multi-stage: enable mounting stages across each other with selinux enabled
+ * executor: Share selinux label of first stage with other stages in a build
+ * buildkit: add from field to bind and cache mounts so images can be used as source
+ * Use config.ProxyEnv from containers/common
+ * use libnetwork from c/common for networking
+ * setup the netns in the buildah parent process
+ * build(deps): bump github.com/containerd/containerd from 1.5.8 to 1.5.9
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.7.4 to 1.7.6
+ * build: fix libsubid test
+ * Allow callers to replace the ContainerSuffix
+ * parse: allow parsing anomaly non-human value for memory control group
+ * .cirrus: remove static_build from ci
+ * stage_executor: re-use all possible layers from cache for squashed builds
+ * build(deps): bump github.com/spf13/cobra from 1.2.1 to 1.3.0
+ * Allow rootless buildah to set resource limits on cgroup V2
+ * build(deps): bump github.com/docker/docker
+ * tests: move buildkit mount tests files from TESTSDIR to TESTDIR before modification
+ * build(deps): bump github.com/opencontainers/runc from 1.0.2 to 1.0.3
+ * Wire logger through to config
+ * copier.Put: check for is-not-a-directory using lstat, not stat
+ * Turn on rootless cgroupv2 tests
+ * Grab all of the containers.conf settings for namespaces.
+ * image: set MediaType in OCI manifests
+ * copier: RemoveAll possibly-directories
+ * Simple README fix
+ * images: accept multiple filter with logical AND
+ * build(deps): bump github.com/containernetworking/cni from 0.8.1 to 1.0.1
+ * UPdate vendor of container/storage
+ * build(deps): bump github.com/onsi/gomega from 1.16.0 to 1.17.0
+ * build(deps): bump github.com/containers/image/v5 from 5.16.1 to 5.17.0
+ * Make LocalIP public function so Podman can use it
+ * Fix UnsetEnv for buildah bud
+ * Tests should rely only on static/unchanging images
+ * run: ensure that stdio pipes are labeled correctly
+ * build(deps): bump github.com/docker/docker
+ * Cirrus: Bump up to Fedora 35 & Ubuntu 21.10
+ * chroot: don't use the generate default seccomp filter for unit tests
+ * build(deps): bump github.com/containerd/containerd from 1.5.7 to 1.5.8
+ * ssh-agent: Increase timeout before we explicitly close connection
+ * docs/tutorials: update
+ * Clarify that manifest defaults to localhost as the registry name
+ * "config": remove a stray bit of debug output
+ * "commit": fix a flag typo
+ * Fix an error message: unlocking vs locking
+ * Expand the godoc for CommonBuildOptions.Secrets
+ * chroot: accept an "rw" option
+ * Add --unsetenv option to buildah commit and build
+ * define.TempDirForURL(): show CombinedOutput when a command fails
+ * config: support the variant field
+ * rootless: do not bind mount /sys if not needed
+ * Fix tutorial to specify command on buildah run line
+ * build: history should not contain ARG values
+ * docs: Use guaranteed path for go-md2man
+ * run: honor --network=none from builder if nothing specified
+ * networkpolicy: Should be enabled instead of default when explictly set
+ * Add support for env var secret sources
+ * build(deps): bump github.com/docker/docker
+ * fix: another non-portable shebang
+ * Rootless containers users should use additional groups
+ * Support overlayfs path contains colon
+ * Report ignorefile location when no content added
+ * Add support for host.containers.internal in the /etc/hosts
+ * build(deps): bump github.com/onsi/ginkgo from 1.16.4 to 1.16.5
+ * imagebuildah: fix nil deref
+ * buildkit: add support for mount=type=cache
+ * Default secret mode to 400
+ * [CI:DOCS] Include manifest example usage
+ * docs: update buildah-from, buildah-pull 'platform' option compatibility notes
+ * docs: update buildah-build 'platform' option compatibility notes
+ * De-dockerize the man page as much as possible
+ * [CI:DOCS] Touch up Containerfile man page to show ARG can be 1st
+ * docs: Fix and Update Containerfile man page with supported mount types
+ * mount: add tmpcopyup to tmpfs mount option
+ * buildkit: Add support for --mount=type=tmpfs
+ * build(deps): bump github.com/opencontainers/selinux from 1.8.5 to 1.9.1
+ * Fix command doc links in README.md
+ * build(deps): bump github.com/containers/image/v5 from 5.16.0 to 5.16.1
+ * build: Add support for buildkit like --mount=type=bind
+ * Bump containerd to v1.5.7
+ * build(deps): bump github.com/docker/docker
+ * tests: stop pulling php, composer
+ * Fix .containerignore link file
+ * Cirrus: Fix defunct package metadata breaking cache
+ * build(deps): bump github.com/containers/storage from 1.36.0 to 1.37.0
+ * buildah build: add --all-platforms
+ * Add man page for Containerfile and .containerignore
+ * Plumb the remote logger throughut Buildah
+ * Replace fmt.Sprintf("%d", x) with strconv.Itoa(x)
+ * Run: Cleanup run directory after every RUN step
+ * build(deps): bump github.com/containers/common from 0.45.0 to 0.46.0
+ * Makefile: adjust -ldflags/-gcflags/-gccgoflags depending on the go implementation
+ * Makefile: check for `-race` using `-mod=vendor`
+ * imagebuildah: fix an attempt to write to a nil map
+ * push: support to specify the compression format
+ * conformance: allow test cases to specify dockerUseBuildKit
+ * build(deps): bump github.com/containers/common from 0.44.1 to 0.45.0
+ * build(deps): bump github.com/containers/common from 0.44.0 to 0.44.1
+ * unmarshalConvertedConfig(): handle zstd compression
+ * tests/copy/copy: wire up compression options
+ * Update to github.com/vbauerster/mpb v7.1.5
+ * Add flouthoc to OWNERS
+ * build: Add additional step nodes when labels are modified
+ * Makefile: turn on race detection whenever it's available
+ * conformance: add more tests for exclusion short-circuiting
+ * Update VM Images + Drop prior-ubuntu testing
+ * Bump to v1.24.0-dev
+
+- Changelog for v1.23.0 (2021-09-13)
+ * Vendor in containers/common v0.44.0
+ * build(deps): bump github.com/containers/storage from 1.35.0 to 1.36.0
+ * Update 05-openshift-rootless-build.md
+ * build(deps): bump github.com/opencontainers/selinux from 1.8.4 to 1.8.5
+ * .cirrus.yml: run cross_build_task on Big Sur
+ * Makefile: update cross targets
+ * Add support for rootless overlay mounts
+ * Cirrus: Increase unit-test timeout
+ * Docs: Clarify rmi w/ manifest/index use
+ * build: mirror --authfile to filesystem if pointing to FD instead of file
+ * Fix build with .git url with branch
+ * manifest: rm should remove only manifests not referenced images.
+ * vendor: bump c/common to v0.43.3-0.20210902095222-a7acc160fb25
+ * Avoid rehashing and noop compression writer
+ * corrected man page section; .conf file to mention its man page
+ * copy: add --max-parallel-downloads to tune that copy option
+ * copier.Get(): try to avoid descending into directories
+ * tag: Support tagging manifest list instead of resolving to images
+ * Install new manpages to correct sections
+ * conformance: tighten up exception specifications
+ * Add support for libsubid
+ * Add epoch time field to buildah images
+ * Fix ownership of /home/build/.local/share/containers
+ * build(deps): bump github.com/containers/image/v5 from 5.15.2 to 5.16.0
+ * Rename bud to build, while keeping an alias for to bud.
+ * Replace golang.org/x/crypto/ssh/terminal with golang.org/x/term
+ * build(deps): bump github.com/opencontainers/runc from 1.0.1 to 1.0.2
+ * build(deps): bump github.com/onsi/gomega from 1.15.0 to 1.16.0
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.7.3 to 1.7.4
+ * build(deps): bump github.com/containers/common from 0.43.1 to 0.43.2
+ * Move DiscoverContainerfile to pkg/util directory
+ * build(deps): bump github.com/containers/image/v5 from 5.15.1 to 5.15.2
+ * Remove some references to Docker
+ * build(deps): bump github.com/containers/image/v5 from 5.15.0 to 5.15.1
+ * imagebuildah: handle --manifest directly
+ * build(deps): bump github.com/containers/common from 0.42.1 to 0.43.1
+ * build(deps): bump github.com/opencontainers/selinux from 1.8.3 to 1.8.4
+ * executor: make sure imageMap is updated with terminatedStage
+ * tests/serve/serve.go: use a kernel-assigned port
+ * Bump go for vendor-in-container from 1.13 to 1.16
+ * imagebuildah: move multiple-platform building internal
+ * Adds GenerateStructure helper function to support rootfs-overlay.
+ * Run codespell to fix spelling
+ * Implement SSH RUN mount
+ * build(deps): bump github.com/onsi/gomega from 1.14.0 to 1.15.0
+ * Fix resolv.conf content with run --net=private
+ * run: fix nil deref using the option's logger
+ * build(deps): bump github.com/containerd/containerd from 1.5.1 to 1.5.5
+ * make vendor-in-container
+ * bud: teach --platform to take a list
+ * set base-image annotations
+ * build(deps): bump github.com/opencontainers/selinux from 1.8.2 to 1.8.3
+ * [CI:DOCS] Fix CHANGELOG.md
+ * Bump to v1.23.0-dev [NO TESTS NEEDED]
+ * Accept repositories on login/logout
+
+- Changelog for v1.22.0 (2021-08-02)
+ * c/image, c/storage, c/common vendor before Podman 3.3 release
+ * WIP: tests: new assert()
+ * Proposed patch for 3399 (shadowutils)
+ * Fix handling of --restore shadow-utils
+ * build(deps): bump github.com/containers/image/v5 from 5.13.2 to 5.14.0
+ * runtime-flag (debug) test: handle old & new runc
+ * build(deps): bump github.com/containers/storage from 1.32.6 to 1.33.0
+ * Allow dst and destination for target in secret mounts
+ * Multi-arch: Always push updated version-tagged img
+ * Add a few tests on cgroups V2
+ * imagebuildah.stageExecutor.prepare(): remove pseudonym check
+ * refine dangling filter
+ * Chown with environment variables not set should fail
+ * Just restore protections of shadow-utils
+ * build(deps): bump github.com/opencontainers/runc from 1.0.0 to 1.0.1
+ * Remove specific kernel version number requirement from install.md
+ * Multi-arch image workflow: Make steps generic
+ * chroot: fix environment value leakage to intermediate processes
+ * Update nix pin with `make nixpkgs`
+ * buildah source - create and manage source images
+ * Update cirrus-cron notification GH workflow
+ * Reuse code from containers/common/pkg/parse
+ * Cirrus: Freshen VM images
+ * build(deps): bump github.com/containers/storage from 1.32.5 to 1.32.6
+ * Fix excludes exception begining with / or ./
+ * Fix syntax for --manifest example
+ * build(deps): bump github.com/onsi/gomega from 1.13.0 to 1.14.0
+ * vendor containers/common@main
+ * Cirrus: Drop dependence on fedora-minimal
+ * Adjust conformance-test error-message regex
+ * Workaround appearance of differing debug messages
+ * Cirrus: Install docker from package cache
+ * build(deps): bump github.com/containers/ocicrypt from 1.1.1 to 1.1.2
+ * Switch rusagelogfile to use options.Out
+ * build(deps): bump github.com/containers/storage from 1.32.4 to 1.32.5
+ * Turn stdio back to blocking when command finishes
+ * Add support for default network creation
+ * Cirrus: Updates for master->main rename
+ * Change references from master to main
+ * Add `--env` and `--workingdir` flags to run command
+ * build(deps): bump github.com/opencontainers/runc
+ * [CI:DOCS] buildah bud: spelling --ignore-file requires parameter
+ * [CI:DOCS] push/pull: clarify supported transports
+ * Remove unused function arguments
+ * Create mountOptions for mount command flags
+ * Extract version command implementation to function
+ * Add --json flags to `mount` and `version` commands
+ * build(deps): bump github.com/containers/storage from 1.32.2 to 1.32.3
+ * build(deps): bump github.com/containers/common from 0.40.0 to 0.40.1
+ * copier.Put(): set xattrs after ownership
+ * buildah add/copy: spelling
+ * build(deps): bump github.com/containers/common from 0.39.0 to 0.40.0
+ * buildah copy and buildah add should support .containerignore
+ * Remove unused util.StartsWithValidTransport
+ * Fix documentation of the --format option of buildah push
+ * Don't use alltransports.ParseImageName with known transports
+ * build(deps): bump github.com/containers/image/v5 from 5.13.0 to 5.13.1
+ * man pages: clarify `rmi` removes dangling parents
+ * tests: make it easer to override the location of the copy helper
+ * build(deps): bump github.com/containers/image/v5 from 5.12.0 to 5.13.0
+ * [CI:DOCS] Fix links to c/image master branch
+ * imagebuildah: use the specified logger for logging preprocessing warnings
+ * Fix copy into workdir for a single file
+ * Fix docs links due to branch rename
+ * Update nix pin with `make nixpkgs`
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.7.2 to 1.7.3
+ * build(deps): bump github.com/opencontainers/selinux from 1.8.1 to 1.8.2
+ * build(deps): bump go.etcd.io/bbolt from 1.3.5 to 1.3.6
+ * build(deps): bump github.com/containers/storage from 1.32.1 to 1.32.2
+ * build(deps): bump github.com/mattn/go-shellwords from 1.0.11 to 1.0.12
+ * build(deps): bump github.com/onsi/ginkgo from 1.16.3 to 1.16.4
+ * fix(docs): typo
+ * Move to v1.22.0-dev
+ * Fix handling of auth.json file while in a user namespace
+ * Add rusage-logfile flag to optionally send rusage to a file
+ * imagebuildah: redo step logging
+ * build(deps): bump github.com/onsi/ginkgo from 1.16.2 to 1.16.3
+ * build(deps): bump github.com/containers/storage from 1.32.0 to 1.32.1
+ * Add volumes to make running buildah within a container easier
+ * build(deps): bump github.com/onsi/gomega from 1.12.0 to 1.13.0
+ * Add and use a "copy" helper instead of podman load/save
+ * Bump github.com/containers/common from 0.38.4 to 0.39.0
+ * containerImageRef/containerImageSource: don't buffer uncompressed layers
+ * containerImageRef(): squashed images have no parent images
+ * Sync. workflow across skopeo, buildah, and podman
+ * Bump github.com/containers/storage from 1.31.1 to 1.31.2
+ * Bump github.com/opencontainers/runc from 1.0.0-rc94 to 1.0.0-rc95
+ * Bump to v1.21.1-dev [NO TESTS NEEDED]
+
+- Changelog for v1.21.0 (2021-05-19)
+ * Don't blow up if cpp detects errors
+ * Vendor in containers/common v0.38.4
+ * Remove 'buildah run --security-opt' from completion
+ * update c/common
+ * Fix handling of --default-mounts-file
+ * update vendor of containers/storage v1.31.1
+ * Bump github.com/containers/storage from 1.30.3 to 1.31.0
+ * Send logrus messages back to caller when building
+ * github: Fix bad repo. ref in workflow config
+ * Check earlier for bad image tags name
+ * buildah bud: fix containers/podman/issues/10307
+ * Bump github.com/containers/storage from 1.30.1 to 1.30.3
+ * Cirrus: Support [CI:DOCS] test skipping
+ * Notification email for cirrus-cron build failures
+ * Bump github.com/opencontainers/runc from 1.0.0-rc93 to 1.0.0-rc94
+ * Fix race condition
+ * Fix copy race while walking paths
+ * Preserve ownership of lower directory when doing an overlay mount
+ * Bump github.com/onsi/gomega from 1.11.0 to 1.12.0
+ * Update nix pin with `make nixpkgs`
+ * codespell cleanup
+ * Multi-arch github-action workflow unification
+ * Bump github.com/containers/image/v5 from 5.11.1 to 5.12.0
+ * Bump github.com/onsi/ginkgo from 1.16.1 to 1.16.2
+ * imagebuildah: ignore signatures when tagging images
+ * update to latest libimage
+ * Bump github.com/containers/common from 0.37.0 to 0.37.1
+ * Bump github.com/containers/storage from 1.30.0 to 1.30.1
+ * Upgrade to GitHub-native Dependabot
+ * Document location of auth.json file if XDG_RUNTIME_DIR is not set
+ * run.bats: fix flake in run-user test
+ * Cirrus: Update F34beta -> F34
+ * pr-should-include-tests: try to make work in buildah
+ * runUsingRuntime: when relaying error from the runtime, mention that
+ * Run(): avoid Mkdir() into the rootfs
+ * imagebuildah: replace archive with chrootarchive
+ * imagebuildah.StageExecutor.volumeCacheSaveVFS(): set up bind mounts
+ * conformance: use :Z with transient mounts when SELinux is enabled
+ * bud.bats: fix a bats warning
+ * imagebuildah: create volume directories when using overlays
+ * imagebuildah: drop resolveSymlink()
+ * namespaces test - refactoring and cleanup
+ * Refactor 'idmapping' system test
+ * Cirrus: Update Ubuntu images to 21.04
+ * Tiny fixes in bud system tests
+ * Add compabitility wrappers for removed packages
+ * Fix expected message at pulling image
+ * Fix system tests of 'bud' subcommand
+ * [CI:DOCS] Update steps for CentOS runc users
+ * Add support for secret mounts
+ * Add buildah manifest rm command
+ * restore push/pull and util API
+ * [CI:DOCS] Remove older distro docs
+ * Rename rhel secrets to subscriptions
+ * vendor in openshift/imagebuilder
+ * Remove buildah bud --loglevel ...
+ * use new containers/common/libimage package
+ * Fix copier when using globs
+ * Test namespace flags of 'bud' subcommand
+ * Add system test of 'bud' subcommand
+ * Output names of multiple tags in buildah bud
+ * push to docker test: don't get fooled by podman
+ * copier: add Remove()
+ * build(deps): bump github.com/containers/image/v5 from 5.10.5 to 5.11.1
+ * Restore log timestamps
+ * Add system test of 'buildah help' with a tiny fix
+ * tests: copy.bats: fix infinite hang
+ * Do not force hard code to crun in rootless mode
+ * build(deps): bump github.com/openshift/imagebuilder from 1.2.0 to 1.2.1
+ * build(deps): bump github.com/containers/ocicrypt from 1.1.0 to 1.1.1
+ * build(deps): bump github.com/containers/common from 0.35.4 to 0.36.0
+ * Fix arg missing warning in bud
+ * Check without flag in 'from --cgroup-parent' test
+ * Minor fixes to Buildah as a library tutorial documentation
+ * Add system test of 'buildah version' for packaged buildah
+ * Add a few system tests of 'buildah from'
+ * Log the final error with %+v at logging level "trace"
+ * copier: add GetOptions.NoCrossDevice
+ * Update nix pin with `make nixpkgs`
+ * Bump to v1.20.2-dev
+
+- Changelog for v1.20.1 (2021-04-13)
+ * Run container with isolation type set at 'from'
+ * bats helpers.bash - minor refactoring
+ * Bump containers/storage vendor to v1.29.0
+ * build(deps): bump github.com/onsi/ginkgo from 1.16.0 to 1.16.1
+ * Cirrus: Update VMs w/ F34beta
+ * CLI add/copy: add a --from option
+ * build(deps): bump github.com/onsi/ginkgo from 1.15.2 to 1.16.0
+ * Add authentication system tests for 'commit' and 'bud'
+ * fix local image lookup for custom platform
+ * Double-check existence of OCI runtimes
+ * Cirrus: Make use of shared get_ci_vm container
+ * Add system tests of "buildah run"
+ * Update nix pin with `make nixpkgs`
+ * Remove some stuttering on returns errors
+ * Setup alias for --tty to --terminal
+ * Add conformance tests for COPY /...
+ * Put a few more minutes on the clock for the CI conformance test
+ * Add a conformance test for COPY --from $symlink
+ * Add conformance tests for COPY ""
+ * Check for symlink in builtin volume
+ * Sort all mounts by destination directory
+ * System-test cleanup
+ * Export parse.Platform string to be used by podman-remote
+ * blobcache: fix sequencing error
+ * build(deps): bump github.com/containers/common from 0.35.3 to 0.35.4
+ * Fix URL in demos/buildah_multi_stage.sh
+ * Add a few system tests
+ * [NO TESTS NEEDED] Use --recurse-modules when building git context
+ * Bump to v1.20.1-dev
+
+- Changelog for v1.20.0 (2021-03-25)
+ * vendor in containers/storage v1.28.1
+ * build(deps): bump github.com/containers/common from 0.35.2 to 0.35.3
+ * tests: prefetch: use buildah, not podman, for pulls
+ * Use faster way to check image tag existence during multi-arch build
+ * Add information about multi-arch images to the Readme
+ * COPY --chown: expand the conformance test
+ * pkg/chrootuser: use a bufio.Scanner
+ * [CI:DOCS] Fix rootful typo in docs
+ * build(deps): bump github.com/onsi/ginkgo from 1.15.1 to 1.15.2
+ * Add documentation and testing for .containerignore
+ * build(deps): bump github.com/sirupsen/logrus from 1.8.0 to 1.8.1
+ * build(deps): bump github.com/hashicorp/go-multierror from 1.1.0 to 1.1.1
+ * Lookup Containerfile if user specifies a directory
+ * Add Tag format placeholder to docs
+ * copier: ignore sockets
+ * image: propagate errors from extractRootfs
+ * Remove system test of 'buildah containers -a'
+ * Clarify userns options are usable only as root in man pages
+ * Fix system test of 'containers -a'
+ * Remove duplicated code in addcopy
+ * build(deps): bump github.com/onsi/ginkgo from 1.15.0 to 1.15.1
+ * build(deps): bump github.com/onsi/gomega from 1.10.5 to 1.11.0
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.7.1 to 1.7.2
+ * Update multi-arch buildah build setup with new logic
+ * Update nix pin with `make nixpkgs`
+ * overlay.bats: fix the "overlay source permissions" test
+ * imagebuildah: use overlay for volumes when using overlay
+ * Make PolicyMap and PullPolicy names align
+ * copier: add GetOptions.IgnoreUnreadable
+ * Check local image to match system context
+ * fix: Containerfiles - smaller set of userns u/gids
+ * Set upperdir permissions based on source
+ * Shrink the vendoring size of pkc/cli
+ * Clarify image name match failure message
+ * ADD/COPY: create the destination directory first, chroot to it
+ * copier.GetOptions: add NoDerefSymLinks
+ * copier: add an Eval function
+ * Update system test for 'from --cap-add/drop'
+ * copier: fix a renaming bug
+ * copier: return child process stderr if we can't JSON decode the response
+ * Add some system tests
+ * build(deps): bump github.com/containers/storage from 1.26.0 to 1.27.0
+ * complement add/copy --chmod documentation
+ * buildah login and logout, do not need to enter user namespace
+ * Add multi-arch image build
+ * chmod/chown added/fixed in bash completions
+ * OWNERS: add @lsm5
+ * buildah add/copy --chmod dockerfile implementation
+ * bump github.com/openshift/imagebuilder from 1.1.8 to 1.2.0
+ * buildah add/copy --chmod cli implementation for files and urls
+ * Make sure we set the buildah version label
+ * Isolation strings, should match user input
+ * [CI:DOCS] buildah-from.md: remove dup arch,os
+ * build(deps): bump github.com/containers/image/v5 from 5.10.2 to 5.10.3
+ * Cirrus: Temp. disable prior-fedora (F32) testing
+ * pr-should-include-tests: recognized "renamed" tests
+ * build(deps): bump github.com/sirupsen/logrus from 1.7.0 to 1.8.0
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.7.0 to 1.7.1
+ * build(deps): bump github.com/containers/common from 0.34.2 to 0.35.0
+ * Fix reaping of stages with no instructions
+ * add stale bot
+ * Add base image name to comment
+ * build(deps): bump github.com/spf13/cobra from 1.1.1 to 1.1.3
+ * Don't fail copy to emptydir
+ * buildah: use volatile containers
+ * vendor: update containers/storage
+ * Eliminate the use of containers/building import in pkg subdirs
+ * Add more support for removing config
+ * Improve messages about --cache-from not being supported
+ * Revert patch to allow COPY/ADD of empty dirs.
+ * Don't fail copy to emptydir
+ * Fix tutorial for rootless mode
+ * Fix caching layers with build args
+ * Vendor in containers/image v5.10.2
+ * build(deps): bump github.com/containers/common from 0.34.0 to 0.34.2
+ * build(deps): bump github.com/onsi/ginkgo from 1.14.2 to 1.15.0
+ * 'make validate': require PRs to include tests
+ * build(deps): bump github.com/onsi/gomega from 1.10.4 to 1.10.5
+ * build(deps): bump github.com/containers/storage from 1.24.5 to 1.25.0
+ * Use chown function for U volume flag from containers/common repository
+ * --iidfile: print hash prefix
+ * bump containernetworking/cni to v0.8.1 - fix for CVE-2021-20206
+ * run: fix check for host pid namespace
+ * Finish plumbing for buildah bud --manifest
+ * buildah manifest add localimage should work
+ * Stop testing directory permissions with latest docker
+ * Fix build arg check
+ * build(deps): bump github.com/containers/ocicrypt from 1.0.3 to 1.1.0
+ * [ci:docs] Fix man page for buildah push
+ * Update nix pin with `make nixpkgs`
+ * Bump to containers/image v5.10.1
+ * Rebuild layer if a change in ARG is detected
+ * Bump golang.org/x/crypto to the latest
+ * Add Ashley and Urvashi to Approvers
+ * local image lookup by digest
+ * Use build-arg ENV val from local environment if set
+ * Pick default OCI Runtime from containers.conf
+ * Added required devel packages
+ * Cirrus: Native OSX Build
+ * Cirrus: Two minor cleanup items
+ * Workaround for RHEL gating test failure
+ * build(deps): bump github.com/stretchr/testify from 1.6.1 to 1.7.0
+ * build(deps): bump github.com/mattn/go-shellwords from 1.0.10 to 1.0.11
+ * Reset upstream branch to dev version
+ * If destination does not exists, do not throw error
+
+- Changelog for v1.19.0 (2021-01-08)
+ * Update vendor of containers/storage and containers/common
+ * Buildah inspect should be able to inspect manifests
+ * Make buildah push support pushing manifests lists and digests
+ * Fix handling of TMPDIR environment variable
+ * Add support for --manifest flags
+ * Upper directory should match mode of destination directory
+ * Only grab the OS, Arch if the user actually specified them
+ * Use --arch and --os and --variant options to select architecture and os
+ * Cirrus: Track libseccomp and golang version
+ * copier.PutOptions: add an "IgnoreDevices" flag
+ * fix: `rmi --prune` when parent image is in store.
+ * build(deps): bump github.com/containers/storage from 1.24.3 to 1.24.4
+ * build(deps): bump github.com/containers/common from 0.31.1 to 0.31.2
+ * Allow users to specify stdin into containers
+ * Drop log message on failure to mount on /sys file systems to info
+ * Spelling
+ * SELinux no longer requires a tag.
+ * build(deps): bump github.com/opencontainers/selinux from 1.6.0 to 1.8.0
+ * build(deps): bump github.com/containers/common from 0.31.0 to 0.31.1
+ * Update nix pin with `make nixpkgs`
+ * Switch references of /var/run -> /run
+ * Allow FROM to be overriden with from option
+ * copier: don't assume we can chroot() on Unixy systems
+ * copier: add PutOptions.NoOverwriteDirNonDir, Get/PutOptions.Rename
+ * copier: handle replacing directories with not-directories
+ * copier: Put: skip entries with zero-length names
+ * build(deps): bump github.com/containers/storage from 1.24.2 to 1.24.3
+ * Add U volume flag to chown source volumes
+ * Turn off PRIOR_UBUNTU Test until vm is updated
+ * pkg, cli: rootless uses correct isolation
+ * build(deps): bump github.com/onsi/gomega from 1.10.3 to 1.10.4
+ * update installation doc to reflect current status
+ * Move away from using docker.io
+ * enable short-name aliasing
+ * build(deps): bump github.com/containers/storage from 1.24.1 to 1.24.2
+ * build(deps): bump github.com/containers/common from 0.30.0 to 0.31.0
+ * Throw errors when using bogus --network flags
+ * pkg/supplemented test: replace our null blobinfocache
+ * build(deps): bump github.com/containers/common from 0.29.0 to 0.30.0
+ * inserts forgotten quotation mark
+ * Not prefer use local image create/add manifest
+ * Add container information to .containerenv
+ * Add --ignorefile flag to use alternate .dockerignore flags
+ * Add a source debug build
+ * Fix crash on invalid filter commands
+ * build(deps): bump github.com/containers/common from 0.27.0 to 0.29.0
+ * Switch to using containers/common pkg's
+ * fix: non-portable shebang #2812
+ * Remove copy/paste errors that leaked `Podman` into man pages.
+ * Add suggests cpp to spec file
+ * Apply suggestions from code review
+ * update docs for debian testing and unstable
+ * imagebuildah: disable pseudo-terminals for RUN
+ * Compute diffID for mapped-layer at creating image source
+ * intermediateImageExists: ignore images whose history we can't read
+ * Bump to v1.19.0-dev
+ * build(deps): bump github.com/containers/common from 0.26.3 to 0.27.0
+
+- Changelog for v1.18.0 (2020-11-16)
+ * Fix testing error caused by simultanious merge
+ * Vendor in containers/storage v1.24.0
+ * short-names aliasing
+ * Add --policy flag to buildah pull
+ * Stop overwrapping and stuttering
+ * copier.Get(): ignore ENOTSUP/ENOSYS when listing xattrs
+ * Run: don't forcibly disable UTS namespaces in rootless mode
+ * test: ensure non-directory in a Dockerfile path is handled correctly
+ * Add a few tests for `pull` command
+ * Fix buildah config --cmd to handle array
+ * build(deps): bump github.com/containers/storage from 1.23.8 to 1.23.9
+ * Fix NPE when Dockerfile path contains non-directory entries
+ * Update buildah bud man page from podman build man page
+ * Move declaration of decryption-keys to common cli
+ * Run: correctly call copier.Mkdir
+ * util: digging UID/GID out of os.FileInfo should work on Unix
+ * imagebuildah.getImageTypeAndHistoryAndDiffIDs: cache results
+ * Verify userns-uid-map and userns-gid-map input
+ * Use CPP, CC and flags in dep check scripts
+ * Avoid overriding LDFLAGS in Makefile
+ * ADD: handle --chown on URLs
+ * Update nix pin with `make nixpkgs`
+ * (*Builder).Run: MkdirAll: handle EEXIST error
+ * copier: try to force loading of nsswitch modules before chroot()
+ * fix MkdirAll usage
+ * build(deps): bump github.com/containers/common from 0.26.2 to 0.26.3
+ * build(deps): bump github.com/containers/storage from 1.23.7 to 1.23.8
+ * Use osusergo build tag for static build
+ * imagebuildah: cache should take image format into account
+ * Bump to v1.18.0-dev
+
+- Changelog for v1.17.0 (2020-10-29)
+ * Handle cases where other tools mount/unmount containers
+ * overlay.MountReadOnly: support RO overlay mounts
+ * overlay: use fusermount for rootless umounts
+ * overlay: fix umount
+ * Switch default log level of Buildah to Warn. Users need to see these messages
+ * Drop error messages about OCI/Docker format to Warning level
+ * build(deps): bump github.com/containers/common from 0.26.0 to 0.26.2
+ * tests/testreport: adjust for API break in storage v1.23.6
+ * build(deps): bump github.com/containers/storage from 1.23.5 to 1.23.7
+ * build(deps): bump github.com/fsouza/go-dockerclient from 1.6.5 to 1.6.6
+ * copier: put: ignore Typeflag="g"
+ * Use curl to get repo file (fix #2714)
+ * build(deps): bump github.com/containers/common from 0.25.0 to 0.26.0
+ * build(deps): bump github.com/spf13/cobra from 1.0.0 to 1.1.1
+ * Remove docs that refer to bors, since we're not using it
+ * Buildah bud should not use stdin by default
+ * bump containerd, docker, and golang.org/x/sys
+ * Makefile: cross: remove windows.386 target
+ * copier.copierHandlerPut: don't check length when there are errors
+ * Stop excessive wrapping
+ * CI: require that conformance tests pass
+ * bump(github.com/openshift/imagebuilder) to v1.1.8
+ * Skip tlsVerify insecure BUILD_REGISTRY_SOURCES
+ * Fix build path wrong https://github.com/containers/podman/issues/7993
+ * refactor pullpolicy to avoid deps
+ * build(deps): bump github.com/containers/common from 0.24.0 to 0.25.0
+ * CI: run gating tasks with a lot more memory
+ * ADD and COPY: descend into excluded directories, sometimes
+ * copier: add more context to a couple of error messages
+ * copier: check an error earlier
+ * copier: log stderr output as debug on success
+ * Update nix pin with `make nixpkgs`
+ * Set directory ownership when copied with ID mapping
+ * build(deps): bump github.com/sirupsen/logrus from 1.6.0 to 1.7.0
+ * build(deps): bump github.com/containers/common from 0.23.0 to 0.24.0
+ * Cirrus: Remove bors artifacts
+ * Sort build flag definitions alphabetically
+ * ADD: only expand archives at the right time
+ * Remove configuration for bors
+ * Shell Completion for podman build flags
+ * Bump c/common to v0.24.0
+ * New CI check: xref --help vs man pages
+ * CI: re-enable several linters
+ * Move --userns-uid-map/--userns-gid-map description into buildah man page
+ * add: preserve ownerships and permissions on ADDed archives
+ * Makefile: tweak the cross-compile target
+ * Bump containers/common to v0.23.0
+ * chroot: create bind mount targets 0755 instead of 0700
+ * Change call to Split() to safer SplitN()
+ * chroot: fix handling of errno seccomp rules
+ * build(deps): bump github.com/containers/image/v5 from 5.5.2 to 5.6.0
+ * Add In Progress section to contributing
+ * integration tests: make sure tests run in ${topdir}/tests
+ * Run(): ignore containers.conf's environment configuration
+ * Warn when setting healthcheck in OCI format
+ * Cirrus: Skip git-validate on branches
+ * tools: update git-validation to the latest commit
+ * tools: update golangci-lint to v1.18.0
+ * Add a few tests of push command
+ * Add(): fix handling of relative paths with no ContextDir
+ * build(deps): bump github.com/containers/common from 0.21.0 to 0.22.0
+ * Lint: Use same linters as podman
+ * Validate: reference HEAD
+ * Fix buildah mount to display container names not ids
+ * Update nix pin with `make nixpkgs`
+ * Add missing --format option in buildah from man page
+ * Fix up code based on codespell
+ * build(deps): bump github.com/openshift/imagebuilder from 1.1.6 to 1.1.7
+ * build(deps): bump github.com/containers/storage from 1.23.4 to 1.23.5
+ * Improve buildah completions
+ * Cirrus: Fix validate commit epoch
+ * Fix bash completion of manifest flags
+ * Uniform some man pages
+ * Update Buildah Tutorial to address BZ1867426
+ * Update bash completion of `manifest add` sub command
+ * copier.Get(): hard link targets shouldn't be relative paths
+ * build(deps): bump github.com/onsi/gomega from 1.10.1 to 1.10.2
+ * Pass timestamp down to history lines
+ * Timestamp gets updated everytime you inspect an image
+ * bud.bats: use absolute paths in newly-added tests
+ * contrib/cirrus/lib.sh: don't use CN for the hostname
+ * tests: Add some tests
+ * Update `manifest add` man page
+ * Extend flags of `manifest add`
+ * build(deps): bump github.com/containers/storage from 1.23.3 to 1.23.4
+ * build(deps): bump github.com/onsi/ginkgo from 1.14.0 to 1.14.1
+ * Bump to v1.17.0-dev
+ * CI: expand cross-compile checks
+
+- Changelog for v1.16.0 (2020-09-03)
+ * fix build on 32bit arches
+ * containerImageRef.NewImageSource(): don't always force timestamps
+ * Add fuse module warning to image readme
+ * Heed our retry delay option values when retrying commit/pull/push
+ * Switch to containers/common for seccomp
+ * Use --timestamp rather then --omit-timestamp
+ * docs: remove outdated notice
+ * docs: remove outdated notice
+ * build-using-dockerfile: add a hidden --log-rusage flag
+ * build(deps): bump github.com/containers/image/v5 from 5.5.1 to 5.5.2
+ * Discard ReportWriter if user sets options.Quiet
+ * build(deps): bump github.com/containers/common from 0.19.0 to 0.20.3
+ * Fix ownership of content copied using COPY --from
+ * newTarDigester: zero out timestamps in tar headers
+ * Update nix pin with `make nixpkgs`
+ * bud.bats: correct .dockerignore integration tests
+ * Use pipes for copying
+ * run: include stdout in error message
+ * run: use the correct error for errors.Wrapf
+ * copier: un-export internal types
+ * copier: add Mkdir()
+ * in_podman: don't get tripped up by $CIRRUS_CHANGE_TITLE
+ * docs/buildah-commit.md: tweak some wording, add a --rm example
+ * imagebuildah: don’t blank out destination names when COPYing
+ * Replace retry functions with common/pkg/retry
+ * StageExecutor.historyMatches: compare timestamps using .Equal
+ * Update vendor of containers/common
+ * Fix errors found in coverity scan
+ * Change namespace handling flags to better match podman commands
+ * conformance testing: ignore buildah.BuilderIdentityAnnotation labels
+ * Vendor in containers/storage v1.23.0
+ * Add buildah.IsContainer interface
+ * Avoid feeding run_buildah to pipe
+ * fix(buildahimage): add xz dependency in buildah image
+ * Bump github.com/containers/common from 0.15.2 to 0.18.0
+ * Howto for rootless image building from OpenShift
+ * Add --omit-timestamp flag to buildah bud
+ * Update nix pin with `make nixpkgs`
+ * Shutdown storage on failures
+ * Handle COPY --from when an argument is used
+ * Bump github.com/seccomp/containers-golang from 0.5.0 to 0.6.0
+ * Cirrus: Use newly built VM images
+ * Bump github.com/opencontainers/runc from 1.0.0-rc91 to 1.0.0-rc92
+ * Enhance the .dockerignore man pages
+ * conformance: add a test for COPY from subdirectory
+ * fix bug manifest inspct
+ * Add documentation for .dockerignore
+ * Add BuilderIdentityAnnotation to identify buildah version
+ * DOC: Add quay.io/containers/buildah image to README.md
+ * Update buildahimages readme
+ * fix spelling mistake in "info" command result display
+ * Don't bind /etc/host and /etc/resolv.conf if network is not present
+ * blobcache: avoid an unnecessary NewImage()
+ * Build static binary with `buildGoModule`
+ * copier: split StripSetidBits into StripSetuidBit/StripSetgidBit/StripStickyBit
+ * tarFilterer: handle multiple archives
+ * Fix a race we hit during conformance tests
+ * Rework conformance testing
+ * Update 02-registries-repositories.md
+ * test-unit: invoke cmd/buildah tests with --flags
+ * parse: fix a type mismatch in a test
+ * Fix compilation of tests/testreport/testreport
+ * build.sh: log the version of Go that we're using
+ * test-unit: increase the test timeout to 40/45 minutes
+ * Add the "copier" package
+ * Fix & add notes regarding problematic language in codebase
+ * Add dependency on github.com/stretchr/testify/require
+ * CompositeDigester: add the ability to filter tar streams
+ * BATS tests: make more robust
+ * vendor golang.org/x/text@v0.3.3
+ * Switch golang 1.12 to golang 1.13
+ * imagebuildah: wait for stages that might not have even started yet
+ * chroot, run: not fail on bind mounts from /sys
+ * chroot: do not use setgroups if it is blocked
+ * Set engine env from containers.conf
+ * imagebuildah: return the right stage's image as the "final" image
+ * Fix a help string
+ * Deduplicate environment variables
+ * switch containers/libpod to containers/podman
+ * Bump github.com/containers/ocicrypt from 1.0.2 to 1.0.3
+ * Bump github.com/opencontainers/selinux from 1.5.2 to 1.6.0
+ * Mask out /sys/dev to prevent information leak
+ * linux: skip errors from the runtime kill
+ * Mask over the /sys/fs/selinux in mask branch
+ * Add VFS additional image store to container
+ * tests: add auth tests
+ * Allow "readonly" as alias to "ro" in mount options
+ * Ignore OS X specific consistency mount option
+ * Bump github.com/onsi/ginkgo from 1.13.0 to 1.14.0
+ * Bump github.com/containers/common from 0.14.0 to 0.15.2
+ * Rootless Buildah should default to IsolationOCIRootless
+ * imagebuildah: fix inheriting multi-stage builds
+ * Make imagebuildah.BuildOptions.Architecture/OS optional
+ * Make imagebuildah.BuildOptions.Jobs optional
+ * Resolve a possible race in imagebuildah.Executor.startStage()
+ * Switch scripts to use containers.conf
+ * Bump openshift/imagebuilder to v1.1.6
+ * Bump go.etcd.io/bbolt from 1.3.4 to 1.3.5
+ * buildah, bud: support --jobs=N for parallel execution
+ * executor: refactor build code inside new function
+ * Add bud regression tests
+ * Cirrus: Fix missing htpasswd in registry img
+ * docs: clarify the 'triples' format
+ * CHANGELOG.md: Fix markdown formatting
+ * Add nix derivation for static builds
+ * Bump to v1.16.0-dev
+ * add version centos7 for compatible
+
+- Changelog for v1.15.0 (2020-06-17)
+ * Bump github.com/containers/common from 0.12.0 to 0.13.1
+ * Bump github.com/containers/storage from 1.20.1 to 1.20.2
+ * Bump github.com/seccomp/containers-golang from 0.4.1 to 0.5.0
+ * Bump github.com/stretchr/testify from 1.6.0 to 1.6.1
+ * Bump github.com/opencontainers/runc from 1.0.0-rc9 to 1.0.0-rc90
+ * Add CVE-2020-10696 to CHANGELOG.md and changelog.txt
+ * Bump github.com/stretchr/testify from 1.5.1 to 1.6.0
+ * Bump github.com/onsi/ginkgo from 1.12.2 to 1.12.3
+ * Vendor in containers/common v0.12.0
+ * fix lighttpd example
+ * Vendor in new go.etcd.io/bbolt
+ * Bump github.com/onsi/ginkgo from 1.12.1 to 1.12.2
+ * Bump imagebuilder for ARG fix
+ * Bump github.com/containers/common from 0.11.2 to 0.11.4
+ * remove dependency on openshift struct
+ * Warn on unset build arguments
+ * vendor: update seccomp/containers-golang to v0.4.1
+ * Ammended docs
+ * Updated docs
+ * clean up comments
+ * update exit code for tests
+ * Implement commit for encryption
+ * implementation of encrypt/decrypt push/pull/bud/from
+ * fix resolve docker image name as transport
+ * Bump github.com/opencontainers/go-digest from 1.0.0-rc1 to 1.0.0
+ * Bump github.com/onsi/ginkgo from 1.12.0 to 1.12.1
+ * Bump github.com/containers/storage from 1.19.1 to 1.19.2
+ * Bump github.com/containers/image/v5 from 5.4.3 to 5.4.4
+ * Add preliminary profiling support to the CLI
+ * Bump github.com/containers/common from 0.10.0 to 0.11.2
+ * Evaluate symlinks in build context directory
+ * fix error info about get signatures for containerImageSource
+ * Add Security Policy
+ * Cirrus: Fixes from review feedback
+ * Bump github.com/containers/storage from 1.19.0 to 1.19.1
+ * Bump github.com/sirupsen/logrus from 1.5.0 to 1.6.0
+ * imagebuildah: stages shouldn't count as their base images
+ * Update containers/common v0.10.0
+ * Bump github.com/fsouza/go-dockerclient from 1.6.4 to 1.6.5
+ * Add registry to buildahimage Dockerfiles
+ * Cirrus: Use pre-installed VM packages + F32
+ * Cirrus: Re-enable all distro versions
+ * Cirrus: Update to F31 + Use cache images
+ * golangci-lint: Disable gosimple
+ * Lower number of golangci-lint threads
+ * Fix permissions on containers.conf
+ * Don't force tests to use runc
+ * Bump github.com/containers/common from 0.9.1 to 0.9.5
+ * Return exit code from failed containers
+ * Bump github.com/containers/storage from 1.18.2 to 1.19.0
+ * Bump github.com/containers/common from 0.9.0 to 0.9.1
+ * cgroup_manager should be under [engine]
+ * Use c/common/pkg/auth in login/logout
+ * Cirrus: Temporarily disable Ubuntu 19 testing
+ * Add containers.conf to stablebyhand build
+ * Update gitignore to exclude test Dockerfiles
+ * Bump github.com/fsouza/go-dockerclient from 1.6.3 to 1.6.4
+ * Bump github.com/containers/common from 0.8.1 to 0.9.0
+ * Bump back to v1.15.0-dev
+ * Remove warning for systemd inside of container
+
+- Changelog for v1.14.8 (2020-04-09)
+ * Run (make vendor)
+ * Run (make -C tests/tools vendor)
+ * Run (go mod tidy) before (go mod vendor) again
+ * Fix (make vendor)
+ * Bump validation
+ * Bump back to v1.15.0-dev
+
+- Changelog for v1.14.7 (2020-04-07)
+ * Bump github.com/containers/image/v5 from 5.3.1 to 5.4.3
+ * make vendor: run `tidy` after `vendor`
+ * Do not skip the directory when the ignore pattern matches
+ * Bump github.com/containers/common from 0.7.0 to 0.8.1
+ * Downgrade siruspen/logrus from 1.4.2
+ * Fix errorf conventions
+ * dockerignore tests : remove symlinks, rework
+ * Bump back to v1.15.0-dev
+
+- Changelog for v1.14.6 (2020-04-02)
+ * bud.bats - cleanup, refactoring
+ * vendor in latest containers/storage 1.18.0 and containers/common v0.7.0
+ * Bump github.com/spf13/cobra from 0.0.6 to 0.0.7
+ * Bump github.com/containers/storage from 1.16.5 to 1.17.0
+ * Bump github.com/containers/image/v5 from 5.2.1 to 5.3.1
+ * Fix Amazon install step
+ * Bump back to v1.15.0-dev
+ * Fix bud-build-arg-cache test
+ * Make image history work correctly with new args handling
+ * Don't add args to the RUN environment from the Builder
+ * Update github.com/openshift/imagebuilder to v1.1.4
+ * Add .swp files to .gitignore
+
+- Changelog for v1.14.5 (2020-03-26)
+ * revert #2246 FIPS mode change
+ * Bump back to v1.15.0-dev
+ * image with dup layers: we now have one on quay
+ * digest test : make more robust
+
+- Changelog for v1.14.4 (2020-03-25)
+ * Fix fips-mode check for RHEL8 boxes
+ * Fix potential CVE in tarfile w/ symlink (Edit 02-Jun-2020: Addresses CVE-2020-10696)
+ * Fix .dockerignore with globs and ! commands
+ * update install steps for Amazon Linux 2
+ * Bump github.com/openshift/imagebuilder from 1.1.2 to 1.1.3
+ * Add comment for RUN command in volume ownership test
+ * Run stat command directly for volume ownership test
+ * vendor in containers/common v0.6.1
+ * Cleanup go.sum
+ * Bump back to v1.15.0-dev
+
+- Changelog for v1.14.3 (2020-03-17)
+ * Update containers/storage to v1.16.5
+ * Bump github.com/containers/storage from 1.16.2 to 1.16.4
+ * Bump github.com/openshift/imagebuilder from 1.1.1 to 1.1.2
+ * Update github.com/openshift/imagebuilder vendoring
+ * Update unshare man page to fix script example
+ * Fix compilation errors on non linux platforms
+ * Bump containers/common and opencontainers/selinux versions
+ * Add tests for volume ownership
+ * Preserve volume uid and gid through subsequent commands
+ * Fix FORWARD_NULL errors found by Coverity
+ * Bump github.com/containers/storage from 1.16.1 to 1.16.2
+ * Fix errors found by codespell
+ * Bump back to v1.15.0-dev
+ * Add Pull Request Template
+
+- Changelog for v1.14.2 (2020-03-03)
+ * Add Buildah pull request template
+ * Bump to containers/storage v1.16.1
+ * run_linux: fix tight loop if file is not pollable
+ * Bump github.com/opencontainers/selinux from 1.3.2 to 1.3.3
+ * Bump github.com/containers/common from 0.4.1 to 0.4.2
+ * Bump back to v1.15.0-dev
+ * Add Containerfile to build a versioned stable image on quay.io
+
+- Changelog for v1.14.1 (2020-02-27)
+ * Search for local runtime per values in containers.conf
+ * Set correct ownership on working directory
+ * BATS : in teardown, umount stale mounts
+ * Bump github.com/spf13/cobra from 0.0.5 to 0.0.6
+ * Bump github.com/fsouza/go-dockerclient from 1.6.1 to 1.6.3
+ * Bump github.com/stretchr/testify from 1.4.0 to 1.5.1
+ * Replace unix with syscall to allow vendoring into libpod
+ * Update to containers/common v0.4.1
+ * Improve remote manifest retrieval
+ * Fix minor spelling errors in containertools README
+ * Clear the right variable in buildahimage
+ * Correct a couple of incorrect format specifiers
+ * Update to containers/common v0.3.0
+ * manifest push --format: force an image type, not a list type
+ * run: adjust the order in which elements are added to $PATH
+ * getDateAndDigestAndSize(): handle creation time not being set
+ * Bump github.com/containers/common from 0.2.0 to 0.2.1
+ * include installation steps for CentOS 8 and Stream
+ * include installation steps for CentOS7 and forks
+ * Adjust Ubuntu install info to also work on Pop!_OS
+ * Make the commit id clear like Docker
+ * Show error on copied file above context directory in build
+ * Bump github.com/containers/image/v5 from 5.2.0 to 5.2.1
+ * pull/from/commit/push: retry on most failures
+ * Makefile: fix install.cni.sudo
+ * Repair buildah so it can use containers.conf on the server side
+ * Bump github.com/mattn/go-shellwords from 1.0.9 to 1.0.10
+ * Bump github.com/fsouza/go-dockerclient from 1.6.0 to 1.6.1
+ * Fixing formatting & build instructions
+ * Add Code of Conduct
+ * Bors: Fix no. req. github reviews
+ * Cirrus+Bors: Simplify temp branch skipping
+ * Bors-ng: Add documentation and status-icon
+ * Bump github.com/onsi/ginkgo from 1.11.0 to 1.12.0
+ * fix XDG_RUNTIME_DIR for authfile
+ * Cirrus: Disable F29 testing
+ * Cirrus: Add jq package
+ * Cirrus: Fix lint + validation using wrong epoch
+ * Stop using fedorproject registry
+ * Bors: Workaround ineffective required statuses
+ * Bors: Enable app + Disable Travis
+ * Cirrus: Add standardized log-collection
+ * Cirrus: Improve automated lint + validation
+ * Allow passing options to golangci-lint
+ * Cirrus: Fixes from review feedback
+ * Cirrus: Temporarily ignore VM testing failures
+ * Cirrus: Migrate off papr + implement VM testing
+ * Cirrus: Update packages + fixes for get_ci_vm.sh
+ * Show validation command-line
+ * Skip overlay test w/ vfs driver
+ * use alpine, not centos, for various tests
+ * Flake handling: cache and prefetch images
+ * Bump to v1.15.0-dev
+
+- Changelog for v1.14.0 (2020-02-05)
+ * bump github.com/mtrmac/gpgme
+ * Update containers/common to v0.1.4
+ * manifest push: add --format option
+ * Bump github.com/onsi/gomega from 1.8.1 to 1.9.0
+ * vendor github.com/containers/image/v5@v5.2.0
+ * info test: deal with random key order
+ * Bump back to v1.14.0-dev
+
+- Changelog for v1.13.2 (2020-01-29)
+ * sign.bats: set GPG_TTY=/dev/null
+ * Fix parse_unsupported.go
+ * getDateAndDigestAndSize(): use manifest.Digest
+ * Bump github.com/opencontainers/selinux from 1.3.0 to 1.3.1
+ * Bump github.com/containers/common from 0.1.0 to 0.1.2
+ * Touch up os/arch doc
+ * chroot: handle slightly broken seccomp defaults
+ * buildahimage: specify fuse-overlayfs mount options
+ * Bump github.com/mattn/go-shellwords from 1.0.7 to 1.0.9
+ * copy.bats: make sure we detect failures due to missing source
+ * parse: don't complain about not being able to rename something to itself
+ * Makefile: use a $(GO_TEST) macro, fix a typo
+ * manifests: unit test fix
+ * Fix build for 32bit platforms
+ * Allow users to set OS and architecture on bud
+ * Fix COPY in containerfile with envvar
+ * Bump c/storage to v1.15.7
+ * add --sign-by to bud/commit/push, --remove-signatures for pull/push
+ * Remove cut/paste error in CHANGELOG.md
+ * Update vendor of containers/common to v0.1.0
+ * update install instructions for Debian, Raspbian and Ubuntu
+ * Add support for containers.conf
+ * Bump back to v1.14.0-dev
+
+- Changelog for v1.13.1 (2020-01-14)
+ * Bump github.com/containers/common from 0.0.5 to 0.0.7
+ * Bump github.com/onsi/ginkgo from 1.10.3 to 1.11.0
+ * Bump github.com/pkg/errors from 0.8.1 to 0.9.0
+ * Bump github.com/onsi/gomega from 1.7.1 to 1.8.1
+ * Add codespell support
+ * copyFileWithTar: close source files at the right time
+ * copy: don't digest files that we ignore
+ * Check for .dockerignore specifically
+ * Travis: rm go 1.12.x
+ * Don't setup excludes, if their is only one pattern to match
+ * set HOME env to /root on chroot-isolation by default
+ * docs: fix references to containers-*.5
+ * update openshift/api
+ * fix bug Add check .dockerignore COPY file
+ * buildah bud --volume: run from tmpdir, not source dir
+ * Fix imageNamePrefix to give consistent names in buildah-from
+ * cpp: use -traditional and -undef flags
+ * Fix image reference in tutorial 4
+ * discard outputs coming from onbuild command on buildah-from --quiet
+ * make --format columnizing consistent with buildah images
+ * Bump to v1.14.0-dev
+
+- Changelog for v1.13.0 (2019-12-27)
+ * Bump to c/storage v1.15.5
+ * Update container/storage to v1.15.4
+ * Fix option handling for volumes in build
+ * Rework overlay pkg for use with libpod
+ * Fix buildahimage builds for buildah
+ * Add support for FIPS-Mode backends
+ * Set the TMPDIR for pulling/pushing image to $TMPDIR
+ * WIP: safer test for pull --all-tags
+ * BATS major cleanup: blobcache.bats: refactor
+ * BATS major cleanup: part 4: manual stuff
+ * BATS major cleanup, step 3: yet more run_buildah
+ * BATS major cleanup, part 2: use more run_buildah
+ * BATS major cleanup, part 1: log-level
+ * Bump github.com/containers/image/v5 from 5.0.0 to 5.1.0
+ * Bump github.com/containers/common from 0.0.3 to 0.0.5
+ * Bump to v1.13.0-dev
+
+- Changelog for v1.12.0 (2019-12-13)
+ * Allow ADD to use http src
+ * Bump to c/storage v.1.15.3
+ * install.md: update golang dependency
+ * imgtype: reset storage opts if driver overridden
+ * Start using containers/common
+ * overlay.bats typo: fuse-overlays should be fuse-overlayfs
+ * chroot: Unmount with MNT_DETACH instead of UnmountMountpoints()
+ * bind: don't complain about missing mountpoints
+ * imgtype: check earlier for expected manifest type
+ * Vendor containers/storage fix
+ * Vendor containers/storage v1.15.1
+ * Add history names support
+ * PR takeover of #1966
+ * Tests: Add inspect test check steps
+ * Tests: Add container name and id check in containers test steps
+ * Test: Get permission in add test
+ * Tests: Add a test for tag by id
+ * Tests: Add test cases for push test
+ * Tests: Add image digest test
+ * Tests: Add some buildah from tests
+ * Tests: Add two commit test
+ * Tests: Add buildah bud with --quiet test
+ * Tests: Add two test for buildah add
+ * Bump back to v1.12.0-dev
+
+- Changelog for v1.11.6 (2019-12-03)
+ * Handle missing equal sign in --from and --chown flags for COPY/ADD
+ * bud COPY does not download URL
+ * Bump github.com/onsi/gomega from 1.7.0 to 1.7.1
+ * Fix .dockerignore exclude regression
+ * Ran buildah through codespell
+ * commit(docker): always set ContainerID and ContainerConfig
+ * Touch up commit man page image parameter
+ * Add builder identity annotations.
+ * info: use util.Runtime()
+ * Bump github.com/onsi/ginkgo from 1.10.2 to 1.10.3
+ * Bump back to v1.12.0-dev
+
+- Changelog for v1.11.5 (2019-11-11)
+ * Enhance error on unsafe symbolic link targets
+ * Add OCIRuntime to info
+ * Check nonexsit authfile
+ * Only output image id if running buildah bud --quiet
+ * Fix --pull=true||false and add --pull-never to bud and from (retry)
+ * cgroups v2: tweak or skip tests
+ * Prepwork: new 'skip' helpers for tests
+ * Handle configuration blobs for manifest lists
+ * unmarshalConvertedConfig: avoid using the updated image's ref
+ * Add completions for Manifest commands
+ * Add disableFips option to secrets pkg
+ * Update bud.bats test archive test
+ * Add test for caching based on content digest
+ * Builder.untarPath(): always evaluate b.ContentDigester.Hash()
+ * Bump github.com/onsi/ginkgo from 1.10.1 to 1.10.2
+ * Fix another broken test: copy-url-mtime
+ * yet more fixes
+ * Actual bug fix for 'add' test: fix the expected mode
+ * BATS tests - lots of mostly minor cleanup
+ * build: drop support for ostree
+ * Add support for make vendor-in-container
+ * imgtype: exit with error if storage fails
+ * remove XDG_RUNTIME_DIR from default authfile path
+ * fix troubleshooting redirect instructions
+ * Bump back to v1.12.0-dev
+
+- Changelog for v1.11.4 (2019-10-28)
+ * buildah: add a "manifest" command
+ * manifests: add the module
+ * pkg/supplemented: add a package for grouping images together
+ * pkg/manifests: add a manifest list build/manipulation API
+ * Update for ErrUnauthorizedForCredentials API change in containers/image
+ * Update for manifest-lists API changes in containers/image
+ * version: also note the version of containers/image
+ * Move to containers/image v5.0.0
+ * Enable --device directory as src device
+ * Fix git build with branch specified
+ * Bump github.com/openshift/imagebuilder from 1.1.0 to 1.1.1
+ * Bump github.com/fsouza/go-dockerclient from 1.4.4 to 1.5.0
+ * Add clarification to the Tutorial for new users
+ * Silence "using cache" to ensure -q is fully quiet
+ * Add OWNERS File to Buildah
+ * Bump github.com/containers/storage from 1.13.4 to 1.13.5
+ * Move runtime flag to bud from common
+ * Commit: check for storage.ErrImageUnknown using errors.Cause()
+ * Fix crash when invalid COPY --from flag is specified.
+ * Bump back to v1.12.0-dev
+
+- Changelog for v1.11.3 (2019-10-04)
+ * Update c/image to v4.0.1
+ * Bump github.com/spf13/pflag from 1.0.3 to 1.0.5
+ * Fix --build-args handling
+ * Bump github.com/spf13/cobra from 0.0.3 to 0.0.5
+ * Bump github.com/cyphar/filepath-securejoin from 0.2.1 to 0.2.2
+ * Bump github.com/onsi/ginkgo from 1.8.0 to 1.10.1
+ * Bump github.com/fsouza/go-dockerclient from 1.3.0 to 1.4.4
+ * Add support for retrieving context from stdin "-"
+ * Ensure bud remote context cleans up on error
+ * info: add cgroups2
+ * Bump github.com/seccomp/libseccomp-golang from 0.9.0 to 0.9.1
+ * Bump github.com/mattn/go-shellwords from 1.0.5 to 1.0.6
+ * Bump github.com/stretchr/testify from 1.3.0 to 1.4.0
+ * Bump github.com/opencontainers/selinux from 1.2.2 to 1.3.0
+ * Bump github.com/etcd-io/bbolt from 1.3.2 to 1.3.3
+ * Bump github.com/onsi/gomega from 1.5.0 to 1.7.0
+ * update c/storage to v1.13.4
+ * Print build 'STEP' line to stdout, not stderr
+ * Fix travis-ci on forks
+ * Vendor c/storage v1.13.3
+ * Use Containerfile by default
+ * Added tutorial on how to include Buildah as library
+ * util/util: Fix "configuraitno" -> "configuration" log typo
+ * Bump back to v1.12.0-dev
+
+- Changelog for v1.11.2 (2019-09-13)
+ * Add some cleanup code
+ * Move devices code to unit specific directory.
+ * Bump back to v1.12.0-dev
+
+- Changelog for v1.11.1 (2019-09-11)
+ * Add --devices flag to bud and from
+ * Downgrade .papr to highest atomic verion
+ * Add support for /run/.containerenv
+ * Truncate output of too long image names
+ * Preserve file and directory mount permissions
+ * Bump fedora version from 28 to 30
+ * makeImageRef: ignore EmptyLayer if Squash is set
+ * Set TMPDIR to /var/tmp by default
+ * replace --debug=false with --log-level=error
+ * Allow mounts.conf entries for equal source and destination paths
+ * fix label and annotation for 1-line Dockerfiles
+ * Enable interfacer linter and fix lints
+ * install.md: mention goproxy
+ * Makefile: use go proxy
+ * Bump to v1.12.0-dev
+
+- Changelog for v1.11.0 (2019-08-29)
+ * tests/bud.bats: add --signature-policy to some tests
+ * Vendor github.com/openshift/api
+ * pull/commit/push: pay attention to $BUILD_REGISTRY_SOURCES
+ * Add `--log-level` command line option and deprecate `--debug`
+ * add support for cgroupsV2
+ * Correctly detect ExitError values from Run()
+ * Disable empty logrus timestamps to reduce logger noise
+ * Remove outdated deps Makefile target
+ * Remove gofmt.sh in favor of golangci-lint
+ * Remove govet.sh in favor of golangci-lint
+ * Allow to override build date with SOURCE_DATE_EPOCH
+ * Update shebangs to take env into consideration
+ * Fix directory pull image names
+ * Add --digestfile and Re-add push statement as debug
+ * README: mention that Podman uses Buildah's API
+ * Use content digests in ADD/COPY history entries
+ * add: add a DryRun flag to AddAndCopyOptions
+ * Fix possible runtime panic on bud
+ * Add security-related volume options to validator
+ * use correct path for ginkgo
+ * Add bud 'without arguments' integration tests
+ * Update documentation about bud
+ * add: handle hard links when copying with .dockerignore
+ * add: teach copyFileWithTar() about symlinks and directories
+ * Allow buildah bud to be called without arguments
+ * imagebuilder: fix detection of referenced stage roots
+ * Touch up go mod instructions in install
+ * run_linux: fix mounting /sys in a userns
+ * Vendor Storage v1.13.2
+ * Cirrus: Update VM images
+ * Fix handling of /dev/null masked devices
+ * Update `bud`/`from` help to contain indicator for `--dns=none`
+ * Bump back to v1.11.0-dev
+
+- Changelog for v1.10.1 (2019-08-08)
+ * Bump containers/image to v3.0.2 to fix keyring issue
+ * Bug fix for volume minus syntax
+ * Bump container/storage v1.13.1 and containers/image v3.0.1
+ * bump github.com/containernetworking/cni to v0.7.1
+ * Add overlayfs to fuse-overlayfs tip
+ * Add automatic apparmor tag discovery
+ * Fix bug whereby --get-login has no effect
+ * Bump to v1.11.0-dev
+
+- Changelog for v1.10.0 (2019-08-02)
+ * vendor github.com/containers/image@v3.0.0
+ * Remove GO111MODULE in favor of `-mod=vendor`
+ * Vendor in containers/storage v1.12.16
+ * Add '-' minus syntax for removal of config values
+ * tests: enable overlay tests for rootless
+ * rootless, overlay: use fuse-overlayfs
+ * vendor github.com/containers/image@v2.0.1
+ * Added '-' syntax to remove volume config option
+ * delete `successfully pushed` message
+ * Add golint linter and apply fixes
+ * vendor github.com/containers/storage@v1.12.15
+ * Change wait to sleep in buildahimage readme
+ * Handle ReadOnly images when deleting images
+ * Add support for listing read/only images
+
+- Changelog for v1.9.2 (2019-07-19)
+ * from/import: record the base image's digest, if it has one
+ * Fix CNI version retrieval to not require network connection
+ * Add misspell linter and apply fixes
+ * Add goimports linter and apply fixes
+ * Add stylecheck linter and apply fixes
+ * Add unconvert linter and apply fixes
+ * image: make sure we don't try to use zstd compression
+ * run.bats: skip the "z" flag when testing --mount
+ * Update to runc v1.0.0-rc8
+ * Update to match updated runtime-tools API
+ * bump github.com/opencontainers/runtime-tools to v0.9.0
+ * Build e2e tests using the proper build tags
+ * Add unparam linter and apply fixes
+ * Run: correct a typo in the --cap-add help text
+ * unshare: add a --mount flag
+ * fix push check image name is not empty
+ * Bump to v1.9.2-dev
+
+- Changelog for v1.9.1 (2019-07-12)
+ * add: fix slow copy with no excludes
+ * Add errcheck linter and fix missing error check
+ * Improve tests/tools/Makefile parallelism and abstraction
+ * Fix response body not closed resource leak
+ * Switch to golangci-lint
+ * Add gomod instructions and mailing list links
+ * On Masked path, check if /dev/null already mounted before mounting
+ * Update to containers/storage v1.12.13
+ * Refactor code in package imagebuildah
+ * Add rootless podman with NFS issue in documentation
+ * Add --mount for buildah run
+ * import method ValidateVolumeOpts from libpod
+ * Fix typo
+ * Makefile: set GO111MODULE=off
+ * rootless: add the built-in slirp DNS server
+ * Update docker/libnetwork to get rid of outdated sctp package
+ * Update buildah-login.md
+ * migrate to go modules
+ * install.md: mention go modules
+ * tests/tools: go module for test binaries
+ * fix --volume splits comma delimited option
+ * Add bud test for RUN with a priv'd command
+ * vendor logrus v1.4.2
+ * pkg/cli: panic when flags can't be hidden
+ * pkg/unshare: check all errors
+ * pull: check error during report write
+ * run_linux.go: ignore unchecked errors
+ * conformance test: catch copy error
+ * chroot/run_test.go: export funcs to actually be executed
+ * tests/imgtype: ignore error when shutting down the store
+ * testreport: check json error
+ * bind/util.go: remove unused func
+ * rm chroot/util.go
+ * imagebuildah: remove unused `dedupeStringSlice`
+ * StageExecutor: EnsureContainerPath: catch error from SecureJoin()
+ * imagebuildah/build.go: return instead of branching
+ * rmi: avoid redundant branching
+ * conformance tests: nilness: allocate map
+ * imagebuildah/build.go: avoid redundant `filepath.Join()`
+ * imagebuildah/build.go: avoid redundant `os.Stat()`
+ * imagebuildah: omit comparison to bool
+ * fix "ineffectual assignment" lint errors
+ * docker: ignore "repeats json tag" lint error
+ * pkg/unshare: use `...` instead of iterating a slice
+ * conformance: bud test: use raw strings for regexes
+ * conformance suite: remove unused func/var
+ * buildah test suite: remove unused vars/funcs
+ * testreport: fix golangci-lint errors
+ * util: remove redundant `return` statement
+ * chroot: only log clean-up errors
+ * images_test: ignore golangci-lint error
+ * blobcache: log error when draining the pipe
+ * imagebuildah: check errors in deferred calls
+ * chroot: fix error handling in deferred funcs
+ * cmd: check all errors
+ * chroot/run_test.go: check errors
+ * chroot/run.go: check errors in deferred calls
+ * imagebuildah.Executor: remove unused onbuild field
+ * docker/types.go: remove unused struct fields
+ * util: use strings.ContainsRune instead of index check
+ * Cirrus: Initial implementation
+ * Bump to v1.9.1-dev
+
+- Changelog for v1.9.0 (2019-06-15)
+ * buildah-run: fix-out-of-range panic (2)
+ * Bump back to v1.9.0-dev
+
+- Changelog for v1.8.4 (2019-06-13)
+ Update containers/image to v2.0.0
+ run: fix hang with run and --isolation=chroot
+ run: fix hang when using run
+ chroot: drop unused function call
+ remove --> before imgageID on build
+ Always close stdin pipe
+ Write deny to setgroups when doing single user mapping
+ Avoid including linux/memfd.h
+ Add a test for the symlink pointing to a directory
+ Add missing continue
+ Fix the handling of symlinks to absolute paths
+ Only set default network sysctls if not rootless
+ Support --dns=none like podman
+ fix bug --cpu-shares parsing typo
+ Fix validate complaint
+ Update vendor on containers/storage to v1.12.10
+ Create directory paths for COPY thereby ensuring correct perms
+ imagebuildah: use a stable sort for comparing build args
+ imagebuildah: tighten up cache checking
+ bud.bats: add a test verying the order of --build-args
+ add -t to podman run
+ imagebuildah: simplify screening by top layers
+ imagebuildah: handle ID mappings for COPY --from
+ imagebuildah: apply additionalTags ourselves
+ bud.bats: test additional tags with cached images
+ bud.bats: add a test for WORKDIR and COPY with absolute destinations
+ Cleanup Overlay Mounts content
+
+- Changelog for v1.8.3 (2019-06-04)
+ * Add support for file secret mounts
+ * Add ability to skip secrets in mounts file
+ * allow 32bit builds
+ * fix tutorial instructions
+ * imagebuilder: pass the right contextDir to Add()
+ * add: use fileutils.PatternMatcher for .dockerignore
+ * bud.bats: add another .dockerignore test
+ * unshare: fallback to single usermapping
+ * addHelperSymlink: clear the destination on os.IsExist errors
+ * bud.bats: test replacing symbolic links
+ * imagebuildah: fix handling of destinations that end with '/'
+ * bud.bats: test COPY with a final "/" in the destination
+ * linux: add check for sysctl before using it
+ * unshare: set _CONTAINERS_ROOTLESS_GID
+ * Rework buildahimamges
+ * build context: support https git repos
+ * Add a test for ENV special chars behaviour
+ * Check in new Dockerfiles
+ * Apply custom SHELL during build time
+ * config: expand variables only at the command line
+ * SetEnv: we only need to expand v once
+ * Add default /root if empty on chroot iso
+ * Add support for Overlay volumes into the container.
+ * Export buildah validate volume functions so it can share code with libpod
+ * Bump baseline test to F30
+ * Fix rootless handling of /dev/shm size
+ * Avoid fmt.Printf() in the library
+ * imagebuildah: tighten cache checking back up
+ * Handle WORKDIR with dangling target
+ * Default Authfile to proper path
+ * Make buildah run --isolation follow BUILDAH_ISOLATION environment
+ * Vendor in latest containers/storage and containers/image
+ * getParent/getChildren: handle layerless images
+ * imagebuildah: recognize cache images for layerless images
+ * bud.bats: test scratch images with --layers caching
+ * Get CHANGELOG.md updates
+ * Add some symlinks to test our .dockerignore logic
+ * imagebuildah: addHelper: handle symbolic links
+ * commit/push: use an everything-allowed policy
+ * Correct manpage formatting in files section
+ * Remove must be root statement from buildah doc
+ * Change image names to stable, testing and upstream
+ * Bump back to v1.9.0-dev
+
+- Changelog for v1.8.2 (2019-05-02)
+ * Vendor Storage 1.12.6
+ * Create scratch file in TESTDIR
+ * Test bud-copy-dot with --layers picks up changed file
+ * Bump back to 1.9.0-dev
+
+- Changelog for v1.8.1 (2019-05-01)
+ * Don't create directory on container
+ * Replace kubernetes/pause in tests with k8s.gcr.io/pause
+ * imagebuildah: don't remove intermediate images if we need them
+ * Rework buildahimagegit to buildahimageupstream
+ * Fix Transient Mounts
+ * Handle WORKDIRs that are symlinks
+ * allow podman to build a client for windows
+ * Touch up 1.9-dev to 1.9.0-dev
+ * Bump to 1.9-dev
+
+- Changelog for v1.8.0 (2019-04-26)
+ * Resolve symlink when checking container path
+ * commit: commit on every instruction, but not always with layers
+ * CommitOptions: drop the unused OnBuild field
+ * makeImageRef: pass in the whole CommitOptions structure
+ * cmd: API cleanup: stores before images
+ * run: check if SELinux is enabled
+ * Fix buildahimages Dockerfiles to include support for additionalimages mounted from host.
+ * Detect changes in rootdir
+ * Fix typo in buildah-pull(1)
+ * Vendor in latest containers/storage
+ * Keep track of any build-args used during buildah bud --layers
+ * commit: always set a parent ID
+ * imagebuildah: rework unused-argument detection
+ * fix bug dest path when COPY .dockerignore
+ * Move Host IDMAppings code from util to unshare
+ * Add BUILDAH_ISOLATION rootless back
+ * Travis CI: fail fast, upon error in any step
+ * imagebuildah: only commit images for intermediate stages if we have to
+ * Use errors.Cause() when checking for IsNotExist errors
+ * auto pass http_proxy to container
+ * Bump back to 1.8-dev
+
+- Changelog for v1.7.3 (2019-04-16)
+ * imagebuildah: don't leak image structs
+ * Add Dockerfiles for buildahimages
+ * Bump to Replace golang 1.10 with 1.12
+ * add --dns* flags to buildah bud
+ * Add hack/build_speed.sh test speeds on building container images
+ * Create buildahimage Dockerfile for Quay
+ * rename 'is' to 'expect_output'
+ * squash.bats: test squashing in multi-layered builds
+ * bud.bats: test COPY --from in a Dockerfile while using the cache
+ * commit: make target image names optional
+ * Fix bud-args to allow comma separation
+ * oops, missed some tests in commit.bats
+ * new helper: expect_line_count
+ * New tests for #1467 (string slices in cmdline opts)
+ * Workarounds for dealing with travis; review feedback
+ * BATS tests - extensive but minor cleanup
+ * imagebuildah: defer pulling images for COPY --from
+ * imagebuildah: centralize COMMIT and image ID output
+ * Travis: do not use traviswait
+ * imagebuildah: only initialize imagebuilder configuration once per stage
+ * Make cleaner error on Dockerfile build errors
+ * unshare: move to pkg/
+ * unshare: move some code from cmd/buildah/unshare
+ * Fix handling of Slices versus Arrays
+ * imagebuildah: reorganize stage and per-stage logic
+ * imagebuildah: add empty layers for instructions
+ * Add missing step in installing into Ubuntu
+ * fix bug in .dockerignore support
+ * imagebuildah: deduplicate prepended "FROM" instructions
+ * Touch up intro
+ * commit: set created-by to the shell if it isn't set
+ * commit: check that we always set a "created-by"
+ * docs/buildah.md: add "containers-" prefixes under "SEE ALSO"
+ * Bump back to 1.8-dev
+
+- Changelog for v1.7.2 (2019-03-28)
+ * mount: do not create automatically a namespace
+ * buildah: correctly create the userns if euid!=0
+ * imagebuildah.Build: consolidate cleanup logic
+ * CommitOptions: drop the redundant Store field
+ * Move pkg/chrootuser from libpod to buildah.
+ * imagebuildah: record image IDs and references more often
+ * vendor imagebuilder v1.1.0
+ * imagebuildah: fix requiresStart/noRunsRemaining confusion
+ * imagebuildah: check for unused args across stages
+ * bump github.com/containernetworking/cni to v0.7.0-rc2
+ * imagebuildah: use "useCache" instead of "noCache"
+ * imagebuildah.resolveNameToImageRef(): take name as a parameter
+ * Export fields of the DokcerIgnore struct
+ * imagebuildah: drop the duplicate containerIDs list
+ * rootless: by default use the host network namespace
+ * imagebuildah: split Executor and per-stage execution
+ * imagebuildah: move some fields around
+ * golint: make golint happy
+ * docs: 01-intro.md: add missing . in Dockerfile examples
+ * fix bug using .dockerignore
+ * Do not create empty mounts.conf file
+ * images: suppress a spurious blank line with no images
+ * from: distinguish between ADD and COPY
+ * fix bug to not separate each --label value with comma
+ * buildah-bud.md: correct a typo, note a default
+ * Remove mistaken code that got merged in other PR
+ * add sample registries.conf to docs
+ * escape shell variables in README example
+ * slirp4netns: set mtu to 65520
+ * images: imageReposToMap() already adds :
+ * imagebuildah.ReposToMap: move to cmd
+ * Build: resolve copyFrom references earlier
+ * Allow rootless users to use the cache directory in homedir
+ * bud.bats: use the per-test temp directory
+ * bud.bats: log output before counting length
+ * Simplify checks for leftover args
+ * Print commitID with --layers
+ * fix bug images use the template to print results
+ * rootless: honor --net host
+ * onsi/gomeage add missing files
+ * vendor latest openshift/imagebuilder
+ * Remove noop from squash help
+ * Prepend a comment to files setup in container
+ * imagebuildah resolveSymlink: fix handling of relative links
+ * Errors should be printed to stderr
+ * Add recommends for slirp4netns and fuse-overlay
+ * Update pull and pull-always flags
+ * Hide from users command options that we don't want them to use.
+ * Update secrets fipsmode patch to work on rootless containers
+ * fix unshare option handling and documentation
+ * Vendor in latest containers/storage
+ * Hard-code docker.Transport use in pull --all-tags
+ * Use a types.ImageReference instead of (transport, name) strings in pullImage etc.
+ * Move the computation of srcRef before first pullAndFindImage
+ * Don't throw away user-specified tag for pull --all-tags
+ * CHANGES BEHAVIOR: Remove the string format input to localImageNameForReference
+ * Don't try to parse imageName as transport:image in pullImage
+ * Use reference.WithTag instead of manual string manipulation in Pull
+ * Don't pass image = transport:repo:tag, transport=transport to pullImage
+ * Fix confusing variable naming in Pull
+ * Don't try to parse image name as a transport:image
+ * Fix error reporting when parsing trans+image
+ * Remove 'transport == ""' handling from the pull path
+ * Clean up "pulls" of local image IDs / ID prefixes
+ * Simplify ExpandNames
+ * Document the semantics of transport+name returned by ResolveName
+ * UPdate gitvalidation epoch
+ * Bump back to 1.8-dev
+
+- Changelog for v1.7.1 (2019-02-26)
+ * vendor containers/image v1.5
+ * Move secrets code from libpod into buildah
+ * Update CHANGELOG.md with the past changes
+ * README.md: fix typo
+ * Fix a few issues found by tests/validate/gometalinter.sh
+ * Neutralize buildah/unshare on non-Linux platforms
+ * Explicitly specify a directory to find(1)
+ * README.md: rephrase Buildah description
+ * Stop printing default twice in cli --help
+ * install.md: add section about vendoring
+ * Bump to 1.8-dev
+
+- Changelog for v1.7 (2019-02-21)
+ * vendor containers/image v1.4
+ * Make "images --all" faster
+ * Remove a misleading comment
+ * Remove quiet option from pull options
+ * Make sure buildah pull --all-tags only works with docker transport
+ * Support oci layout format
+ * Fix pulling of images within buildah
+ * Fix tls-verify polarity
+ * Travis: execute make vendor and hack/tree_status.sh
+ * vendor.conf: remove unused dependencies
+ * add missing vendor/github.com/containers/libpod/vendor.conf
+ * vendor.conf: remove github.com/inconshreveable/mousetrap
+ * make vendor: always fetch the latest vndr
+ * add hack/tree_status.sh script
+ * Bump c/Storage to 1.10
+ * Add --all-tags test to pull
+ * mount: make error clearer
+ * Remove global flags from cli help
+ * Set --disable-compression to true as documented
+ * Help document using buildah mount in rootless mode
+ * healthcheck start-period: update documentation
+ * Vendor in latest c/storage and c/image
+ * dumpbolt: handle nested buckets
+ * Fix buildah commit compress by default
+ * Test on xenial, not trusty
+ * unshare: reexec using a memfd copy instead of the binary
+ * Add --target to bud command
+ * Fix example for setting multiple environment variables
+ * main: fix rootless mode
+ * buildah: force umask 022
+ * pull.bats: specify registry config when using registries
+ * pull.bats: use the temporary directory, not /tmp
+ * unshare: do not set rootless mode if euid=0
+ * Touch up cli help examples and a few nits
+ * Add an undocumented dumpbolt command
+ * Move tar commands into containers/storage
+ * Fix bud issue with 2 line Dockerfile
+ * Add package install descriptions
+ * Note configuration file requirements
+ * Replace urfave/cli with cobra
+ * cleanup vendor.conf
+ * Vendor in latest containers/storage
+ * Add Quiet to PullOptions and PushOptions
+ * cmd/commit: add flag omit-timestamp to allow for deterministic builds
+ * Add options for empty-layer history entries
+ * Make CLI help descriptions and usage a bit more consistent
+ * vndr opencontainers/selinux
+ * Bump baseline test Fedora to 29
+ * Bump to v1.7-dev-1
+ * Bump to v1.6-1
+ * Add support for ADD --chown
+ * imagebuildah: make EnsureContainerPath() check/create the right one
+ * Bump 1.7-dev
+ * Fix contrib/rpm/bulidah.spec changelog date
+
+- Changelog for v1.6-1 (2019-01-18)
+ * Add support for ADD --chown
+ * imagebuildah: make EnsureContainerPath() check/create the right one
+ * Fix contrib/rpm/bulidah.spec changelog date
+ * Vendor in latest containers/storage
+ * Revendor everything
+ * Revendor in latest code by release
+ * unshare: do not set USER=root
+ * run: ignore EIO when flushing at the end, avoid double log
+ * build-using-dockerfile,commit: disable compression by default
+ * Update some comments
+ * Make rootless work under no_pivot_root
+ * Add CreatedAtRaw date field for use with Format
+ * Properly format images JSON output
+ * pull: add all-tags option
+ * Fix support for multiple Short options
+ * pkg/blobcache: add synchronization
+ * Skip empty files in file check of conformance test
+ * Use NoPivot also for RUN, not only for run
+ * Remove no longer used isReferenceInsecure / isRegistryInsecure
+ * Do not set OCIInsecureSkipTLSVerify based on registries.conf
+ * Remove duplicate entries from images JSON output
+ * vendor parallel-copy from containers/image
+ * blobcache.bats: adjust explicit push tests
+ * Handle one line Dockerfile with layers
+ * We should only warn if user actually requests Hostname be set in image
+ * Fix compiler Warning about comparing different size types
+ * imagebuildah: don't walk if rootdir and path are equal
+ * Add aliases for buildah containers, so buildah list, ls and ps work
+ * vendor: use faster version instead compress/gzip
+ * vendor: update libpod
+ * Properly handle Hostname inside of RUN command
+ * docs: mention how to mount in rootless mode
+ * tests: use fully qualified name for centos image
+ * travis.yml: use the fully qualified name for alpine
+ * mount: allow mount only when using vfs
+ * Add some tests for buildah pull
+ * Touch up images -q processing
+ * Refactor: Use library shared idtools.ParseIDMap() instead of bundling it
+ * bump GITVALIDATE_EPOCH
+ * cli.BudFlags: add `--platform` nop
+ * Makefile: allow packagers to more easily add tags
+ * Makefile: soften the requirement on git
+ * tests: add containers json test
+ * Inline blobCache.putBlob into blobCacheDestination.PutBlob
+ * Move saveStream and putBlob near blobCacheDestination.PutBlob
+ * Remove BlobCache.PutBlob
+ * Update for API changes
+ * Vendor c/image after merging c/image#536
+ * Handle 'COPY --from' in Dockerfile
+ * Vendor in latest content from github.com/containers/storage
+ * Clarify docker.io default in push with docker-daemon
+ * Test blob caching
+ * Wire in a hidden --blob-cache option
+ * Use a blob cache when we're asked to use one
+ * Add --disable-compression to 'build-using-dockerfile'
+ * Add a blob cache implementation
+ * vendor: update containers/storage
+ * Update for sysregistriesv2 API changes
+ * Update containers/image to 63a1cbdc5e6537056695cf0d627c0a33b334df53
+ * clean up makefile variables
+ * Fix file permission
+ * Complete the instructions for the command
+ * Show warning when a build arg not used
+ * Assume user 0 group 0, if /etc/passwd file in container.
+ * Add buildah info command
+ * Enable -q when --filter is used for images command
+ * Add v1.5 Release Announcement
+ * Fix dangling filter for images command
+ * Fix completions to print Names as well as IDs
+ * tests: Fix file permissions
+ * Bump 1.6-dev
+
+- Changelog for v1.5-1 (2018-11-21)
+ * Bump min go to 1.10 in install.md
+ * vendor: update ostree-go
+ * Update docker build command line in conformance test
+ * Print command in SystemExec as debug information
+ * Add some skip word for inspect check in conformance test
+ * Update regex for multi stage base test
+ * Sort CLI flags
+ * vendor: update containers/storage
+ * Add note to install about non-root on RHEL/CentOS
+ * Update imagebuild depdency to support heading ARGs in Dockerfile
+ * rootless: do not specify --rootless to the OCI runtime
+ * Export resolvesymlink function
+ * Exclude --force-rm from common bud cli flags
+ * run: bind mount /etc/hosts and /etc/resolv.conf if not in a volume
+ * rootless: use slirp4netns to setup the network namespace
+ * Instructions for completing the pull command
+ * Fix travis to not run environment variable patch
+ * rootless: only discard network configuration names
+ * run: only set up /etc/hosts or /etc/resolv.conf with network
+ * common: getFormat: match entire string not only the prefix
+ * vendor: update libpod
+ * Change validation EPOCH
+ * Fixing broken link for container-registries.conf
+ * Restore rootless isolation test for from volume ro test
+ * ostree: fix tag for build constraint
+ * Handle directories better in bud -f
+ * vndr in latest containers/storage
+ * Fix unshare gofmt issue
+ * runSetupBuiltinVolumes(): break up volume setup
+ * common: support a per-user registries conf file
+ * unshare: do not override the configuration
+ * common: honor the rootless configuration file
+ * unshare: create a new mount namespace
+ * unshare: support libpod rootless pkg
+ * Use libpod GetDefaultStorage to report proper storage config
+ * Allow container storage to manage the SELinux labels
+ * Resolve image names with default transport in from command
+ * run: When the value of isolation is set, use the set value instead of the default value.
+ * Vendor in latest containers/storage and opencontainers/selinux
+ * Remove no longer valid todo
+ * Check for empty buildTime in version
+ * Change gofmt so it runs on all but 1.10
+ * Run gofmt only on Go 1.11
+ * Walk symlinks when checking cached images for copied/added files
+ * ReserveSELinuxLabels(): handle wrapped errors from OpenBuilder
+ * Set WorkingDir to empty, not / for conformance
+ * Update calls in e2e to addres 1101
+ * imagebuilder.BuildDockerfiles: return the image ID
+ * Update for changes in the containers/image API
+ * bump(github.com/containers/image)
+ * Allow setting --no-pivot default with an env var
+ * Add man page and bash completion, for --no-pivot
+ * Add the --no-pivot flag to the run command
+ * Improve reporting about individual pull failures
+ * Move the "short name but no search registries" error handling to resolveImage
+ * Return a "search registries were needed but empty" indication in util.ResolveName
+ * Simplify handling of the "tried to pull an image but found nothing" case in newBuilder
+ * Don't even invoke the pull loop if options.FromImage == ""
+ * Eliminate the long-running ref and img variables in resolveImage
+ * In resolveImage, return immediately on success
+ * Fix From As in Dockerfile
+ * Vendor latest containers/image
+ * Vendor in latest libpod
+ * Sort CLI flags of buildah bud
+ * Change from testing with golang 1.9 to 1.11.
+ * unshare: detect when unprivileged userns are disabled
+ * Optimize redundant code
+ * fix missing format param
+ * chroot: fix the args check
+ * imagebuildah: make ResolveSymLink public
+ * Update copy chown test
+ * buildah: use the same logic for XDG_RUNTIME_DIR as podman
+ * V1.4 Release Announcement
+ * Podman --privileged selinux is broken
+ * papr: mount source at gopath
+ * parse: Modify the return value
+ * parse: modify the verification of the isolation value
+ * Make sure we log or return every error
+ * pullImage(): when completing an image name, try docker://
+ * Fix up Tutorial 3 to account for format
+ * Vendor in latest containers/storage and containers/image
+ * docs/tutorials/01-intro.md: enhanced installation instructions
+ * Enforce "blocked" for registries for the "docker" transport
+ * Correctly set DockerInsecureSkipTLSVerify when pulling images
+ * chroot: set up seccomp and capabilities after supplemental groups
+ * chroot: fix capabilities list setup and application
+ * .papr.yml: log the podman version
+ * namespaces.bats: fix handling of uidmap/gidmap options in pairs
+ * chroot: only create user namespaces when we know we need them
+ * Check /proc/sys/user/max_user_namespaces on unshare(NEWUSERNS)
+ * bash/buildah: add isolation option to the from command
+
+- Changelog for v1.4 (2018-10-02)
+ * from: fix isolation option
+ * Touchup pull manpage
+ * Export buildah ReserveSELinuxLables so podman can use it
+ * Add buildah.io to README.md and doc fixes
+ * Update rmi man for prune changes
+ * Ignore file not found removal error in bud
+ * bump(github.com/containers/{storage,image})
+ * NewImageSource(): only create one Diff() at a time
+ * Copy ExposedPorts from base image into the config
+ * tests: run conformance test suite in Travis
+ * Change rmi --prune to not accept an imageID
+ * Clear intermediate container IDs after each stage
+ * Request podman version for build issues
+ * unshare: keep the additional groups of the user
+ * Builtin volumes should be owned by the UID/GID of the container
+ * Get rid of dangling whitespace in markdown files
+ * Move buildah from projecatatomic/buildah to containers/buildah
+ * nitpick: parse.validateFlags loop in bud cli
+ * bash: Completion options
+ * Add signature policy to push tests
+ * vendor in latest containers/image
+ * Fix grammar in Container Tools Guide
+ * Don't build btrfs if it is not installed
+ * new: Return image-pulling errors from resolveImage
+ * pull: Return image-pulling errors from pullImage
+ * Add more volume mount tests
+ * chroot: create missing parent directories for volume mounts
+ * Push: Allow an empty destination
+ * Add Podman relationship to readme, create container tools guide
+ * Fix arg usage in buildah-tag
+ * Add flags/arguments order verification to other commands
+ * Handle ErrDuplicateName errors from store.CreateContainer()
+ * Evaluate symbolic links on Add/Copy Commands
+ * Vendor in latest containers/image and containers/storage
+ * Retain bounding set when running containers as non root
+ * run container-diff tests in Travis
+ * buildah-images.md: Fix option contents
+ * push: show image digest after push succeed
+ * Vendor in latest containers/storage,image,libpod and runc
+ * Change references to cri-o to point at new repository
+ * Exclude --layers from the common bug cli flags
+ * demos: Increase the executable permissions
+ * run: clear default seccomp filter if not enabled
+ * Bump maximum cyclomatic complexity to 45
+ * stdin: on HUP, read everything
+ * nitpick: use tabs in tests/helpers.bash
+ * Add flags/arguments order verification to one arg commands
+ * nitpick: decrease cognitive complexity in buildah-bud
+ * rename: Avoid renaming the same name as other containers
+ * chroot isolation: chroot() before setting up seccomp
+ * Small nitpick at the "if" condition in tag.go
+ * cmd/images: Modify json option
+ * cmd/images: Disallow the input of image when using the -a option
+ * Fix examples to include context directory
+ * Update containers/image to fix commit layer issue
+ * cmd/containers: End loop early when using the json option
+ * Make buildah-from error message clear when flags are after arg
+ * Touch up README.md for conformance tests
+ * Update container/storage for lock fix
+ * cmd/rm: restore the correct containerID display
+ * Remove debug lines
+ * Remove docker build image after each test
+ * Add README for conformance test
+ * Update the MakeOptions to accept all command options for buildah
+ * Update regrex to fit the docker output in test "run with JSON"
+ * cmd/buildah: Remove redundant variable declarations
+ * Warn about using Commands in Dockerfile that are not supported by OCI.
+ * Add buildah bud conformance test
+ * Fix rename to also change container name in builder
+ * Makefile: use $(GO) env-var everywhere
+ * Cleanup code to more closely match Docker Build images
+ * Document BUILDAH_* environment variables in buildah bud --help output
+ * Return error immediately if error occurs in Prepare step
+ * Fix --layers ADD from url issue
+ * Add "Sign your PRs" TOC item to contributing.md.
+ * Display the correct ID after deleting image
+ * rmi: Modify the handling of errors
+ * Let util.ResolveName() return parsing errors
+ * Explain Open Container Initiative (OCI) acronym, add link
+ * Update vendor for urfave/cli back to master
+ * Handle COPY --chown in Dockerfile
+ * Switch to Recommends container-selinux
+ * Update vendor for containernetworking, imagebuildah and podman
+ * Document STORAGE_DRIVER and STORAGE_OPTS environment variable
+ * Change references to projectatomic/libpod to containers/libpod
+ * Add container PATH retrieval example
+ * Expand variables names for --env
+ * imagebuildah: provide a way to provide stdin for RUN
+ * Remove an unused srcRef.NewImageSource in pullImage
+ * chroot: correct a comment
+ * chroot: bind mount an empty directory for masking
+ * Don't bother with --no-pivot for rootless isolation
+ * CentOS need EPEL repo
+ * Export a Pull() function
+ * Remove stream options, since docker build does not have it
+ * release v1.3: mention openSUSE
+ * Add Release Announcements directory
+ * Bump to v1.4-dev
+
+- Changelog for v1.3 (2018-08-04)
+ * Revert pull error handling from 881
+ * bud should not search context directory for Dockerfile
+ * Set BUILDAH_ISOLATION=rootless when running unprivileged
+ * .papr.sh: Also test with BUILDAH_ISOLATION=rootless
+ * Skip certain tests when we're using "rootless" isolation
+ * .travis.yml: run integration tests with BUILDAH_ISOLATION=chroot
+ * Add and implement IsolationOCIRootless
+ * Add a value for IsolationOCIRootless
+ * Fix rmi to remove intermediate images associated with an image
+ * Return policy error on pull
+ * Update containers/image to 216acb1bcd2c1abef736ee322e17147ee2b7d76c
+ * Switch to github.com/containers/image/pkg/sysregistriesv2
+ * unshare: make adjusting the OOM score optional
+ * Add flags validation
+ * chroot: handle raising process limits
+ * chroot: make the resource limits name map module-global
+ * Remove rpm.bats, we need to run this manually
+ * Set the default ulimits to match Docker
+ * buildah: no args is out of bounds
+ * unshare: error message missed the pid
+ * preprocess ".in" suffixed Dockerfiles
+ * Fix the the in buildah-config man page
+ * Only test rpmbuild on latest fedora
+ * Add support for multiple Short options
+ * Update to latest urvave/cli
+ * Add additional SELinux tests
+ * Vendor in latest github.com/containers/{image;storage}
+ * Stop testing with golang 1.8
+ * Fix volume cache issue with buildah bud --layers
+ * Create buildah pull command
+ * Increase the deadline for gometalinter during 'make validate'
+ * .papr.sh: Also test with BUILDAH_ISOLATION=chroot
+ * .travis.yml: run integration tests with BUILDAH_ISOLATION=chroot
+ * Add a Dockerfile
+ * Set BUILDAH_ISOLATION=chroot when running unprivileged
+ * Add and implement IsolationChroot
+ * Update github.com/opencontainers/runc
+ * maybeReexecUsingUserNamespace: add a default for root
+ * Allow ping command without NET_RAW Capabilities
+ * rmi.storageImageID: fix Wrapf format warning
+ * Allow Dockerfile content to come from stdin
+ * Vendor latest container/storage to fix overlay mountopt
+ * userns: assign additional IDs sequentially
+ * Remove default dev/pts
+ * Add OnBuild test to baseline test
+ * tests/run.bats(volumes): use :z when SELinux is enabled
+ * Avoid a stall in runCollectOutput()
+ * Use manifest from container/image
+ * Vendor in latest containers/image and containers/storage
+ * add rename command
+ * Completion command
+ * Update CHANGELOG.md
+ * Update vendor for runc to fix 32 bit builds
+ * bash completion: remove shebang
+ * Update vendor for runc to fix 32 bit builds
+
+- Changelog for v1.2 (2018-07-14)
+ * Vendor in lates containers/image
+ * build-using-dockerfile: let -t include transports again
+ * Block use of /proc/acpi and /proc/keys from inside containers
+ * Fix handling of --registries-conf
+ * Fix becoming a maintainer link
+ * add optional CI test fo darwin
+ * Don't pass a nil error to errors.Wrapf()
+ * image filter test: use kubernetes/pause as a "since"
+ * Add --cidfile option to from
+ * vendor: update containers/storage
+ * Contributors need to find the CONTRIBUTOR.md file easier
+ * Add a --loglevel option to build-with-dockerfile
+ * Create Development plan
+ * cmd: Code improvement
+ * allow buildah cross compile for a darwin target
+ * Add unused function param lint check
+ * docs: Follow man-pages(7) suggestions for SYNOPSIS
+ * Start using github.com/seccomp/containers-golang
+ * umount: add all option to umount all mounted containers
+ * runConfigureNetwork(): remove an unused parameter
+ * Update github.com/opencontainers/selinux
+ * Fix buildah bud --layers
+ * Force ownership of /etc/hosts and /etc/resolv.conf to 0:0
+ * main: if unprivileged, reexec in a user namespace
+ * Vendor in latest imagebuilder
+ * Reduce the complexity of the buildah.Run function
+ * mount: output it before replacing lastError
+ * Vendor in latest selinux-go code
+ * Implement basic recognition of the "--isolation" option
+ * Run(): try to resolve non-absolute paths using $PATH
+ * Run(): don't include any default environment variables
+ * build without seccomp
+ * vendor in latest runtime-tools
+ * bind/mount_unsupported.go: remove import errors
+ * Update github.com/opencontainers/runc
+ * Add Capabilities lists to BuilderInfo
+ * Tweaks for commit tests
+ * commit: recognize committing to second storage locations
+ * Fix ARGS parsing for run commands
+ * Add info on registries.conf to from manpage
+ * Switch from using docker to podman for testing in .papr
+ * buildah: set the HTTP User-Agent
+ * ONBUILD tutorial
+ * Add information about the configuration files to the install docs
+ * Makefile: add uninstall
+ * Add tilde info for push to troubleshooting
+ * mount: support multiple inputs
+ * Use the right formatting when adding entries to /etc/hosts
+ * Vendor in latest go-selinux bindings
+ * Allow --userns-uid-map/--userns-gid-map to be global options
+ * bind: factor out UnmountMountpoints
+ * Run(): simplify runCopyStdio()
+ * Run(): handle POLLNVAL results
+ * Run(): tweak terminal mode handling
+ * Run(): rename 'copyStdio' to 'copyPipes'
+ * Run(): don't set a Pdeathsig for the runtime
+ * Run(): add options for adding and removing capabilities
+ * Run(): don't use a callback when a slice will do
+ * setupSeccomp(): refactor
+ * Change RunOptions.Stdin/Stdout/Stderr to just be Reader/Writers
+ * Escape use of '_' in .md docs
+ * Break out getProcIDMappings()
+ * Break out SetupIntermediateMountNamespace()
+ * Add Multi From Demo
+ * Use the c/image conversion code instead of converting configs manually
+ * Don't throw away the manifest MIME type and guess again
+ * Consolidate loading manifest and config in initConfig
+ * Pass a types.Image to Builder.initConfig
+ * Require an image ID in importBuilderDataFromImage
+ * Use c/image/manifest.GuessMIMEType instead of a custom heuristic
+ * Do not ignore any parsing errors in initConfig
+ * Explicitly handle "from scratch" images in Builder.initConfig
+ * Fix parsing of OCI images
+ * Simplify dead but dangerous-looking error handling
+ * Don't ignore v2s1 history if docker_version is not set
+ * Add --rm and --force-rm to buildah bud
+ * Add --all,-a flag to buildah images
+ * Separate stdio buffering from writing
+ * Remove tty check from images --format
+ * Add environment variable BUILDAH_RUNTIME
+ * Add --layers and --no-cache to buildah bud
+ * Touch up images man
+ * version.md: fix DESCRIPTION
+ * tests: add containers test
+ * tests: add images test
+ * images: fix usage
+ * fix make clean error
+ * Change 'registries' to 'container registries' in man
+ * add commit test
+ * Add(): learn to record hashes of what we add
+ * Minor update to buildah config documentation for entrypoint
+ * Bump to v1.2-dev
+ * Add registries.conf link to a few man pages
+
+- Changelog for v1.1 (2018-06-08)
+ * Drop capabilities if running container processes as non root
+ * Print Warning message if cmd will not be used based on entrypoint
+ * Update 01-intro.md
+ * Shouldn't add insecure registries to list of search registries
+ * Report errors on bad transports specification when pushing images
+ * Move parsing code out of common for namespaces and into pkg/parse.go
+ * Add disable-content-trust noop flag to bud
+ * Change freenode chan to buildah
+ * runCopyStdio(): don't close stdin unless we saw POLLHUP
+ * Add registry errors for pull
+ * runCollectOutput(): just read until the pipes are closed on us
+ * Run(): provide redirection for stdio
+ * rmi, rm: add test
+ * add mount test
+ * Add parameter judgment for commands that do not require parameters
+ * Add context dir to bud command in baseline test
+ * run.bats: check that we can run with symlinks in the bundle path
+ * Give better messages to users when image can not be found
+ * use absolute path for bundlePath
+ * Add environment variable to buildah --format
+ * rm: add validation to args and all option
+ * Accept json array input for config entrypoint
+ * Run(): process RunOptions.Mounts, and its flags
+ * Run(): only collect error output from stdio pipes if we created some
+ * Add OnBuild support for Dockerfiles
+ * Quick fix on demo readme
+ * run: fix validate flags
+ * buildah bud should require a context directory or URL
+ * Touchup tutorial for run changes
+ * Validate common bud and from flags
+ * images: Error if the specified imagename does not exist
+ * inspect: Increase err judgments to avoid panic
+ * add test to inspect
+ * buildah bud picks up ENV from base image
+ * Extend the amount of time travis_wait should wait
+ * Add a make target for Installing CNI plugins
+ * Add tests for namespace control flags
+ * copy.bats: check ownerships in the container
+ * Fix SELinux test errors when SELinux is enabled
+ * Add example CNI configurations
+ * Run: set supplemental group IDs
+ * Run: use a temporary mount namespace
+ * Use CNI to configure container networks
+ * add/secrets/commit: Use mappings when setting permissions on added content
+ * Add CLI options for specifying namespace and cgroup setup
+ * Always set mappings when using user namespaces
+ * Run(): break out creation of stdio pipe descriptors
+ * Read UID/GID mapping information from containers and images
+ * Additional bud CI tests
+ * Run integration tests under travis_wait in Travis
+ * build-using-dockerfile: add --annotation
+ * Implement --squash for build-using-dockerfile and commit
+ * Vendor in latest container/storage for devicemapper support
+ * add test to inspect
+ * Vendor github.com/onsi/ginkgo and github.com/onsi/gomega
+ * Test with Go 1.10, too
+ * Add console syntax highlighting to troubleshooting page
+ * bud.bats: print "$output" before checking its contents
+ * Manage "Run" containers more closely
+ * Break Builder.Run()'s "run runc" bits out
+ * util.ResolveName(): handle completion for tagged/digested image names
+ * Handle /etc/hosts and /etc/resolv.conf properly in container
+ * Documentation fixes
+ * Make it easier to parse our temporary directory as an image name
+ * Makefile: list new pkg/ subdirectoris as dependencies for buildah
+ * containerImageSource: return more-correct errors
+ * API cleanup: PullPolicy and TerminalPolicy should be types
+ * Make "run --terminal" and "run -t" aliases for "run --tty"
+ * Vendor github.com/containernetworking/cni v0.6.0
+ * Update github.com/containers/storage
+ * Update github.com/projectatomic/libpod
+ * Add support for buildah bud --label
+ * buildah push/from can push and pull images with no reference
+ * Vendor in latest containers/image
+ * Update gometalinter to fix install.tools error
+ * Update troubleshooting with new run workaround
+ * Added a bud demo and tidied up
+ * Attempt to download file from url, if fails assume Dockerfile
+ * Add buildah bud CI tests for ENV variables
+ * Re-enable rpm .spec version check and new commit test
+ * Update buildah scratch demo to support el7
+ * Added Docker compatibility demo
+ * Update to F28 and new run format in baseline test
+ * Touchup man page short options across man pages
+ * Added demo dir and a demo. chged distrorlease
+ * builder-inspect: fix format option
+ * Add cpu-shares short flag (-c) and cpu-shares CI tests
+ * Minor fixes to formatting in rpm spec changelog
+ * Fix rpm .spec changelog formatting
+ * CI tests and minor fix for cache related noop flags
+ * buildah-from: add effective value to mount propagation
+
+- Changelog for v1.0 (2018-05-06)
+ * Declare Buildah 1.0
+ * Add cache-from and no-cache noops, and fix doco
+ * Update option and documentation for --force-rm
+ * Adding noop for --force-rm to match --rm
+ * Add buildah bud ENTRYPOINT,CMD,RUN tests
+ * Adding buildah bud RUN test scenarios
+ * Extend tests for empty buildah run command
+ * Fix formatting error in run.go
+ * Update buildah run to make command required
+ * Expanding buildah run cmd/entrypoint tests
+ * Update test cases for buildah run behaviour
+ * Remove buildah run cmd and entrypoint execution
+ * Add Files section with registries.conf to pertinent man pages
+ * tests/config: perfect test
+ * tests/from: add name test
+ * Do not print directly to stdout in Commit()
+ * Touch up auth test commands
+ * Force "localhost" as a default registry
+ * Drop util.GetLocalTime()
+ * Vendor in latest containers/image
+ * Validate host and container paths passed to --volume
+ * test/from: add add-host test
+ * Add --compress, --rm, --squash flags as a noop for bud
+ * Add FIPS mode secret to buildah run and bud
+ * Add config --comment/--domainname/--history-comment/--hostname
+ * 'buildah config': stop replacing Created-By whenever it's not specified
+ * Modify man pages so they compile correctly in mandb
+ * Add description on how to do --isolation to buildah-bud man page
+ * Add support for --iidfile to bud and commit
+ * Refactor buildah bud for vendoring
+ * Fail if date or git not installed
+ * Revert update of entrypoint behaviour to match docker
+ * Vendor in latest imagebuilder code to fix multiple stage builds
+ * Add /bin/sh -c to entrypoint in config
+ * image_test: Improve the test
+ * Fix README example of buildah config
+ * buildah-image: add validation to 'format'
+ * Simple changes to allow buildah to pass make validate
+ * Clarify the use of buildah config options
+ * containers_test: Perfect testing
+ * buildah images and podman images are listing different sizes
+ * buildah-containers: add tests and example to the man page
+ * buildah-containers: add validation to 'format'
+ * Clarify the use of buildah config options
+ * Minor fix for lighttpd example in README
+ * Add tls-verification to troubleshooting
+ * Modify buildah rmi to account for changes in containers/storage
+ * Vendor in latest containers/image and containers/storage
+ * addcopy: add src validation
+ * Remove tarball as an option from buildah push --help
+ * Fix secrets patch
+ * Update entrypoint behaviour to match docker
+ * Display imageId after commit
+ * config: add support for StopSignal
+ * Fix docker login issue in travis.yml
+ * Allow referencing stages as index and names
+ * Add multi-stage builds tests
+ * Add multi-stage builds support
+ * Add accessor functions for comment and stop signal
+ * Vendor in latest imagebuilder, to get mixed case AS support
+ * Allow umount to have multi-containers
+ * Update buildah push doc
+ * buildah bud walks symlinks
+ * Imagename is required for commit atm, update manpage
+
+- Changelog for v0.16.0 (2018-04-08)
+ * Bump to v0.16.0
+ * Remove requires for ostree-lib in rpm spec file
+ * Add support for shell
+ * buildah.spec should require ostree-libs
+ * Vendor in latest containers/image
+ * bash: prefer options
+ * Change image time to locale, add troubleshooting.md, add logo to other mds
+ * buildah-run.md: fix error SYNOPSIS
+ * docs: fix error example
+ * Allow --cmd parameter to have commands as values
+ * Touchup README to re-enable logo
+ * Clean up README.md
+ * Make default-mounts-file a hidden option
+ * Document the mounts.conf file
+ * Fix man pages to format correctly
+ * Add various transport support to buildah from
+ * Add unit tests to run.go
+ * If the user overrides the storage driver, the options should be dropped
+ * Show Config/Manifest as JSON string in inspect when format is not set
+ * Switch which for that in README.md
+ * Remove COPR
+ * Fix wrong order of parameters
+ * Vendor in latest containers/image
+ * Remove shallowCopy(), which shouldn't be saving us time any more
+ * shallowCopy: avoid a second read of the container's layer
diff --git a/vendor/github.com/containers/buildah/chroot/run_common.go b/vendor/github.com/containers/buildah/chroot/run_common.go
new file mode 100644
index 00000000..fbd0689f
--- /dev/null
+++ b/vendor/github.com/containers/buildah/chroot/run_common.go
@@ -0,0 +1,854 @@
+//go:build linux || freebsd
+
+package chroot
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path/filepath"
+ "runtime"
+ "slices"
+ "strconv"
+ "strings"
+ "sync"
+ "syscall"
+
+ "github.com/containers/buildah/bind"
+ "github.com/containers/buildah/internal/pty"
+ "github.com/containers/buildah/util"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/sirupsen/logrus"
+ "go.podman.io/storage/pkg/ioutils"
+ "go.podman.io/storage/pkg/reexec"
+ "go.podman.io/storage/pkg/unshare"
+ "golang.org/x/sys/unix"
+ "golang.org/x/term"
+)
+
+const (
+ // runUsingChrootCommand is a command we use as a key for reexec
+ runUsingChrootCommand = "buildah-chroot-runtime"
+ // runUsingChrootExec is a command we use as a key for reexec
+ runUsingChrootExecCommand = "buildah-chroot-exec"
+ // containersConfEnv is an environment variable that we need to pass down except for the command itself
+ containersConfEnv = "CONTAINERS_CONF"
+)
+
+func init() {
+ reexec.Register(runUsingChrootCommand, runUsingChrootMain)
+ reexec.Register(runUsingChrootExecCommand, runUsingChrootExecMain)
+ for limitName, limitNumber := range rlimitsMap {
+ rlimitsReverseMap[limitNumber] = limitName
+ }
+}
+
+type runUsingChrootExecSubprocOptions struct {
+ Spec *specs.Spec
+ BundlePath string
+ NoPivot bool
+}
+
+// RunUsingChroot runs a chrooted process, using some of the settings from the
+// passed-in spec, and using the specified bundlePath to hold temporary files,
+// directories, and mountpoints.
+func RunUsingChroot(spec *specs.Spec, bundlePath, homeDir string, stdin io.Reader, stdout, stderr io.Writer, noPivot bool) (err error) {
+ var confwg sync.WaitGroup
+ var homeFound bool
+ for _, env := range spec.Process.Env {
+ if strings.HasPrefix(env, "HOME=") {
+ homeFound = true
+ break
+ }
+ }
+ if !homeFound {
+ spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("HOME=%s", homeDir))
+ }
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ // Write the runtime configuration, mainly for debugging.
+ specbytes, err := json.Marshal(spec)
+ if err != nil {
+ return err
+ }
+ if err = ioutils.AtomicWriteFile(filepath.Join(bundlePath, "config.json"), specbytes, 0o600); err != nil {
+ return fmt.Errorf("storing runtime configuration: %w", err)
+ }
+ logrus.Debugf("config = %v", string(specbytes))
+
+ // Default to using stdin/stdout/stderr if we weren't passed objects to use.
+ if stdin == nil {
+ stdin = os.Stdin
+ }
+ if stdout == nil {
+ stdout = os.Stdout
+ }
+ if stderr == nil {
+ stderr = os.Stderr
+ }
+
+ // Create a pipe for passing configuration down to the next process.
+ preader, pwriter, err := os.Pipe()
+ if err != nil {
+ return fmt.Errorf("creating configuration pipe: %w", err)
+ }
+ config, conferr := json.Marshal(runUsingChrootSubprocOptions{
+ Spec: spec,
+ BundlePath: bundlePath,
+ NoPivot: noPivot,
+ })
+ if conferr != nil {
+ return fmt.Errorf("encoding configuration for %q: %w", runUsingChrootCommand, conferr)
+ }
+
+ // Set our terminal's mode to raw, to pass handling of special
+ // terminal input to the terminal in the container.
+ if spec.Process.Terminal && term.IsTerminal(unix.Stdin) {
+ state, err := term.MakeRaw(unix.Stdin)
+ if err != nil {
+ logrus.Warnf("error setting terminal state: %v", err)
+ } else {
+ defer func() {
+ if err = term.Restore(unix.Stdin, state); err != nil {
+ logrus.Errorf("unable to restore terminal state: %v", err)
+ }
+ }()
+ }
+ }
+
+ // Raise any resource limits that are higher than they are now, before
+ // we drop any more privileges.
+ if err = setRlimits(spec, false, true); err != nil {
+ return err
+ }
+
+ // Start the grandparent subprocess.
+ cmd := unshare.Command(runUsingChrootCommand)
+ setPdeathsig(cmd.Cmd)
+ cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr
+ cmd.Dir = "/"
+ cmd.Env = []string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())}
+ if _, ok := os.LookupEnv(containersConfEnv); ok {
+ cmd.Env = append(cmd.Env, containersConfEnv+"="+os.Getenv(containersConfEnv))
+ }
+
+ interrupted := make(chan os.Signal, 100)
+ cmd.Hook = func(int) error {
+ signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
+ go func() {
+ for receivedSignal := range interrupted {
+ if err := cmd.Process.Signal(receivedSignal); err != nil {
+ logrus.Infof("%v while attempting to forward %v to child process", err, receivedSignal)
+ }
+ }
+ }()
+ return nil
+ }
+
+ logrus.Debugf("Running %#v in %#v", cmd.Cmd, cmd)
+ confwg.Add(1)
+ go func() {
+ _, conferr = io.Copy(pwriter, bytes.NewReader(config))
+ pwriter.Close()
+ confwg.Done()
+ }()
+ cmd.ExtraFiles = append([]*os.File{preader}, cmd.ExtraFiles...)
+ err = cmd.Run()
+ confwg.Wait()
+ signal.Stop(interrupted)
+ close(interrupted)
+ if err == nil {
+ return conferr
+ }
+ return err
+}
+
+// main() for grandparent subprocess. Its main job is to shuttle stdio back
+// and forth, managing a pseudo-terminal if we want one, for our child, the
+// parent subprocess.
+func runUsingChrootMain() {
+ var options runUsingChrootSubprocOptions
+
+ runtime.LockOSThread()
+
+ // Set logging.
+ if level := os.Getenv("LOGLEVEL"); level != "" {
+ if ll, err := strconv.Atoi(level); err == nil {
+ logrus.SetLevel(logrus.Level(ll))
+ }
+ os.Unsetenv("LOGLEVEL")
+ }
+
+ // Unpack our configuration.
+ confPipe := os.NewFile(3, "confpipe")
+ if confPipe == nil {
+ fmt.Fprintf(os.Stderr, "error reading options pipe\n")
+ os.Exit(1)
+ }
+ defer confPipe.Close()
+ if err := json.NewDecoder(confPipe).Decode(&options); err != nil {
+ fmt.Fprintf(os.Stderr, "error decoding options: %v\n", err)
+ os.Exit(1)
+ }
+
+ if options.Spec == nil || options.Spec.Process == nil {
+ fmt.Fprintf(os.Stderr, "invalid options spec in runUsingChrootMain\n")
+ os.Exit(1)
+ }
+ noPivot := options.NoPivot
+
+ // Prepare to shuttle stdio back and forth.
+ rootUID32, rootGID32, err := util.GetHostRootIDs(options.Spec)
+ if err != nil {
+ logrus.Errorf("error determining ownership for container stdio")
+ os.Exit(1)
+ }
+ rootUID := int(rootUID32)
+ rootGID := int(rootGID32)
+ relays := make(map[int]int)
+ closeOnceRunning := []*os.File{}
+ var ctty *os.File
+ var stdin io.Reader
+ var stdinCopy io.WriteCloser
+ var stdout io.Writer
+ var stderr io.Writer
+ fdDesc := make(map[int]string)
+ if options.Spec.Process.Terminal {
+ ptyMasterFd, ptyFd, err := pty.GetPtyDescriptors()
+ if err != nil {
+ logrus.Errorf("error opening PTY descriptors: %v", err)
+ os.Exit(1)
+ }
+ // Make notes about what's going where.
+ relays[ptyMasterFd] = unix.Stdout
+ relays[unix.Stdin] = ptyMasterFd
+ fdDesc[ptyMasterFd] = "container terminal"
+ fdDesc[unix.Stdin] = "stdin"
+ fdDesc[unix.Stdout] = "stdout"
+ winsize := &unix.Winsize{}
+ // Set the pseudoterminal's size to the configured size, or our own.
+ if options.Spec.Process.ConsoleSize != nil {
+ // Use configured sizes.
+ winsize.Row = uint16(options.Spec.Process.ConsoleSize.Height)
+ winsize.Col = uint16(options.Spec.Process.ConsoleSize.Width)
+ } else {
+ if term.IsTerminal(unix.Stdin) {
+ // Use the size of our terminal.
+ winsize, err = unix.IoctlGetWinsize(unix.Stdin, unix.TIOCGWINSZ)
+ if err != nil {
+ logrus.Debugf("error reading current terminal's size")
+ winsize.Row = 0
+ winsize.Col = 0
+ }
+ }
+ }
+ if winsize.Row != 0 && winsize.Col != 0 {
+ if err = unix.IoctlSetWinsize(ptyFd, unix.TIOCSWINSZ, winsize); err != nil {
+ logrus.Warnf("error setting terminal size for pty")
+ }
+ // FIXME - if we're connected to a terminal, we should
+ // be passing the updated terminal size down when we
+ // receive a SIGWINCH.
+ }
+ // Open an *os.File object that we can pass to our child.
+ ctty = os.NewFile(uintptr(ptyFd), "/dev/tty")
+ // Set ownership for the PTY.
+ if err = ctty.Chown(rootUID, rootGID); err != nil {
+ var cttyInfo unix.Stat_t
+ err2 := unix.Fstat(ptyFd, &cttyInfo)
+ from := ""
+ op := "setting"
+ if err2 == nil {
+ op = "changing"
+ from = fmt.Sprintf("from %d/%d ", cttyInfo.Uid, cttyInfo.Gid)
+ }
+ logrus.Warnf("error %s ownership of container PTY %sto %d/%d: %v", op, from, rootUID, rootGID, err)
+ }
+ // Set permissions on the PTY.
+ if err = ctty.Chmod(0o620); err != nil {
+ logrus.Errorf("error setting permissions of container PTY: %v", err)
+ os.Exit(1)
+ }
+ // Make a note that our child (the parent subprocess) should
+ // have the PTY connected to its stdio, and that we should
+ // close it once it's running.
+ stdin = ctty
+ stdout = ctty
+ stderr = ctty
+ closeOnceRunning = append(closeOnceRunning, ctty)
+ } else {
+ // Create pipes for stdio.
+ stdinRead, stdinWrite, err := os.Pipe()
+ if err != nil {
+ logrus.Errorf("error opening pipe for stdin: %v", err)
+ }
+ stdoutRead, stdoutWrite, err := os.Pipe()
+ if err != nil {
+ logrus.Errorf("error opening pipe for stdout: %v", err)
+ }
+ stderrRead, stderrWrite, err := os.Pipe()
+ if err != nil {
+ logrus.Errorf("error opening pipe for stderr: %v", err)
+ }
+ // Make notes about what's going where.
+ relays[unix.Stdin] = int(stdinWrite.Fd())
+ relays[int(stdoutRead.Fd())] = unix.Stdout
+ relays[int(stderrRead.Fd())] = unix.Stderr
+ fdDesc[int(stdinWrite.Fd())] = "container stdin pipe"
+ fdDesc[int(stdoutRead.Fd())] = "container stdout pipe"
+ fdDesc[int(stderrRead.Fd())] = "container stderr pipe"
+ fdDesc[unix.Stdin] = "stdin"
+ fdDesc[unix.Stdout] = "stdout"
+ fdDesc[unix.Stderr] = "stderr"
+ // Set ownership for the pipes.
+ if err = stdinRead.Chown(rootUID, rootGID); err != nil {
+ logrus.Errorf("error setting ownership of container stdin pipe: %v", err)
+ os.Exit(1)
+ }
+ if err = stdoutWrite.Chown(rootUID, rootGID); err != nil {
+ logrus.Errorf("error setting ownership of container stdout pipe: %v", err)
+ os.Exit(1)
+ }
+ if err = stderrWrite.Chown(rootUID, rootGID); err != nil {
+ logrus.Errorf("error setting ownership of container stderr pipe: %v", err)
+ os.Exit(1)
+ }
+ // Make a note that our child (the parent subprocess) should
+ // have the pipes connected to its stdio, and that we should
+ // close its ends of them once it's running.
+ stdin = stdinRead
+ stdout = stdoutWrite
+ stderr = stderrWrite
+ closeOnceRunning = append(closeOnceRunning, stdinRead, stdoutWrite, stderrWrite)
+ stdinCopy = stdinWrite
+ defer stdoutRead.Close()
+ defer stderrRead.Close()
+ }
+ for readFd, writeFd := range relays {
+ if err := unix.SetNonblock(readFd, true); err != nil {
+ logrus.Errorf("error setting descriptor %d (%s) non-blocking: %v", readFd, fdDesc[readFd], err)
+ return
+ }
+ if err := unix.SetNonblock(writeFd, false); err != nil {
+ logrus.Errorf("error setting descriptor %d (%s) blocking: %v", relays[writeFd], fdDesc[writeFd], err)
+ return
+ }
+ }
+ if err := unix.SetNonblock(relays[unix.Stdin], true); err != nil {
+ logrus.Errorf("error setting %d to nonblocking: %v", relays[unix.Stdin], err)
+ }
+ go func() {
+ buffers := make(map[int]*bytes.Buffer)
+ for _, writeFd := range relays {
+ buffers[writeFd] = new(bytes.Buffer)
+ }
+ pollTimeout := -1
+ stdinClose := false
+ for len(relays) > 0 {
+ fds := make([]unix.PollFd, 0, len(relays))
+ for fd := range relays {
+ fds = append(fds, unix.PollFd{Fd: int32(fd), Events: unix.POLLIN | unix.POLLHUP})
+ }
+ _, err := unix.Poll(fds, pollTimeout)
+ if !util.LogIfNotRetryable(err, fmt.Sprintf("poll: %v", err)) {
+ return
+ }
+ removeFds := make(map[int]struct{})
+ for _, rfd := range fds {
+ if rfd.Revents&unix.POLLHUP == unix.POLLHUP {
+ removeFds[int(rfd.Fd)] = struct{}{}
+ }
+ if rfd.Revents&unix.POLLNVAL == unix.POLLNVAL {
+ logrus.Debugf("error polling descriptor %s: closed?", fdDesc[int(rfd.Fd)])
+ removeFds[int(rfd.Fd)] = struct{}{}
+ }
+ if rfd.Revents&unix.POLLIN == 0 {
+ if stdinClose && stdinCopy == nil {
+ continue
+ }
+ continue
+ }
+ b := make([]byte, 8192)
+ nread, err := unix.Read(int(rfd.Fd), b)
+ util.LogIfNotRetryable(err, fmt.Sprintf("read %s: %v", fdDesc[int(rfd.Fd)], err))
+ if nread > 0 {
+ if wfd, ok := relays[int(rfd.Fd)]; ok {
+ nwritten, err := buffers[wfd].Write(b[:nread])
+ if err != nil {
+ logrus.Debugf("buffer: %v", err)
+ continue
+ }
+ if nwritten != nread {
+ logrus.Debugf("buffer: expected to buffer %d bytes, wrote %d", nread, nwritten)
+ continue
+ }
+ }
+ // If this is the last of the data we'll be able to read
+ // from this descriptor, read as much as there is to read.
+ for rfd.Revents&unix.POLLHUP == unix.POLLHUP {
+ nr, err := unix.Read(int(rfd.Fd), b)
+ util.LogIfUnexpectedWhileDraining(err, fmt.Sprintf("read %s: %v", fdDesc[int(rfd.Fd)], err))
+ if nr <= 0 {
+ break
+ }
+ if wfd, ok := relays[int(rfd.Fd)]; ok {
+ nwritten, err := buffers[wfd].Write(b[:nr])
+ if err != nil {
+ logrus.Debugf("buffer: %v", err)
+ break
+ }
+ if nwritten != nr {
+ logrus.Debugf("buffer: expected to buffer %d bytes, wrote %d", nr, nwritten)
+ break
+ }
+ }
+ }
+ }
+ if nread == 0 {
+ removeFds[int(rfd.Fd)] = struct{}{}
+ }
+ }
+ pollTimeout = -1
+ for wfd, buffer := range buffers {
+ if buffer.Len() > 0 {
+ nwritten, err := unix.Write(wfd, buffer.Bytes())
+ util.LogIfNotRetryable(err, fmt.Sprintf("write %s: %v", fdDesc[wfd], err))
+ if nwritten >= 0 {
+ _ = buffer.Next(nwritten)
+ }
+ }
+ if buffer.Len() > 0 {
+ pollTimeout = 100
+ }
+ if wfd == relays[unix.Stdin] && stdinClose && buffer.Len() == 0 {
+ stdinCopy.Close()
+ delete(relays, unix.Stdin)
+ }
+ }
+ for rfd := range removeFds {
+ if rfd == unix.Stdin {
+ buffer, found := buffers[relays[unix.Stdin]]
+ if found && buffer.Len() > 0 {
+ stdinClose = true
+ continue
+ }
+ }
+ if !options.Spec.Process.Terminal && rfd == unix.Stdin {
+ stdinCopy.Close()
+ }
+ delete(relays, rfd)
+ }
+ }
+ }()
+
+ // Set up mounts and namespaces, and run the parent subprocess.
+ status, err := runUsingChroot(options.Spec, options.BundlePath, ctty, stdin, stdout, stderr, noPivot, closeOnceRunning)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error running subprocess: %v\n", err)
+ os.Exit(1)
+ }
+
+ // Pass the process's exit status back to the caller by exiting with the same status.
+ if status.Exited() {
+ if status.ExitStatus() != 0 {
+ fmt.Fprintf(os.Stderr, "subprocess exited with status %d\n", status.ExitStatus())
+ }
+ os.Exit(status.ExitStatus())
+ } else if status.Signaled() {
+ fmt.Fprintf(os.Stderr, "subprocess exited on %s\n", status.Signal())
+ os.Exit(1)
+ }
+}
+
+// runUsingChroot, still in the grandparent process, sets up various bind
+// mounts and then runs the parent process in its own user namespace with the
+// necessary ID mappings.
+func runUsingChroot(spec *specs.Spec, bundlePath string, ctty *os.File, stdin io.Reader, stdout, stderr io.Writer, noPivot bool, closeOnceRunning []*os.File) (wstatus unix.WaitStatus, err error) {
+ var confwg sync.WaitGroup
+
+ // Create a new mount namespace for ourselves and bind mount everything to a new location.
+ undoIntermediates, err := bind.SetupIntermediateMountNamespace(spec, bundlePath)
+ if err != nil {
+ return 1, err
+ }
+ defer func() {
+ if undoErr := undoIntermediates(); undoErr != nil {
+ logrus.Debugf("error cleaning up intermediate mount NS: %v", err)
+ }
+ }()
+
+ // Bind mount in our filesystems.
+ undoChroots, err := setupChrootBindMounts(spec, bundlePath)
+ if err != nil {
+ return 1, err
+ }
+ defer func() {
+ if undoErr := undoChroots(); undoErr != nil {
+ logrus.Debugf("error cleaning up intermediate chroot bind mounts: %v", err)
+ }
+ }()
+
+ // Create a pipe for passing configuration down to the next process.
+ preader, pwriter, err := os.Pipe()
+ if err != nil {
+ return 1, fmt.Errorf("creating configuration pipe: %w", err)
+ }
+ config, conferr := json.Marshal(runUsingChrootExecSubprocOptions{
+ Spec: spec,
+ BundlePath: bundlePath,
+ NoPivot: noPivot,
+ })
+ if conferr != nil {
+ fmt.Fprintf(os.Stderr, "error re-encoding configuration for %q\n", runUsingChrootExecCommand)
+ os.Exit(1)
+ }
+
+ // Apologize for the namespace configuration that we're about to ignore.
+ logNamespaceDiagnostics(spec)
+
+ // We need to lock the thread so that PR_SET_PDEATHSIG won't trigger if the current thread exits.
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ // Start the parent subprocess.
+ cmd := unshare.Command(append([]string{runUsingChrootExecCommand}, spec.Process.Args...)...)
+ setPdeathsig(cmd.Cmd)
+ cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr
+ cmd.Dir = "/"
+ cmd.Env = []string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())}
+ if _, ok := os.LookupEnv(containersConfEnv); ok {
+ cmd.Env = append(cmd.Env, containersConfEnv+"="+os.Getenv(containersConfEnv))
+ }
+ if ctty != nil {
+ cmd.Setsid = true
+ cmd.Ctty = ctty
+ }
+ cmd.ExtraFiles = append([]*os.File{preader}, cmd.ExtraFiles...)
+ if err := setPlatformUnshareOptions(spec, cmd); err != nil {
+ return 1, fmt.Errorf("setting platform unshare options: %w", err)
+ }
+ interrupted := make(chan os.Signal, 100)
+ cmd.Hook = func(int) error {
+ for _, f := range closeOnceRunning {
+ f.Close()
+ }
+ signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
+ go func() {
+ for receivedSignal := range interrupted {
+ if err := cmd.Process.Signal(receivedSignal); err != nil {
+ logrus.Infof("%v while attempting to forward %v to child process", err, receivedSignal)
+ }
+ }
+ }()
+ return nil
+ }
+
+ logrus.Debugf("Running %#v in %#v", cmd.Cmd, cmd)
+ confwg.Add(1)
+ go func() {
+ _, conferr = io.Copy(pwriter, bytes.NewReader(config))
+ pwriter.Close()
+ confwg.Done()
+ }()
+ err = cmd.Run()
+ confwg.Wait()
+ signal.Stop(interrupted)
+ close(interrupted)
+ if err != nil {
+ if exitError, ok := err.(*exec.ExitError); ok {
+ if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok {
+ if waitStatus.Exited() {
+ if waitStatus.ExitStatus() != 0 {
+ fmt.Fprintf(os.Stderr, "subprocess exited with status %d\n", waitStatus.ExitStatus())
+ }
+ os.Exit(waitStatus.ExitStatus())
+ } else if waitStatus.Signaled() {
+ fmt.Fprintf(os.Stderr, "subprocess exited on %s\n", waitStatus.Signal())
+ os.Exit(1)
+ }
+ }
+ }
+ fmt.Fprintf(os.Stderr, "process exited with error: %v\n", err)
+ os.Exit(1)
+ }
+
+ return 0, nil
+}
+
+// main() for parent subprocess. Its main job is to try to make our
+// environment look like the one described by the runtime configuration blob,
+// and then launch the intended command as a child.
+func runUsingChrootExecMain() {
+ args := os.Args[1:]
+ var options runUsingChrootExecSubprocOptions
+ var err error
+
+ runtime.LockOSThread()
+
+ // Set logging.
+ if level := os.Getenv("LOGLEVEL"); level != "" {
+ if ll, err := strconv.Atoi(level); err == nil {
+ logrus.SetLevel(logrus.Level(ll))
+ }
+ os.Unsetenv("LOGLEVEL")
+ }
+
+ // Unpack our configuration.
+ confPipe := os.NewFile(3, "confpipe")
+ if confPipe == nil {
+ fmt.Fprintf(os.Stderr, "error reading options pipe\n")
+ os.Exit(1)
+ }
+ defer confPipe.Close()
+ if err := json.NewDecoder(confPipe).Decode(&options); err != nil {
+ fmt.Fprintf(os.Stderr, "error decoding options: %v\n", err)
+ os.Exit(1)
+ }
+
+ // Set the hostname. We're already in a distinct UTS namespace and are admins in the user
+ // namespace which created it, so we shouldn't get a permissions error, but seccomp policy
+ // might deny our attempt to call sethostname() anyway, so log a debug message for that.
+ if options.Spec == nil || options.Spec.Process == nil {
+ fmt.Fprintf(os.Stderr, "invalid options spec passed in\n")
+ os.Exit(1)
+ }
+
+ if options.Spec.Hostname != "" {
+ setContainerHostname(options.Spec.Hostname)
+ }
+
+ // Try to chroot into the root. Do this before we potentially
+ // block the syscall via the seccomp profile. Allow the
+ // platform to override this - on FreeBSD, we use a simple
+ // jail to set the hostname in the container, and on Linux
+ // we attempt to pivot_root.
+ if err := createPlatformContainer(options); err != nil {
+ logrus.Debugf("createPlatformContainer: %v", err)
+ var oldst, newst unix.Stat_t
+ if err := unix.Stat(options.Spec.Root.Path, &oldst); err != nil {
+ fmt.Fprintf(os.Stderr, "error stat()ing intended root directory %q: %v\n", options.Spec.Root.Path, err)
+ os.Exit(1)
+ }
+ if err := unix.Chdir(options.Spec.Root.Path); err != nil {
+ fmt.Fprintf(os.Stderr, "error chdir()ing to intended root directory %q: %v\n", options.Spec.Root.Path, err)
+ os.Exit(1)
+ }
+ if err := unix.Chroot(options.Spec.Root.Path); err != nil {
+ fmt.Fprintf(os.Stderr, "error chroot()ing into directory %q: %v\n", options.Spec.Root.Path, err)
+ os.Exit(1)
+ }
+ if err := unix.Stat("/", &newst); err != nil {
+ fmt.Fprintf(os.Stderr, "error stat()ing current root directory: %v\n", err)
+ os.Exit(1)
+ }
+ if oldst.Dev != newst.Dev || oldst.Ino != newst.Ino {
+ fmt.Fprintf(os.Stderr, "unknown error chroot()ing into directory %q: %v\n", options.Spec.Root.Path, err)
+ os.Exit(1)
+ }
+ logrus.Debugf("chrooted into %q", options.Spec.Root.Path)
+ }
+
+ // not doing because it's still shared: creating devices
+ // not doing because it's not applicable: setting annotations
+ // not doing because it's still shared: setting sysctl settings
+ // not doing because cgroupfs is read only: configuring control groups
+ // -> this means we can use the freezer to make sure there aren't any lingering processes
+ // -> this means we ignore cgroups-based controls
+ // not doing because we don't set any in the config: running hooks
+ // not doing because we don't set it in the config: setting rootfs read-only
+ // not doing because we don't set it in the config: setting rootfs propagation
+ logrus.Debugf("setting apparmor profile")
+ if err = setApparmorProfile(options.Spec); err != nil {
+ fmt.Fprintf(os.Stderr, "error setting apparmor profile for process: %v\n", err)
+ os.Exit(1)
+ }
+ if err = setSelinuxLabel(options.Spec); err != nil {
+ fmt.Fprintf(os.Stderr, "error setting SELinux label for process: %v\n", err)
+ os.Exit(1)
+ }
+
+ logrus.Debugf("setting resource limits")
+ if err = setRlimits(options.Spec, false, false); err != nil {
+ fmt.Fprintf(os.Stderr, "error setting process resource limits for process: %v\n", err)
+ os.Exit(1)
+ }
+
+ // Try to change to the directory.
+ cwd := options.Spec.Process.Cwd
+ if !filepath.IsAbs(cwd) {
+ cwd = "/" + cwd
+ }
+ cwd = filepath.Clean(cwd)
+ if err := unix.Chdir("/"); err != nil {
+ fmt.Fprintf(os.Stderr, "error chdir()ing into new root directory %q: %v\n", options.Spec.Root.Path, err)
+ os.Exit(1)
+ }
+ if err := unix.Chdir(cwd); err != nil {
+ fmt.Fprintf(os.Stderr, "error chdir()ing into directory %q under root %q: %v\n", cwd, options.Spec.Root.Path, err)
+ os.Exit(1)
+ }
+ logrus.Debugf("changed working directory to %q", cwd)
+
+ // Drop privileges.
+ user := options.Spec.Process.User
+ if len(user.AdditionalGids) > 0 {
+ gids := make([]int, len(user.AdditionalGids))
+ for i := range user.AdditionalGids {
+ gids[i] = int(user.AdditionalGids[i])
+ }
+ logrus.Debugf("setting supplemental groups")
+ if err = syscall.Setgroups(gids); err != nil {
+ fmt.Fprintf(os.Stderr, "error setting supplemental groups list: %v\n", err)
+ os.Exit(1)
+ }
+ } else {
+ setgroups, _ := os.ReadFile("/proc/self/setgroups")
+ if strings.Trim(string(setgroups), "\n") != "deny" {
+ logrus.Debugf("clearing supplemental groups")
+ if err = syscall.Setgroups([]int{}); err != nil {
+ fmt.Fprintf(os.Stderr, "error clearing supplemental groups list: %v\n", err)
+ os.Exit(1)
+ }
+ }
+ }
+
+ logrus.Debugf("setting gid")
+ if err = unix.Setresgid(int(user.GID), int(user.GID), int(user.GID)); err != nil {
+ fmt.Fprintf(os.Stderr, "error setting GID: %v\n", err)
+ os.Exit(1)
+ }
+
+ if err = setSeccomp(options.Spec); err != nil {
+ fmt.Fprintf(os.Stderr, "error setting seccomp filter for process: %v\n", err)
+ os.Exit(1)
+ }
+
+ logrus.Debugf("setting capabilities")
+ var keepCaps []string
+ if user.UID != 0 {
+ keepCaps = []string{"CAP_SETUID"}
+ }
+ if err := setCapabilities(options.Spec, keepCaps...); err != nil {
+ fmt.Fprintf(os.Stderr, "error setting capabilities for process: %v\n", err)
+ os.Exit(1)
+ }
+
+ logrus.Debugf("setting uid")
+ if err = unix.Setresuid(int(user.UID), int(user.UID), int(user.UID)); err != nil {
+ fmt.Fprintf(os.Stderr, "error setting UID: %v\n", err)
+ os.Exit(1)
+ }
+
+ // Set $PATH to the value for the container, so that when args[0] is not an absolute path,
+ // exec.Command() can find it using exec.LookPath().
+ for _, env := range slices.Backward(options.Spec.Process.Env) {
+ if val, ok := strings.CutPrefix(env, "PATH="); ok {
+ os.Setenv("PATH", val)
+ break
+ }
+ }
+
+ // Actually run the specified command.
+ cmd := exec.Command(args[0], args[1:]...)
+ setPdeathsig(cmd)
+ cmd.Env = options.Spec.Process.Env
+ cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
+ cmd.Dir = cwd
+ logrus.Debugf("Running %#v (PATH = %q)", cmd, os.Getenv("PATH"))
+ interrupted := make(chan os.Signal, 100)
+ if err = cmd.Start(); err != nil {
+ fmt.Fprintf(os.Stderr, "process failed to start with error: %v\n", err)
+ }
+ go func() {
+ for range interrupted {
+ if err := cmd.Process.Signal(syscall.SIGKILL); err != nil {
+ logrus.Infof("%v while attempting to send SIGKILL to child process", err)
+ }
+ }
+ }()
+ signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
+ err = cmd.Wait()
+ signal.Stop(interrupted)
+ close(interrupted)
+ if err != nil {
+ if exitError, ok := err.(*exec.ExitError); ok {
+ if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok {
+ if waitStatus.Exited() {
+ if waitStatus.ExitStatus() != 0 {
+ fmt.Fprintf(os.Stderr, "subprocess exited with status %d\n", waitStatus.ExitStatus())
+ }
+ os.Exit(waitStatus.ExitStatus())
+ } else if waitStatus.Signaled() {
+ fmt.Fprintf(os.Stderr, "subprocess exited on %s\n", waitStatus.Signal())
+ os.Exit(1)
+ }
+ }
+ }
+ fmt.Fprintf(os.Stderr, "process exited with error: %v\n", err)
+ os.Exit(1)
+ }
+}
+
+// parses the resource limits for ourselves and any processes that
+// we'll start into a format that's more in line with the kernel APIs
+func parseRlimits(spec *specs.Spec) (map[int]unix.Rlimit, error) {
+ if spec.Process == nil {
+ return nil, nil
+ }
+ parsed := make(map[int]unix.Rlimit)
+ for _, limit := range spec.Process.Rlimits {
+ resource, recognized := rlimitsMap[strings.ToUpper(limit.Type)]
+ if !recognized {
+ return nil, fmt.Errorf("parsing limit type %q", limit.Type)
+ }
+ parsed[resource] = makeRlimit(limit)
+ }
+ return parsed, nil
+}
+
+// setRlimits sets any resource limits that we want to apply to processes that
+// we'll start.
+func setRlimits(spec *specs.Spec, onlyLower, onlyRaise bool) error {
+ limits, err := parseRlimits(spec)
+ if err != nil {
+ return err
+ }
+ for resource, desired := range limits {
+ var current unix.Rlimit
+ if err := unix.Getrlimit(resource, ¤t); err != nil {
+ return fmt.Errorf("reading %q limit: %w", rlimitsReverseMap[resource], err)
+ }
+ if desired.Max > current.Max && onlyLower {
+ // this would raise a hard limit, and we're only here to lower them
+ continue
+ }
+ if desired.Max < current.Max && onlyRaise {
+ // this would lower a hard limit, and we're only here to raise them
+ continue
+ }
+ if err := unix.Setrlimit(resource, &desired); err != nil {
+ return fmt.Errorf("setting %q limit to soft=%d,hard=%d (was soft=%d,hard=%d): %w", rlimitsReverseMap[resource], desired.Cur, desired.Max, current.Cur, current.Max, err)
+ }
+ }
+ return nil
+}
+
+func isDevNull(dev os.FileInfo) bool {
+ if dev.Mode()&os.ModeCharDevice != 0 {
+ stat, _ := dev.Sys().(*syscall.Stat_t)
+ nullStat := syscall.Stat_t{}
+ if err := syscall.Stat(os.DevNull, &nullStat); err != nil {
+ logrus.Warnf("unable to stat /dev/null: %v", err)
+ return false
+ }
+ if stat.Rdev == nullStat.Rdev {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/containers/buildah/chroot/run_freebsd.go b/vendor/github.com/containers/buildah/chroot/run_freebsd.go
new file mode 100644
index 00000000..e8760544
--- /dev/null
+++ b/vendor/github.com/containers/buildah/chroot/run_freebsd.go
@@ -0,0 +1,272 @@
+//go:build freebsd
+
+package chroot
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "io/fs"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "syscall"
+
+ "github.com/containers/buildah/pkg/jail"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/sirupsen/logrus"
+ "go.podman.io/storage/pkg/fileutils"
+ "go.podman.io/storage/pkg/mount"
+ "go.podman.io/storage/pkg/unshare"
+ "golang.org/x/sys/unix"
+)
+
+var (
+ rlimitsMap = map[string]int{
+ "RLIMIT_AS": unix.RLIMIT_AS,
+ "RLIMIT_CORE": unix.RLIMIT_CORE,
+ "RLIMIT_CPU": unix.RLIMIT_CPU,
+ "RLIMIT_DATA": unix.RLIMIT_DATA,
+ "RLIMIT_FSIZE": unix.RLIMIT_FSIZE,
+ "RLIMIT_MEMLOCK": unix.RLIMIT_MEMLOCK,
+ "RLIMIT_NOFILE": unix.RLIMIT_NOFILE,
+ "RLIMIT_NPROC": unix.RLIMIT_NPROC,
+ "RLIMIT_RSS": unix.RLIMIT_RSS,
+ "RLIMIT_STACK": unix.RLIMIT_STACK,
+ }
+ rlimitsReverseMap = map[int]string{}
+)
+
+type runUsingChrootSubprocOptions struct {
+ Spec *specs.Spec
+ BundlePath string
+ NoPivot bool
+}
+
+func setPlatformUnshareOptions(spec *specs.Spec, cmd *unshare.Cmd) error {
+ return nil
+}
+
+func setContainerHostname(name string) {
+ // On FreeBSD, we have to set this later when we create the
+ // jail below in createPlatformContainer
+}
+
+func setSelinuxLabel(spec *specs.Spec) error {
+ // Ignore this on FreeBSD
+ return nil
+}
+
+func setApparmorProfile(spec *specs.Spec) error {
+ // FreeBSD doesn't have apparmor`
+ return nil
+}
+
+func setCapabilities(spec *specs.Spec, keepCaps ...string) error {
+ // FreeBSD capabilities are nothing like Linux
+ return nil
+}
+
+func makeRlimit(limit specs.POSIXRlimit) unix.Rlimit {
+ return unix.Rlimit{Cur: int64(limit.Soft), Max: int64(limit.Hard)}
+}
+
+func createPlatformContainer(options runUsingChrootExecSubprocOptions) error {
+ path := options.Spec.Root.Path
+ jconf := jail.NewConfig()
+ jconf.Set("name", filepath.Base(path)+"-chroot")
+ jconf.Set("host.hostname", options.Spec.Hostname)
+ jconf.Set("persist", false)
+ jconf.Set("path", path)
+ jconf.Set("ip4", jail.INHERIT)
+ jconf.Set("ip6", jail.INHERIT)
+ jconf.Set("allow.raw_sockets", true)
+ jconf.Set("enforce_statfs", 1)
+ _, err := jail.CreateAndAttach(jconf)
+ if err != nil {
+ return fmt.Errorf("creating jail: %w", err)
+ }
+ return nil
+}
+
+// logNamespaceDiagnostics knows which namespaces we want to create.
+// Output debug messages when that differs from what we're being asked to do.
+func logNamespaceDiagnostics(spec *specs.Spec) {
+ // Nothing here for FreeBSD
+}
+
+func makeReadOnly(mntpoint string, flags uintptr) error {
+ var fs unix.Statfs_t
+ // Make sure it's read-only.
+ if err := unix.Statfs(mntpoint, &fs); err != nil {
+ return fmt.Errorf("checking if directory %q was bound read-only: %w", mntpoint, err)
+ }
+ return nil
+}
+
+func saveDir(spec *specs.Spec, path string) string {
+ id := filepath.Base(spec.Root.Path)
+ return filepath.Join(filepath.Dir(path), ".save-"+id)
+}
+
+func copyFile(source, dest string) error {
+ in, err := os.Open(source)
+ if err != nil {
+ return err
+ }
+ defer in.Close()
+
+ out, err := os.Create(dest)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+
+ _, err = io.Copy(out, in)
+ if err != nil {
+ return err
+ }
+ return out.Close()
+}
+
+type rename struct {
+ from, to string
+}
+
+// setupChrootBindMounts actually bind mounts things under the rootfs, and returns a
+// callback that will clean up its work.
+func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func() error, err error) {
+ renames := []rename{}
+ unmounts := []string{}
+ removes := []string{}
+ undoBinds = func() error {
+ for _, r := range renames {
+ if err2 := os.Rename(r.to, r.from); err2 != nil {
+ logrus.Warnf("pkg/chroot: error renaming %q to %q: %v", r.to, r.from, err2)
+ if err == nil {
+ err = err2
+ }
+ }
+ }
+ for _, path := range unmounts {
+ if err2 := mount.Unmount(path); err2 != nil {
+ logrus.Warnf("pkg/chroot: error unmounting %q: %v", spec.Root.Path, err2)
+ if err == nil {
+ err = err2
+ }
+ }
+ }
+ for _, path := range removes {
+ if err2 := os.Remove(path); err2 != nil {
+ logrus.Warnf("pkg/chroot: error removing %q: %v", path, err2)
+ if err == nil {
+ err = err2
+ }
+ }
+ }
+ return err
+ }
+
+ // Now mount all of those things to be under the rootfs's location in this
+ // mount namespace.
+ for _, m := range spec.Mounts {
+ // If the target is there, we can just mount it.
+ var srcinfo os.FileInfo
+ switch m.Type {
+ case "nullfs":
+ srcinfo, err = os.Stat(m.Source)
+ if err != nil {
+ return undoBinds, fmt.Errorf("examining %q for mounting in mount namespace: %w", m.Source, err)
+ }
+ }
+ target := filepath.Join(spec.Root.Path, m.Destination)
+ if err := fileutils.Exists(target); err != nil {
+ // If the target can't be stat()ted, check the error.
+ if !errors.Is(err, fs.ErrNotExist) {
+ return undoBinds, fmt.Errorf("examining %q for mounting in mount namespace: %w", target, err)
+ }
+ // The target isn't there yet, so create it, and make a
+ // note to remove it later.
+ // XXX: This was copied from the linux version which supports bind mounting files.
+ // Leaving it here since I plan to add this to FreeBSD's nullfs.
+ if m.Type != "nullfs" || srcinfo.IsDir() {
+ if err = os.MkdirAll(target, 0o111); err != nil {
+ return undoBinds, fmt.Errorf("creating mountpoint %q in mount namespace: %w", target, err)
+ }
+ removes = append(removes, target)
+ } else {
+ if err = os.MkdirAll(filepath.Dir(target), 0o111); err != nil {
+ return undoBinds, fmt.Errorf("ensuring parent of mountpoint %q (%q) is present in mount namespace: %w", target, filepath.Dir(target), err)
+ }
+ // Don't do this until we can support file mounts in nullfs
+ /*var file *os.File
+ if file, err = os.OpenFile(target, os.O_WRONLY|os.O_CREATE, 0); err != nil {
+ return undoBinds, errors.Wrapf(err, "error creating mountpoint %q in mount namespace", target)
+ }
+ file.Close()
+ removes = append(removes, target)*/
+ }
+ }
+ logrus.Debugf("mount: %v", m)
+ switch m.Type {
+ case "nullfs":
+ // Do the bind mount.
+ if !srcinfo.IsDir() {
+ logrus.Debugf("emulating file mount %q on %q", m.Source, target)
+ err := fileutils.Exists(target)
+ if err == nil {
+ save := saveDir(spec, target)
+ if err := fileutils.Exists(save); err != nil {
+ if errors.Is(err, fs.ErrNotExist) {
+ err = os.MkdirAll(save, 0o111)
+ }
+ if err != nil {
+ return undoBinds, fmt.Errorf("creating file mount save directory %q: %w", save, err)
+ }
+ removes = append(removes, save)
+ }
+ savePath := filepath.Join(save, filepath.Base(target))
+ if err := fileutils.Exists(target); err == nil {
+ logrus.Debugf("moving %q to %q", target, savePath)
+ if err := os.Rename(target, savePath); err != nil {
+ return undoBinds, fmt.Errorf("moving %q to %q: %w", target, savePath, err)
+ }
+ renames = append(renames, rename{
+ from: target,
+ to: savePath,
+ })
+ }
+ } else {
+ removes = append(removes, target)
+ }
+ if err := copyFile(m.Source, target); err != nil {
+ return undoBinds, fmt.Errorf("copying %q to %q: %w", m.Source, target, err)
+ }
+ } else {
+ logrus.Debugf("bind mounting %q on %q", m.Destination, filepath.Join(spec.Root.Path, m.Destination))
+ if err := mount.Mount(m.Source, target, "nullfs", strings.Join(m.Options, ",")); err != nil {
+ return undoBinds, fmt.Errorf("bind mounting %q from host to %q in mount namespace (%q): %w", m.Source, m.Destination, target, err)
+ }
+ logrus.Debugf("bind mounted %q to %q", m.Source, target)
+ unmounts = append(unmounts, target)
+ }
+ case "devfs", "fdescfs", "tmpfs":
+ // Mount /dev, /dev/fd.
+ if err := mount.Mount(m.Source, target, m.Type, strings.Join(m.Options, ",")); err != nil {
+ return undoBinds, fmt.Errorf("mounting %q to %q in mount namespace (%q, %q): %w", m.Type, m.Destination, target, strings.Join(m.Options, ","), err)
+ }
+ logrus.Debugf("mounted a %q to %q", m.Type, target)
+ unmounts = append(unmounts, target)
+ }
+ }
+ return undoBinds, nil
+}
+
+// setPdeathsig sets a parent-death signal for the process
+func setPdeathsig(cmd *exec.Cmd) {
+ if cmd.SysProcAttr == nil {
+ cmd.SysProcAttr = &syscall.SysProcAttr{}
+ }
+ cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
+}
diff --git a/vendor/github.com/containers/buildah/chroot/run_linux.go b/vendor/github.com/containers/buildah/chroot/run_linux.go
new file mode 100644
index 00000000..59c439d0
--- /dev/null
+++ b/vendor/github.com/containers/buildah/chroot/run_linux.go
@@ -0,0 +1,840 @@
+//go:build linux
+
+package chroot
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "slices"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/containers/buildah/copier"
+ "github.com/moby/sys/capability"
+ "github.com/opencontainers/runc/libcontainer/apparmor"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/sirupsen/logrus"
+ "go.podman.io/storage/pkg/mount"
+ "go.podman.io/storage/pkg/unshare"
+ "golang.org/x/sys/unix"
+)
+
+var (
+ rlimitsMap = map[string]int{
+ "RLIMIT_AS": unix.RLIMIT_AS,
+ "RLIMIT_CORE": unix.RLIMIT_CORE,
+ "RLIMIT_CPU": unix.RLIMIT_CPU,
+ "RLIMIT_DATA": unix.RLIMIT_DATA,
+ "RLIMIT_FSIZE": unix.RLIMIT_FSIZE,
+ "RLIMIT_LOCKS": unix.RLIMIT_LOCKS,
+ "RLIMIT_MEMLOCK": unix.RLIMIT_MEMLOCK,
+ "RLIMIT_MSGQUEUE": unix.RLIMIT_MSGQUEUE,
+ "RLIMIT_NICE": unix.RLIMIT_NICE,
+ "RLIMIT_NOFILE": unix.RLIMIT_NOFILE,
+ "RLIMIT_NPROC": unix.RLIMIT_NPROC,
+ "RLIMIT_RSS": unix.RLIMIT_RSS,
+ "RLIMIT_RTPRIO": unix.RLIMIT_RTPRIO,
+ "RLIMIT_RTTIME": unix.RLIMIT_RTTIME,
+ "RLIMIT_SIGPENDING": unix.RLIMIT_SIGPENDING,
+ "RLIMIT_STACK": unix.RLIMIT_STACK,
+ }
+ rlimitsReverseMap = map[int]string{}
+ mountFlagMap = map[int]string{
+ unix.MS_ACTIVE: "MS_ACTIVE",
+ unix.MS_BIND: "MS_BIND",
+ unix.MS_BORN: "MS_BORN",
+ unix.MS_DIRSYNC: "MS_DIRSYNC",
+ unix.MS_KERNMOUNT: "MS_KERNMOUNT",
+ unix.MS_LAZYTIME: "MS_LAZYTIME",
+ unix.MS_MANDLOCK: "MS_MANDLOCK",
+ unix.MS_MOVE: "MS_MOVE",
+ unix.MS_NOATIME: "MS_NOATIME",
+ unix.MS_NODEV: "MS_NODEV",
+ unix.MS_NODIRATIME: "MS_NODIRATIME",
+ unix.MS_NOEXEC: "MS_NOEXEC",
+ unix.MS_NOREMOTELOCK: "MS_NOREMOTELOCK",
+ unix.MS_NOSEC: "MS_NOSEC",
+ unix.MS_NOSUID: "MS_NOSUID",
+ unix.MS_NOSYMFOLLOW: "MS_NOSYMFOLLOW",
+ unix.MS_NOUSER: "MS_NOUSER",
+ unix.MS_POSIXACL: "MS_POSIXACL",
+ unix.MS_PRIVATE: "MS_PRIVATE",
+ unix.MS_RDONLY: "MS_RDONLY",
+ unix.MS_REC: "MS_REC",
+ unix.MS_RELATIME: "MS_RELATIME",
+ unix.MS_REMOUNT: "MS_REMOUNT",
+ unix.MS_SHARED: "MS_SHARED",
+ unix.MS_SILENT: "MS_SILENT",
+ unix.MS_SLAVE: "MS_SLAVE",
+ unix.MS_STRICTATIME: "MS_STRICTATIME",
+ unix.MS_SUBMOUNT: "MS_SUBMOUNT",
+ unix.MS_SYNCHRONOUS: "MS_SYNCHRONOUS",
+ unix.MS_UNBINDABLE: "MS_UNBINDABLE",
+ }
+ statFlagMap = map[int]string{
+ unix.ST_MANDLOCK: "ST_MANDLOCK",
+ unix.ST_NOATIME: "ST_NOATIME",
+ unix.ST_NODEV: "ST_NODEV",
+ unix.ST_NODIRATIME: "ST_NODIRATIME",
+ unix.ST_NOEXEC: "ST_NOEXEC",
+ unix.ST_NOSUID: "ST_NOSUID",
+ unix.ST_RDONLY: "ST_RDONLY",
+ unix.ST_RELATIME: "ST_RELATIME",
+ unix.ST_SYNCHRONOUS: "ST_SYNCHRONOUS",
+ }
+)
+
+func mountFlagNames(flags uintptr) []string {
+ var names []string
+ for flag, name := range mountFlagMap {
+ if int(flags)&flag == flag {
+ names = append(names, name)
+ flags = flags &^ (uintptr(flag))
+ }
+ }
+ if flags != 0 { // got some unknown leftovers
+ names = append(names, fmt.Sprintf("%#x", flags))
+ }
+ slices.Sort(names)
+ return names
+}
+
+func statFlagNames(flags uintptr) []string {
+ var names []string
+ flags = flags & ^uintptr(0x20) // mask off ST_VALID
+ for flag, name := range statFlagMap {
+ if int(flags)&flag == flag {
+ names = append(names, name)
+ flags = flags &^ (uintptr(flag))
+ }
+ }
+ if flags != 0 { // got some unknown leftovers
+ names = append(names, fmt.Sprintf("%#x", flags))
+ }
+ slices.Sort(names)
+ return names
+}
+
+type runUsingChrootSubprocOptions struct {
+ Spec *specs.Spec
+ BundlePath string
+ NoPivot bool
+ UIDMappings []syscall.SysProcIDMap
+ GIDMappings []syscall.SysProcIDMap
+}
+
+func setPlatformUnshareOptions(spec *specs.Spec, cmd *unshare.Cmd) error {
+ // If we have configured ID mappings, set them here so that they can apply to the child.
+ hostUidmap, hostGidmap, err := unshare.GetHostIDMappings("")
+ if err != nil {
+ return err
+ }
+ uidmap, gidmap := spec.Linux.UIDMappings, spec.Linux.GIDMappings
+ if len(uidmap) == 0 {
+ // No UID mappings are configured for the container. Borrow our parent's mappings.
+ uidmap = slices.Clone(hostUidmap)
+ for i := range uidmap {
+ uidmap[i].HostID = uidmap[i].ContainerID
+ }
+ }
+ if len(gidmap) == 0 {
+ // No GID mappings are configured for the container. Borrow our parent's mappings.
+ gidmap = slices.Clone(hostGidmap)
+ for i := range gidmap {
+ gidmap[i].HostID = gidmap[i].ContainerID
+ }
+ }
+
+ cmd.UnshareFlags = syscall.CLONE_NEWUTS | syscall.CLONE_NEWNS
+ requestedUserNS := false
+ for _, ns := range spec.Linux.Namespaces {
+ if ns.Type == specs.UserNamespace {
+ requestedUserNS = true
+ }
+ }
+ if len(spec.Linux.UIDMappings) > 0 || len(spec.Linux.GIDMappings) > 0 || requestedUserNS {
+ cmd.UnshareFlags = cmd.UnshareFlags | syscall.CLONE_NEWUSER
+ cmd.UidMappings = uidmap
+ cmd.GidMappings = gidmap
+ cmd.GidMappingsEnableSetgroups = true
+ }
+ cmd.OOMScoreAdj = spec.Process.OOMScoreAdj
+ return nil
+}
+
+func setContainerHostname(name string) {
+ if err := unix.Sethostname([]byte(name)); err != nil {
+ logrus.Debugf("failed to set hostname %q for process: %v", name, err)
+ }
+}
+
+// logNamespaceDiagnostics knows which namespaces we want to create.
+// Output debug messages when that differs from what we're being asked to do.
+func logNamespaceDiagnostics(spec *specs.Spec) {
+ sawMountNS := false
+ sawUTSNS := false
+ for _, ns := range spec.Linux.Namespaces {
+ switch ns.Type {
+ case specs.CgroupNamespace:
+ if ns.Path != "" {
+ logrus.Debugf("unable to join cgroup namespace, sorry about that")
+ } else {
+ logrus.Debugf("unable to create cgroup namespace, sorry about that")
+ }
+ case specs.IPCNamespace:
+ if ns.Path != "" {
+ logrus.Debugf("unable to join IPC namespace, sorry about that")
+ } else {
+ logrus.Debugf("unable to create IPC namespace, sorry about that")
+ }
+ case specs.MountNamespace:
+ if ns.Path != "" {
+ logrus.Debugf("unable to join mount namespace %q, creating a new one", ns.Path)
+ }
+ sawMountNS = true
+ case specs.NetworkNamespace:
+ if ns.Path != "" {
+ logrus.Debugf("unable to join network namespace, sorry about that")
+ } else {
+ logrus.Debugf("unable to create network namespace, sorry about that")
+ }
+ case specs.PIDNamespace:
+ if ns.Path != "" {
+ logrus.Debugf("unable to join PID namespace, sorry about that")
+ } else {
+ logrus.Debugf("unable to create PID namespace, sorry about that")
+ }
+ case specs.UserNamespace:
+ if ns.Path != "" {
+ logrus.Debugf("unable to join user namespace, sorry about that")
+ }
+ case specs.UTSNamespace:
+ if ns.Path != "" {
+ logrus.Debugf("unable to join UTS namespace %q, creating a new one", ns.Path)
+ }
+ sawUTSNS = true
+ }
+ }
+ if !sawMountNS {
+ logrus.Debugf("mount namespace not requested, but creating a new one anyway")
+ }
+ if !sawUTSNS {
+ logrus.Debugf("UTS namespace not requested, but creating a new one anyway")
+ }
+}
+
+// setApparmorProfile sets the apparmor profile for ourselves, and hopefully any child processes that we'll start.
+func setApparmorProfile(spec *specs.Spec) error {
+ if !apparmor.IsEnabled() || spec.Process.ApparmorProfile == "" {
+ return nil
+ }
+ if err := apparmor.ApplyProfile(spec.Process.ApparmorProfile); err != nil {
+ return fmt.Errorf("setting apparmor profile to %q: %w", spec.Process.ApparmorProfile, err)
+ }
+ return nil
+}
+
+// setCapabilities sets capabilities for ourselves, to be more or less inherited by any processes that we'll start.
+func setCapabilities(spec *specs.Spec, keepCaps ...string) error {
+ currentCaps, err := capability.NewPid2(0)
+ if err != nil {
+ return fmt.Errorf("reading capabilities of current process: %w", err)
+ }
+ if err := currentCaps.Load(); err != nil {
+ return fmt.Errorf("loading capabilities: %w", err)
+ }
+ caps, err := capability.NewPid2(0)
+ if err != nil {
+ return fmt.Errorf("reading capabilities of current process: %w", err)
+ }
+ capMap := map[capability.CapType][]string{
+ capability.BOUNDING: spec.Process.Capabilities.Bounding,
+ capability.EFFECTIVE: spec.Process.Capabilities.Effective,
+ capability.INHERITABLE: {},
+ capability.PERMITTED: spec.Process.Capabilities.Permitted,
+ capability.AMBIENT: {},
+ }
+ knownCaps := capability.ListKnown()
+ noCap := capability.Cap(-1)
+ for capType, capList := range capMap {
+ for _, capSpec := range capList {
+ capToSet := noCap
+ for _, c := range knownCaps {
+ if strings.EqualFold("CAP_"+c.String(), capSpec) {
+ capToSet = c
+ break
+ }
+ }
+ if capToSet == noCap {
+ return fmt.Errorf("mapping capability %q to a number", capSpec)
+ }
+ caps.Set(capType, capToSet)
+ }
+ for _, capSpec := range keepCaps {
+ capToSet := noCap
+ for _, c := range knownCaps {
+ if strings.EqualFold("CAP_"+c.String(), capSpec) {
+ capToSet = c
+ break
+ }
+ }
+ if capToSet == noCap {
+ return fmt.Errorf("mapping capability %q to a number", capSpec)
+ }
+ if currentCaps.Get(capType, capToSet) {
+ caps.Set(capType, capToSet)
+ }
+ }
+ }
+ if err = caps.Apply(capability.CAPS | capability.BOUNDS | capability.AMBS); err != nil {
+ return fmt.Errorf("setting capabilities: %w", err)
+ }
+ return nil
+}
+
+func makeRlimit(limit specs.POSIXRlimit) unix.Rlimit {
+ return unix.Rlimit{Cur: limit.Soft, Max: limit.Hard}
+}
+
+func createPlatformContainer(options runUsingChrootExecSubprocOptions) error {
+ if options.NoPivot {
+ return errors.New("not using pivot_root()")
+ }
+ // borrowing a technique from runc, who credit the LXC maintainers for this
+ // open descriptors for the old and new root directories so that we can use fchdir()
+ oldRootFd, err := unix.Open("/", unix.O_DIRECTORY, 0)
+ if err != nil {
+ return fmt.Errorf("opening host root directory: %w", err)
+ }
+ defer func() {
+ if err := unix.Close(oldRootFd); err != nil {
+ logrus.Warnf("closing host root directory: %v", err)
+ }
+ }()
+ newRootFd, err := unix.Open(options.Spec.Root.Path, unix.O_DIRECTORY, 0)
+ if err != nil {
+ return fmt.Errorf("opening container root directory: %w", err)
+ }
+ defer func() {
+ if err := unix.Close(newRootFd); err != nil {
+ logrus.Warnf("closing container root directory: %v", err)
+ }
+ }()
+ // change to the new root directory
+ if err := unix.Fchdir(newRootFd); err != nil {
+ return fmt.Errorf("changing to container root directory: %w", err)
+ }
+ // this makes the current directory the root directory. not actually
+ // sure what happens to the other one
+ if err := unix.PivotRoot(".", "."); err != nil {
+ return fmt.Errorf("pivot_root: %w", err)
+ }
+ // go back and clean up the old one
+ if err := unix.Fchdir(oldRootFd); err != nil {
+ return fmt.Errorf("changing to host root directory: %w", err)
+ }
+ // make sure we only unmount things under this tree
+ if err := unix.Mount(".", ".", "", unix.MS_SLAVE|unix.MS_REC, ""); err != nil {
+ return fmt.Errorf("tweaking mount flags on host root directory before unmounting from mount namespace: %w", err)
+ }
+ // detach this (unnamed?) old directory
+ if err := unix.Unmount(".", unix.MNT_DETACH); err != nil {
+ return fmt.Errorf("unmounting host root directory in mount namespace: %w", err)
+ }
+ // go back to a named root directory
+ if err := unix.Fchdir(newRootFd); err != nil {
+ return fmt.Errorf("changing to container root directory at last: %w", err)
+ }
+ logrus.Debugf("pivot_root()ed into %q", options.Spec.Root.Path)
+ return nil
+}
+
+func mountFlagsForFSFlags(fsFlags uintptr) uintptr {
+ var mountFlags uintptr
+ for _, mapping := range []struct {
+ fsFlag uintptr
+ mountFlag uintptr
+ }{
+ {unix.ST_MANDLOCK, unix.MS_MANDLOCK},
+ {unix.ST_NOATIME, unix.MS_NOATIME},
+ {unix.ST_NODEV, unix.MS_NODEV},
+ {unix.ST_NODIRATIME, unix.MS_NODIRATIME},
+ {unix.ST_NOEXEC, unix.MS_NOEXEC},
+ {unix.ST_NOSUID, unix.MS_NOSUID},
+ {unix.ST_RDONLY, unix.MS_RDONLY},
+ {unix.ST_RELATIME, unix.MS_RELATIME},
+ {unix.ST_SYNCHRONOUS, unix.MS_SYNCHRONOUS},
+ } {
+ if fsFlags&mapping.fsFlag == mapping.fsFlag {
+ mountFlags |= mapping.mountFlag
+ }
+ }
+ return mountFlags
+}
+
+func makeReadOnly(mntpoint string, flags uintptr) error {
+ var fs unix.Statfs_t
+ // Make sure it's read-only.
+ if err := unix.Statfs(mntpoint, &fs); err != nil {
+ return fmt.Errorf("checking if directory %q was bound read-only: %w", mntpoint, err)
+ }
+ if fs.Flags&unix.ST_RDONLY == 0 {
+ // All callers currently pass MS_RDONLY in "flags", but in case they stop doing
+ // that at some point in the future...
+ if err := unix.Mount(mntpoint, mntpoint, "bind", flags|unix.MS_RDONLY|unix.MS_REMOUNT|unix.MS_BIND, ""); err != nil {
+ return fmt.Errorf("remounting %s in mount namespace read-only: %w", mntpoint, err)
+ }
+ }
+ return nil
+}
+
+// setupChrootBindMounts actually bind mounts things under the rootfs, and returns a
+// callback that will clean up its work.
+func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func() error, err error) {
+ var fs unix.Statfs_t
+ undoBinds = func() error {
+ if err2 := unix.Unmount(spec.Root.Path, unix.MNT_DETACH); err2 != nil {
+ retries := 0
+ for (err2 == unix.EBUSY || err2 == unix.EAGAIN) && retries < 50 {
+ time.Sleep(50 * time.Millisecond)
+ err2 = unix.Unmount(spec.Root.Path, unix.MNT_DETACH)
+ retries++
+ }
+ if err2 != nil {
+ logrus.Warnf("pkg/chroot: error unmounting %q (retried %d times): %v", spec.Root.Path, retries, err2)
+ if err == nil {
+ err = err2
+ }
+ }
+ }
+ return err
+ }
+
+ // Now bind mount all of those things to be under the rootfs's location in this
+ // mount namespace.
+ commonFlags := uintptr(unix.MS_BIND | unix.MS_REC | unix.MS_PRIVATE)
+ bindFlags := commonFlags
+ devFlags := commonFlags | unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_RDONLY
+ procFlags := devFlags | unix.MS_NODEV
+ sysFlags := devFlags | unix.MS_NODEV
+
+ // Bind /dev read-only.
+ subDev := filepath.Join(spec.Root.Path, "/dev")
+ if err := unix.Mount("/dev", subDev, "bind", devFlags, ""); err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ err = os.Mkdir(subDev, 0o755)
+ if err == nil {
+ err = unix.Mount("/dev", subDev, "bind", devFlags, "")
+ }
+ }
+ if err != nil {
+ return undoBinds, fmt.Errorf("bind mounting /dev from host into mount namespace: %w", err)
+ }
+ }
+ // Make sure it's read-only.
+ if err = unix.Statfs(subDev, &fs); err != nil {
+ return undoBinds, fmt.Errorf("checking if directory %q was bound read-only: %w", subDev, err)
+ }
+ if fs.Flags&unix.ST_RDONLY == 0 {
+ if err := unix.Mount(subDev, subDev, "bind", devFlags|unix.MS_REMOUNT|unix.MS_BIND, ""); err != nil {
+ return undoBinds, fmt.Errorf("remounting /dev in mount namespace read-only: %w", err)
+ }
+ }
+ logrus.Debugf("bind mounted %q to %q", "/dev", filepath.Join(spec.Root.Path, "/dev"))
+
+ // Bind /proc read-only.
+ subProc := filepath.Join(spec.Root.Path, "/proc")
+ if err := unix.Mount("/proc", subProc, "bind", procFlags, ""); err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ err = os.Mkdir(subProc, 0o755)
+ if err == nil {
+ err = unix.Mount("/proc", subProc, "bind", procFlags, "")
+ }
+ }
+ if err != nil {
+ return undoBinds, fmt.Errorf("bind mounting /proc from host into mount namespace: %w", err)
+ }
+ }
+ logrus.Debugf("bind mounted %q to %q", "/proc", filepath.Join(spec.Root.Path, "/proc"))
+
+ // Bind /sys read-only.
+ subSys := filepath.Join(spec.Root.Path, "/sys")
+ if err := unix.Mount("/sys", subSys, "bind", sysFlags, ""); err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ err = os.Mkdir(subSys, 0o755)
+ if err == nil {
+ err = unix.Mount("/sys", subSys, "bind", sysFlags, "")
+ }
+ }
+ if err != nil {
+ return undoBinds, fmt.Errorf("bind mounting /sys from host into mount namespace: %w", err)
+ }
+ }
+ if err := makeReadOnly(subSys, sysFlags); err != nil {
+ return undoBinds, err
+ }
+
+ mnts, _ := mount.GetMounts()
+ for _, m := range mnts {
+ if !strings.HasPrefix(m.Mountpoint, "/sys/") &&
+ m.Mountpoint != "/sys" {
+ continue
+ }
+ subSys := filepath.Join(spec.Root.Path, m.Mountpoint)
+ if err := unix.Mount(m.Mountpoint, subSys, "bind", sysFlags, ""); err != nil {
+ msg := fmt.Sprintf("could not bind mount %q, skipping: %v", m.Mountpoint, err)
+ if strings.HasPrefix(m.Mountpoint, "/sys") {
+ logrus.Info(msg)
+ } else {
+ logrus.Warning(msg)
+ }
+ continue
+ }
+ if err := makeReadOnly(subSys, sysFlags); err != nil {
+ return undoBinds, err
+ }
+ }
+ logrus.Debugf("bind mounted %q to %q", "/sys", filepath.Join(spec.Root.Path, "/sys"))
+
+ // Bind, overlay, or tmpfs mount everything we've been asked to mount.
+ for _, m := range spec.Mounts {
+ // Skip anything that we just mounted.
+ switch m.Destination {
+ case "/dev", "/proc", "/sys":
+ logrus.Debugf("already bind mounted %q on %q", m.Destination, filepath.Join(spec.Root.Path, m.Destination))
+ continue
+ default:
+ if strings.HasPrefix(m.Destination, "/dev/") {
+ continue
+ }
+ if strings.HasPrefix(m.Destination, "/proc/") {
+ continue
+ }
+ if strings.HasPrefix(m.Destination, "/sys/") {
+ continue
+ }
+ }
+ // Skip anything that isn't a bind or overlay or tmpfs mount.
+ if m.Type != "bind" && m.Type != "tmpfs" && m.Type != "overlay" {
+ logrus.Debugf("skipping mount of type %q on %q", m.Type, m.Destination)
+ continue
+ }
+ // If the target is already there, we can just mount over it.
+ var srcinfo os.FileInfo
+ switch m.Type {
+ case "bind":
+ srcinfo, err = os.Stat(m.Source)
+ if err != nil {
+ return undoBinds, fmt.Errorf("examining %q for mounting in mount namespace: %w", m.Source, err)
+ }
+ case "overlay", "tmpfs":
+ srcinfo, err = os.Stat("/")
+ if err != nil {
+ return undoBinds, fmt.Errorf("examining / to use as a template for a %s mount: %w", m.Type, err)
+ }
+ }
+ target := filepath.Join(spec.Root.Path, m.Destination)
+ // Check if target is a symlink.
+ stat, err := os.Lstat(target)
+ // If target is a symlink, follow the link and ensure the destination exists.
+ if err == nil && stat != nil && (stat.Mode()&os.ModeSymlink != 0) {
+ target, err = copier.Eval(spec.Root.Path, m.Destination, copier.EvalOptions{})
+ if err != nil {
+ return nil, fmt.Errorf("evaluating symlink %q: %w", target, err)
+ }
+ // Stat the destination of the evaluated symlink.
+ _, err = os.Stat(target)
+ }
+ if err != nil {
+ // If the target can't be stat()ted, check the error.
+ if !errors.Is(err, os.ErrNotExist) {
+ return undoBinds, fmt.Errorf("examining %q for mounting in mount namespace: %w", target, err)
+ }
+ // The target isn't there yet, so create it. If the source is a directory,
+ // we need a directory, otherwise we need a non-directory (i.e., a file).
+ if srcinfo.IsDir() {
+ if err = os.MkdirAll(target, 0o755); err != nil {
+ return undoBinds, fmt.Errorf("creating mountpoint %q in mount namespace: %w", target, err)
+ }
+ } else {
+ if err = os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
+ return undoBinds, fmt.Errorf("ensuring parent of mountpoint %q (%q) is present in mount namespace: %w", target, filepath.Dir(target), err)
+ }
+ var file *os.File
+ if file, err = os.OpenFile(target, os.O_WRONLY|os.O_CREATE, 0o755); err != nil {
+ return undoBinds, fmt.Errorf("creating mountpoint %q in mount namespace: %w", target, err)
+ }
+ file.Close()
+ }
+ }
+ // Sort out which flags we're asking for, and what statfs() should be telling us
+ // if we successfully mounted with them.
+ requestFlags := uintptr(0)
+ expectedImportantFlags := uintptr(0)
+ importantFlags := uintptr(0)
+ possibleImportantFlags := uintptr(unix.ST_NODEV | unix.ST_NOEXEC | unix.ST_NOSUID | unix.ST_RDONLY)
+ for _, option := range m.Options {
+ switch option {
+ case "nodev":
+ requestFlags |= unix.MS_NODEV
+ importantFlags |= unix.ST_NODEV
+ expectedImportantFlags |= unix.ST_NODEV
+ case "dev":
+ requestFlags &= ^uintptr(unix.MS_NODEV)
+ importantFlags |= unix.ST_NODEV
+ expectedImportantFlags &= ^uintptr(unix.ST_NODEV)
+ case "noexec":
+ requestFlags |= unix.MS_NOEXEC
+ importantFlags |= unix.ST_NOEXEC
+ expectedImportantFlags |= unix.ST_NOEXEC
+ case "exec":
+ requestFlags &= ^uintptr(unix.MS_NOEXEC)
+ importantFlags |= unix.ST_NOEXEC
+ expectedImportantFlags &= ^uintptr(unix.ST_NOEXEC)
+ case "nosuid":
+ requestFlags |= unix.MS_NOSUID
+ importantFlags |= unix.ST_NOSUID
+ expectedImportantFlags |= unix.ST_NOSUID
+ case "suid":
+ requestFlags &= ^uintptr(unix.MS_NOSUID)
+ importantFlags |= unix.ST_NOSUID
+ expectedImportantFlags &= ^uintptr(unix.ST_NOSUID)
+ case "ro":
+ requestFlags |= unix.MS_RDONLY
+ importantFlags |= unix.ST_RDONLY
+ expectedImportantFlags |= unix.ST_RDONLY
+ case "rw":
+ requestFlags &= ^uintptr(unix.MS_RDONLY)
+ importantFlags |= unix.ST_RDONLY
+ expectedImportantFlags &= ^uintptr(unix.ST_RDONLY)
+ }
+ }
+ switch m.Type {
+ case "bind":
+ // Do the initial bind mount. We'll worry about the flags in a bit.
+ logrus.Debugf("bind mounting %q on %q %v", m.Destination, filepath.Join(spec.Root.Path, m.Destination), m.Options)
+ if err = unix.Mount(m.Source, target, "", bindFlags|requestFlags, ""); err != nil {
+ return undoBinds, fmt.Errorf("bind mounting %q from host to %q in mount namespace (%q): %w", m.Source, m.Destination, target, err)
+ }
+ logrus.Debugf("bind mounted %q to %q", m.Source, target)
+ case "tmpfs":
+ // Mount a tmpfs. We'll worry about the flags in a bit.
+ if err = mount.Mount(m.Source, target, m.Type, strings.Join(append(m.Options, "private"), ",")); err != nil {
+ return undoBinds, fmt.Errorf("mounting tmpfs to %q in mount namespace (%q, %q): %w", m.Destination, target, strings.Join(append(m.Options, "private"), ","), err)
+ }
+ logrus.Debugf("mounted a tmpfs to %q", target)
+ case "overlay":
+ // Mount an overlay. We'll worry about the flags in a bit.
+ if err = mount.Mount(m.Source, target, m.Type, strings.Join(append(m.Options, "private"), ",")); err != nil {
+ return undoBinds, fmt.Errorf("mounting overlay to %q in mount namespace (%q, %q): %w", m.Destination, target, strings.Join(append(m.Options, "private"), ","), err)
+ }
+ logrus.Debugf("mounted a overlay to %q", target)
+ }
+ // Time to worry about the flags.
+ if err = unix.Statfs(target, &fs); err != nil {
+ return undoBinds, fmt.Errorf("checking if volume %q was mounted with requested flags: %w", target, err)
+ }
+ effectiveImportantFlags := uintptr(fs.Flags) & importantFlags
+ if effectiveImportantFlags != expectedImportantFlags {
+ // Do a remount to try to get the desired flags to stick.
+ effectiveUnimportantFlags := uintptr(fs.Flags) & ^possibleImportantFlags
+ remountFlags := unix.MS_REMOUNT | bindFlags | requestFlags | mountFlagsForFSFlags(effectiveUnimportantFlags)
+ // If we are requesting a read-only mount, add any possibleImportantFlags present in fs.Flags to remountFlags.
+ if requestFlags&unix.ST_RDONLY == unix.ST_RDONLY {
+ remountFlags |= uintptr(fs.Flags) & possibleImportantFlags
+ }
+ if err = unix.Mount(target, target, m.Type, remountFlags, ""); err != nil {
+ return undoBinds, fmt.Errorf("remounting %q in mount namespace with flags %v instead of %v: %w", target, mountFlagNames(requestFlags), statFlagNames(effectiveImportantFlags), err)
+ }
+ // Check if the desired flags stuck.
+ if err = unix.Statfs(target, &fs); err != nil {
+ return undoBinds, fmt.Errorf("checking if directory %q was remounted with requested flags %v instead of %v: %w", target, mountFlagNames(requestFlags), statFlagNames(effectiveImportantFlags), err)
+ }
+ newEffectiveImportantFlags := uintptr(fs.Flags) & importantFlags
+ if newEffectiveImportantFlags != expectedImportantFlags {
+ return undoBinds, fmt.Errorf("unable to remount %q with requested flags %v instead of %v, just got %v back", target, mountFlagNames(requestFlags), statFlagNames(effectiveImportantFlags), statFlagNames(newEffectiveImportantFlags))
+ }
+ }
+ }
+
+ // Set up any read-only paths that we need to. If we're running inside
+ // of a container, some of these locations will already be read-only, in
+ // which case can declare victory and move on.
+ for _, roPath := range spec.Linux.ReadonlyPaths {
+ r := filepath.Join(spec.Root.Path, roPath)
+ target, err := filepath.EvalSymlinks(r)
+ if err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ // No target, no problem.
+ continue
+ }
+ return undoBinds, fmt.Errorf("checking %q for symlinks before marking it read-only: %w", r, err)
+ }
+ // Check if the location is already read-only.
+ var fs unix.Statfs_t
+ if err = unix.Statfs(target, &fs); err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ // No target, no problem.
+ continue
+ }
+ return undoBinds, fmt.Errorf("checking if directory %q is already read-only: %w", target, err)
+ }
+ if fs.Flags&unix.ST_RDONLY == unix.ST_RDONLY {
+ continue
+ }
+ // Mount the location over itself, so that we can remount it as read-only, making
+ // sure to preserve any combination of nodev/noexec/nosuid that's already in play.
+ roFlags := mountFlagsForFSFlags(uintptr(fs.Flags)) | unix.MS_RDONLY
+ if err := unix.Mount(target, target, "", bindFlags|roFlags, ""); err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ // No target, no problem.
+ continue
+ }
+ return undoBinds, fmt.Errorf("bind mounting %q onto itself in preparation for making it read-only: %w", target, err)
+ }
+ // Remount the location read-only.
+ if err = unix.Statfs(target, &fs); err != nil {
+ return undoBinds, fmt.Errorf("checking if directory %q was bound read-only: %w", target, err)
+ }
+ if fs.Flags&unix.ST_RDONLY == 0 {
+ if err := unix.Mount(target, target, "", unix.MS_REMOUNT|unix.MS_RDONLY|bindFlags|mountFlagsForFSFlags(uintptr(fs.Flags)), ""); err != nil {
+ return undoBinds, fmt.Errorf("remounting %q in mount namespace read-only: %w", target, err)
+ }
+ }
+ // Check again.
+ if err = unix.Statfs(target, &fs); err != nil {
+ return undoBinds, fmt.Errorf("checking if directory %q was remounted read-only: %w", target, err)
+ }
+ if fs.Flags&unix.ST_RDONLY == 0 {
+ // Still not read only.
+ return undoBinds, fmt.Errorf("verifying that %q in mount namespace was remounted read-only: %w", target, err)
+ }
+ }
+
+ // Create an empty directory for to use for masking directories.
+ roEmptyDir := filepath.Join(bundlePath, "empty")
+ if len(spec.Linux.MaskedPaths) > 0 {
+ if err := os.Mkdir(roEmptyDir, 0o700); err != nil {
+ return undoBinds, fmt.Errorf("creating empty directory %q: %w", roEmptyDir, err)
+ }
+ }
+
+ // Set up any masked paths that we need to. If we're running inside of
+ // a container, some of these locations will already be read-only tmpfs
+ // filesystems or bind mounted to os.DevNull. If we're not running
+ // inside of a container, and nobody else has done that, we'll do it.
+ for _, masked := range spec.Linux.MaskedPaths {
+ t := filepath.Join(spec.Root.Path, masked)
+ target, err := filepath.EvalSymlinks(t)
+ if err != nil {
+ target = t
+ }
+ // Get some info about the target.
+ targetinfo, err := os.Stat(target)
+ if err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ // No target, no problem.
+ continue
+ }
+ return undoBinds, fmt.Errorf("examining %q for masking in mount namespace: %w", target, err)
+ }
+ if targetinfo.IsDir() {
+ // The target's a directory. Check if it's a read-only filesystem.
+ var statfs unix.Statfs_t
+ if err = unix.Statfs(target, &statfs); err != nil {
+ return undoBinds, fmt.Errorf("checking if directory %q is a mountpoint: %w", target, err)
+ }
+ isReadOnly := statfs.Flags&unix.ST_RDONLY == unix.ST_RDONLY
+ // Check if any of the IDs we're mapping could read it.
+ var stat unix.Stat_t
+ if err = unix.Stat(target, &stat); err != nil {
+ return undoBinds, fmt.Errorf("checking permissions on directory %q: %w", target, err)
+ }
+ isAccessible := false
+ if stat.Mode&unix.S_IROTH|unix.S_IXOTH != 0 {
+ isAccessible = true
+ }
+ if !isAccessible && stat.Mode&unix.S_IROTH|unix.S_IXOTH != 0 {
+ if len(spec.Linux.GIDMappings) > 0 {
+ for _, mapping := range spec.Linux.GIDMappings {
+ if stat.Gid >= mapping.ContainerID && stat.Gid < mapping.ContainerID+mapping.Size {
+ isAccessible = true
+ break
+ }
+ }
+ }
+ }
+ if !isAccessible && stat.Mode&unix.S_IRUSR|unix.S_IXUSR != 0 {
+ if len(spec.Linux.UIDMappings) > 0 {
+ for _, mapping := range spec.Linux.UIDMappings {
+ if stat.Uid >= mapping.ContainerID && stat.Uid < mapping.ContainerID+mapping.Size {
+ isAccessible = true
+ break
+ }
+ }
+ }
+ }
+ // Check if it's empty.
+ hasContent := false
+ directory, err := os.Open(target)
+ if err != nil {
+ if !os.IsPermission(err) {
+ return undoBinds, fmt.Errorf("opening directory %q: %w", target, err)
+ }
+ } else {
+ names, err := directory.Readdirnames(0)
+ directory.Close()
+ if err != nil {
+ return undoBinds, fmt.Errorf("reading contents of directory %q: %w", target, err)
+ }
+ hasContent = false
+ for _, name := range names {
+ switch name {
+ case ".", "..":
+ continue
+ default:
+ hasContent = true
+ }
+ if hasContent {
+ break
+ }
+ }
+ }
+ // The target's a directory, so read-only bind mount an empty directory on it.
+ roFlags := uintptr(syscall.MS_BIND | syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC | syscall.MS_RDONLY)
+ if !isReadOnly || (hasContent && isAccessible) {
+ if err = unix.Mount(roEmptyDir, target, "bind", roFlags, ""); err != nil {
+ return undoBinds, fmt.Errorf("masking directory %q in mount namespace: %w", target, err)
+ }
+ if err = unix.Statfs(target, &fs); err != nil {
+ return undoBinds, fmt.Errorf("checking if masked directory %q was mounted read-only in mount namespace: %w", target, err)
+ }
+ if fs.Flags&unix.ST_RDONLY == 0 {
+ if err = unix.Mount(target, target, "", syscall.MS_REMOUNT|roFlags|mountFlagsForFSFlags(uintptr(fs.Flags)), ""); err != nil {
+ return undoBinds, fmt.Errorf("making sure masked directory %q in mount namespace is read only: %w", target, err)
+ }
+ }
+ }
+ } else {
+ // If the target's is not a directory or os.DevNull, bind mount os.DevNull over it.
+ if !isDevNull(targetinfo) {
+ if err = unix.Mount(os.DevNull, target, "", uintptr(syscall.MS_BIND|syscall.MS_RDONLY|syscall.MS_PRIVATE), ""); err != nil {
+ return undoBinds, fmt.Errorf("masking non-directory %q in mount namespace: %w", target, err)
+ }
+ }
+ }
+ }
+ return undoBinds, nil
+}
+
+// setPdeathsig sets a parent-death signal for the process
+func setPdeathsig(cmd *exec.Cmd) {
+ if cmd.SysProcAttr == nil {
+ cmd.SysProcAttr = &syscall.SysProcAttr{}
+ }
+ cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
+}
diff --git a/vendor/github.com/containers/buildah/chroot/seccomp.go b/vendor/github.com/containers/buildah/chroot/seccomp.go
new file mode 100644
index 00000000..350e28cf
--- /dev/null
+++ b/vendor/github.com/containers/buildah/chroot/seccomp.go
@@ -0,0 +1,176 @@
+//go:build linux && seccomp
+
+package chroot
+
+import (
+ "fmt"
+
+ specs "github.com/opencontainers/runtime-spec/specs-go"
+ libseccomp "github.com/seccomp/libseccomp-golang"
+ "github.com/sirupsen/logrus"
+)
+
+// setSeccomp sets the seccomp filter for ourselves and any processes that we'll start.
+func setSeccomp(spec *specs.Spec) error {
+ logrus.Debugf("setting seccomp configuration")
+ if spec.Linux.Seccomp == nil {
+ return nil
+ }
+ mapAction := func(specAction specs.LinuxSeccompAction, errnoRet *uint) libseccomp.ScmpAction {
+ switch specAction {
+ case specs.ActKill:
+ return libseccomp.ActKillThread
+ case specs.ActTrap:
+ return libseccomp.ActTrap
+ case specs.ActErrno:
+ action := libseccomp.ActErrno
+ if errnoRet != nil {
+ action = action.SetReturnCode(int16(*errnoRet))
+ }
+ return action
+ case specs.ActTrace:
+ return libseccomp.ActTrace
+ case specs.ActAllow:
+ return libseccomp.ActAllow
+ case specs.ActLog:
+ return libseccomp.ActLog
+ case specs.ActKillProcess:
+ return libseccomp.ActKillProcess
+ default:
+ logrus.Errorf("unmappable action %v", specAction)
+ }
+ return libseccomp.ActInvalid
+ }
+ mapArch := func(specArch specs.Arch) libseccomp.ScmpArch {
+ switch specArch {
+ case specs.ArchX86:
+ return libseccomp.ArchX86
+ case specs.ArchX86_64:
+ return libseccomp.ArchAMD64
+ case specs.ArchX32:
+ return libseccomp.ArchX32
+ case specs.ArchARM:
+ return libseccomp.ArchARM
+ case specs.ArchAARCH64:
+ return libseccomp.ArchARM64
+ case specs.ArchMIPS:
+ return libseccomp.ArchMIPS
+ case specs.ArchMIPS64:
+ return libseccomp.ArchMIPS64
+ case specs.ArchMIPS64N32:
+ return libseccomp.ArchMIPS64N32
+ case specs.ArchMIPSEL:
+ return libseccomp.ArchMIPSEL
+ case specs.ArchMIPSEL64:
+ return libseccomp.ArchMIPSEL64
+ case specs.ArchMIPSEL64N32:
+ return libseccomp.ArchMIPSEL64N32
+ case specs.ArchPPC:
+ return libseccomp.ArchPPC
+ case specs.ArchPPC64:
+ return libseccomp.ArchPPC64
+ case specs.ArchPPC64LE:
+ return libseccomp.ArchPPC64LE
+ case specs.ArchS390:
+ return libseccomp.ArchS390
+ case specs.ArchS390X:
+ return libseccomp.ArchS390X
+ case specs.ArchPARISC:
+ return libseccomp.ArchPARISC
+ case specs.ArchPARISC64:
+ return libseccomp.ArchPARISC64
+ case specs.ArchRISCV64:
+ return libseccomp.ArchRISCV64
+ default:
+ logrus.Errorf("unmappable arch %v", specArch)
+ }
+ return libseccomp.ArchInvalid
+ }
+ mapOp := func(op specs.LinuxSeccompOperator) libseccomp.ScmpCompareOp {
+ switch op {
+ case specs.OpNotEqual:
+ return libseccomp.CompareNotEqual
+ case specs.OpLessThan:
+ return libseccomp.CompareLess
+ case specs.OpLessEqual:
+ return libseccomp.CompareLessOrEqual
+ case specs.OpEqualTo:
+ return libseccomp.CompareEqual
+ case specs.OpGreaterEqual:
+ return libseccomp.CompareGreaterEqual
+ case specs.OpGreaterThan:
+ return libseccomp.CompareGreater
+ case specs.OpMaskedEqual:
+ return libseccomp.CompareMaskedEqual
+ default:
+ logrus.Errorf("unmappable op %v", op)
+ }
+ return libseccomp.CompareInvalid
+ }
+
+ filter, err := libseccomp.NewFilter(mapAction(spec.Linux.Seccomp.DefaultAction, spec.Linux.Seccomp.DefaultErrnoRet))
+ if err != nil {
+ return fmt.Errorf("creating seccomp filter with default action %q: %w", spec.Linux.Seccomp.DefaultAction, err)
+ }
+ for _, arch := range spec.Linux.Seccomp.Architectures {
+ if err = filter.AddArch(mapArch(arch)); err != nil {
+ return fmt.Errorf("adding architecture %q(%q) to seccomp filter: %w", arch, mapArch(arch), err)
+ }
+ }
+ for _, rule := range spec.Linux.Seccomp.Syscalls {
+ scnames := make(map[libseccomp.ScmpSyscall]string)
+ for _, name := range rule.Names {
+ scnum, err := libseccomp.GetSyscallFromName(name)
+ if err != nil {
+ logrus.Debugf("error mapping syscall %q to a syscall, ignoring %q rule for %q", name, rule.Action, name)
+ continue
+ }
+ scnames[scnum] = name
+ }
+ for scnum := range scnames {
+ if len(rule.Args) == 0 {
+ if err = filter.AddRule(scnum, mapAction(rule.Action, rule.ErrnoRet)); err != nil {
+ return fmt.Errorf("adding a rule (%q:%q) to seccomp filter: %w", scnames[scnum], rule.Action, err)
+ }
+ continue
+ }
+ var conditions []libseccomp.ScmpCondition
+ opsAreAllEquality := true
+ for _, arg := range rule.Args {
+ condition, err := libseccomp.MakeCondition(arg.Index, mapOp(arg.Op), arg.Value, arg.ValueTwo)
+ if err != nil {
+ return fmt.Errorf("building a seccomp condition %d:%v:%d:%d: %w", arg.Index, arg.Op, arg.Value, arg.ValueTwo, err)
+ }
+ if arg.Op != specs.OpEqualTo {
+ opsAreAllEquality = false
+ }
+ conditions = append(conditions, condition)
+ }
+ if err = filter.AddRuleConditional(scnum, mapAction(rule.Action, rule.ErrnoRet), conditions); err != nil {
+ // Okay, if the rules specify multiple equality
+ // checks, assume someone thought that they
+ // were OR'd, when in fact they're ordinarily
+ // supposed to be AND'd. Break them up into
+ // different rules to get that OR effect.
+ if len(rule.Args) > 1 && opsAreAllEquality && err.Error() == "two checks on same syscall argument" {
+ for i := range conditions {
+ if err = filter.AddRuleConditional(scnum, mapAction(rule.Action, rule.ErrnoRet), conditions[i:i+1]); err != nil {
+ return fmt.Errorf("adding a conditional rule (%q:%q[%d]) to seccomp filter: %w", scnames[scnum], rule.Action, i, err)
+ }
+ }
+ } else {
+ return fmt.Errorf("adding a conditional rule (%q:%q) to seccomp filter: %w", scnames[scnum], rule.Action, err)
+ }
+ }
+ }
+ }
+ if err = filter.SetNoNewPrivsBit(spec.Process.NoNewPrivileges); err != nil {
+ return fmt.Errorf("setting no-new-privileges bit to %v: %w", spec.Process.NoNewPrivileges, err)
+ }
+ err = filter.Load()
+ filter.Release()
+ if err != nil {
+ return fmt.Errorf("activating seccomp filter: %w", err)
+ }
+ return nil
+}
diff --git a/vendor/github.com/containers/buildah/chroot/seccomp_freebsd.go b/vendor/github.com/containers/buildah/chroot/seccomp_freebsd.go
new file mode 100644
index 00000000..134a5c4a
--- /dev/null
+++ b/vendor/github.com/containers/buildah/chroot/seccomp_freebsd.go
@@ -0,0 +1,14 @@
+//go:build freebsd && seccomp
+
+package chroot
+
+import (
+ "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+const seccompAvailable = false
+
+func setSeccomp(spec *specs.Spec) error {
+ // Ignore this on FreeBSD
+ return nil
+}
diff --git a/vendor/github.com/containers/buildah/chroot/seccomp_unsupported.go b/vendor/github.com/containers/buildah/chroot/seccomp_unsupported.go
new file mode 100644
index 00000000..1d0387bc
--- /dev/null
+++ b/vendor/github.com/containers/buildah/chroot/seccomp_unsupported.go
@@ -0,0 +1,16 @@
+//go:build (!linux && !freebsd) || !seccomp
+
+package chroot
+
+import (
+ "errors"
+
+ "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+func setSeccomp(spec *specs.Spec) error {
+ if spec.Linux.Seccomp != nil {
+ return errors.New("configured a seccomp filter without seccomp support?")
+ }
+ return nil
+}
diff --git a/vendor/github.com/containers/buildah/chroot/selinux.go b/vendor/github.com/containers/buildah/chroot/selinux.go
new file mode 100644
index 00000000..dd253cd4
--- /dev/null
+++ b/vendor/github.com/containers/buildah/chroot/selinux.go
@@ -0,0 +1,22 @@
+//go:build linux
+
+package chroot
+
+import (
+ "fmt"
+
+ "github.com/opencontainers/runtime-spec/specs-go"
+ selinux "github.com/opencontainers/selinux/go-selinux"
+ "github.com/sirupsen/logrus"
+)
+
+// setSelinuxLabel sets the process label for child processes that we'll start.
+func setSelinuxLabel(spec *specs.Spec) error {
+ logrus.Debugf("setting selinux label")
+ if spec.Process.SelinuxLabel != "" && selinux.GetEnabled() {
+ if err := selinux.SetExecLabel(spec.Process.SelinuxLabel); err != nil {
+ return fmt.Errorf("setting process label to %q: %w", spec.Process.SelinuxLabel, err)
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/containers/buildah/chroot/selinux_unsupported.go b/vendor/github.com/containers/buildah/chroot/selinux_unsupported.go
new file mode 100644
index 00000000..ae9ac896
--- /dev/null
+++ b/vendor/github.com/containers/buildah/chroot/selinux_unsupported.go
@@ -0,0 +1,19 @@
+//go:build !linux && !freebsd
+
+package chroot
+
+import (
+ "errors"
+
+ "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+func setSelinuxLabel(spec *specs.Spec) error {
+ if spec.Linux.MountLabel != "" {
+ return errors.New("configured an SELinux mount label without SELinux support?")
+ }
+ if spec.Process.SelinuxLabel != "" {
+ return errors.New("configured an SELinux process label without SELinux support?")
+ }
+ return nil
+}
diff --git a/vendor/github.com/containers/buildah/chroot/unsupported.go b/vendor/github.com/containers/buildah/chroot/unsupported.go
new file mode 100644
index 00000000..a2e7f45b
--- /dev/null
+++ b/vendor/github.com/containers/buildah/chroot/unsupported.go
@@ -0,0 +1,15 @@
+//go:build !linux && !freebsd
+
+package chroot
+
+import (
+ "fmt"
+ "io"
+
+ "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+// RunUsingChroot is not supported.
+func RunUsingChroot(spec *specs.Spec, bundlePath, homeDir string, stdin io.Reader, stdout, stderr io.Writer) (err error) {
+ return fmt.Errorf("--isolation chroot is not supported on this platform")
+}
diff --git a/vendor/github.com/containers/buildah/commit.go b/vendor/github.com/containers/buildah/commit.go
new file mode 100644
index 00000000..1ae6c012
--- /dev/null
+++ b/vendor/github.com/containers/buildah/commit.go
@@ -0,0 +1,561 @@
+package buildah
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "maps"
+ "os"
+ "strings"
+ "time"
+
+ "github.com/containers/buildah/pkg/blobcache"
+ "github.com/containers/buildah/util"
+ encconfig "github.com/containers/ocicrypt/config"
+ digest "github.com/opencontainers/go-digest"
+ v1 "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/sirupsen/logrus"
+ "go.podman.io/common/libimage"
+ "go.podman.io/common/libimage/manifests"
+ "go.podman.io/image/v5/docker"
+ "go.podman.io/image/v5/docker/reference"
+ "go.podman.io/image/v5/manifest"
+ "go.podman.io/image/v5/signature"
+ is "go.podman.io/image/v5/storage"
+ "go.podman.io/image/v5/transports"
+ "go.podman.io/image/v5/types"
+ "go.podman.io/storage/pkg/archive"
+ "go.podman.io/storage/pkg/stringid"
+)
+
+const (
+ // BuilderIdentityAnnotation is the name of the label which will be set
+ // to contain the name and version of the producer of the image at
+ // commit-time. (N.B. yes, the constant's name includes "Annotation",
+ // but it's added as a label.)
+ BuilderIdentityAnnotation = "io.buildah.version"
+)
+
+// CommitOptions can be used to alter how an image is committed.
+type CommitOptions struct {
+ // PreferredManifestType is the preferred type of image manifest. The
+ // image configuration format will be of a compatible type.
+ PreferredManifestType string
+ // Compression specifies the type of compression which is applied to
+ // layer blobs. The default is to not use compression, but
+ // archive.Gzip is recommended.
+ Compression archive.Compression
+ // SignaturePolicyPath specifies an override location for the signature
+ // policy which should be used for verifying the new image as it is
+ // being written. Except in specific circumstances, no value should be
+ // specified, indicating that the shared, system-wide default policy
+ // should be used.
+ SignaturePolicyPath string
+ // AdditionalTags is a list of additional names to add to the image, if
+ // the transport to which we're writing the image gives us a way to add
+ // them.
+ AdditionalTags []string
+ // ReportWriter is an io.Writer which will be used to log the writing
+ // of the new image.
+ ReportWriter io.Writer
+ // HistoryTimestamp specifies a timestamp to use for the image's
+ // created-on date, the corresponding field in new history entries, and
+ // the timestamps to set on contents in new layer diffs. If left
+ // unset, the current time is used for the configuration and manifest,
+ // and timestamps of layer contents are used as-is.
+ HistoryTimestamp *time.Time
+ // SourceDateEpoch specifies a timestamp to use for the image's
+ // created-on date and the corresponding field in new history entries.
+ // If left unset, the current time is used for the configuration and
+ // manifest.
+ SourceDateEpoch *time.Time
+ // RewriteTimestamp, if set, forces timestamps in generated layers to
+ // not be later than the SourceDateEpoch, if it is set.
+ RewriteTimestamp bool
+ // github.com/containers/image/types SystemContext to hold credentials
+ // and other authentication/authorization information.
+ SystemContext *types.SystemContext
+ // IIDFile tells the builder to write the image ID to the specified file
+ IIDFile string
+ // Squash tells the builder to produce an image with a single layer
+ // instead of with possibly more than one layer.
+ Squash bool
+ // OmitHistory tells the builder to ignore the history of build layers and
+ // base while preparing image-spec, setting this to true will ensure no history
+ // is added to the image-spec. (default false)
+ OmitHistory bool
+ // BlobDirectory is the name of a directory in which we'll look for
+ // prebuilt copies of layer blobs that we might otherwise need to
+ // regenerate from on-disk layers. If blobs are available, the
+ // manifest of the new image will reference the blobs rather than
+ // on-disk layers.
+ BlobDirectory string
+ // EmptyLayer tells the builder to omit the diff for the working
+ // container.
+ EmptyLayer bool
+ // OmitLayerHistoryEntry tells the builder to omit the diff for the
+ // working container and to not add an entry in the commit history. By
+ // default, the rest of the image's history is preserved, subject to
+ // the OmitHistory setting. N.B.: setting this flag, without any
+ // PrependedEmptyLayers, AppendedEmptyLayers, PrependedLinkedLayers, or
+ // AppendedLinkedLayers will more or less produce a copy of the base
+ // image.
+ OmitLayerHistoryEntry bool
+ // OmitTimestamp forces epoch 0 as created timestamp to allow for
+ // deterministic, content-addressable builds.
+ // Deprecated: use HistoryTimestamp or SourceDateEpoch (possibly with
+ // RewriteTimestamp) instead.
+ OmitTimestamp bool
+ // SignBy is the fingerprint of a GPG key to use for signing the image.
+ SignBy string
+ // Manifest list to add the image to.
+ Manifest string
+ // MaxRetries is the maximum number of attempts we'll make to commit
+ // the image to an external registry if the first attempt fails.
+ MaxRetries int
+ // RetryDelay is how long to wait before retrying a commit attempt to a
+ // registry.
+ RetryDelay time.Duration
+ // OciEncryptConfig when non-nil indicates that an image should be encrypted.
+ // The encryption options is derived from the construction of EncryptConfig object.
+ OciEncryptConfig *encconfig.EncryptConfig
+ // OciEncryptLayers represents the list of layers to encrypt.
+ // If nil, don't encrypt any layers.
+ // If non-nil and len==0, denotes encrypt all layers.
+ // integers in the slice represent 0-indexed layer indices, with support for negative
+ // indexing. i.e. 0 is the first layer, -1 is the last (top-most) layer.
+ OciEncryptLayers *[]int
+ // ConfidentialWorkloadOptions is used to force the output image's rootfs to contain a
+ // LUKS-compatibly encrypted disk image (for use with krun) instead of the usual
+ // contents of a rootfs.
+ ConfidentialWorkloadOptions ConfidentialWorkloadOptions
+ // UnsetEnvs is a list of environments to not add to final image.
+ // Deprecated: use UnsetEnv() before committing, or set OverrideChanges
+ // instead.
+ UnsetEnvs []string
+ // OverrideConfig is an optional Schema2Config which can override parts
+ // of the working container's configuration for the image that is being
+ // committed.
+ OverrideConfig *manifest.Schema2Config
+ // OverrideChanges is a slice of Dockerfile-style instructions to make
+ // to the configuration of the image that is being committed, after
+ // OverrideConfig is applied.
+ OverrideChanges []string
+ // ExtraImageContent is a map which describes additional content to add
+ // to the new layer in the committed image. The map's keys are
+ // filesystem paths in the image and the corresponding values are the
+ // paths of files whose contents will be used in their place. The
+ // contents will be owned by 0:0 and have mode 0o644. Currently only
+ // accepts regular files.
+ ExtraImageContent map[string]string
+ // SBOMScanOptions encapsulates options which control whether or not we
+ // run scanners on the rootfs that we're about to commit, and how.
+ SBOMScanOptions []SBOMScanOptions
+ // CompatSetParent causes the "parent" field to be set when committing
+ // the image in Docker format. Newer BuildKit-based builds don't set
+ // this field.
+ CompatSetParent types.OptionalBool
+ // CompatLayerOmissions causes the "/dev", "/proc", and "/sys"
+ // directories to be omitted from the layer diff and related output, as
+ // the classic builder did. Newer BuildKit-based builds include them
+ // in the built image by default.
+ CompatLayerOmissions types.OptionalBool
+ // PrependedLinkedLayers and AppendedLinkedLayers are combinations of
+ // history entries and locations of either directory trees (if
+ // directories, per os.Stat()) or uncompressed layer blobs which should
+ // be added to the image at commit-time. The order of these relative
+ // to PrependedEmptyLayers and AppendedEmptyLayers, and relative to the
+ // corresponding members in the Builder object, in the committed image
+ // is not guaranteed.
+ PrependedLinkedLayers, AppendedLinkedLayers []LinkedLayer
+ // UnsetAnnotations is a list of annotations (names only) to withhold
+ // from the image.
+ UnsetAnnotations []string
+ // Annotations is a list of annotations (in the form "key=value") to
+ // add to the image.
+ Annotations []string
+ // CreatedAnnotation controls whether or not an "org.opencontainers.image.created"
+ // annotation is present in the output image.
+ CreatedAnnotation types.OptionalBool
+}
+
+// LinkedLayer combines a history entry with the location of either a directory
+// tree (if it's a directory, per os.Stat()) or an uncompressed layer blob
+// which should be added to the image at commit-time. The BlobPath and
+// History.EmptyLayer fields should be considered mutually-exclusive.
+type LinkedLayer struct {
+ History v1.History // history entry to add
+ BlobPath string // corresponding uncompressed blob file (layer as a tar archive), or directory tree to archive
+}
+
+// storageAllowedPolicyScopes overrides the policy for local storage
+// to ensure that we can read images from it.
+var storageAllowedPolicyScopes = signature.PolicyTransportScopes{
+ "": []signature.PolicyRequirement{
+ signature.NewPRInsecureAcceptAnything(),
+ },
+}
+
+// checkRegistrySourcesAllows checks the $BUILD_REGISTRY_SOURCES environment
+// variable, if it's set. The contents are expected to be a JSON-encoded
+// github.com/openshift/api/config/v1.Image, set by an OpenShift build
+// controller that arranged for us to be run in a container.
+func checkRegistrySourcesAllows(forWhat string, dest types.ImageReference) (insecure bool, err error) {
+ transport := dest.Transport()
+ if transport == nil {
+ return false, nil
+ }
+ if transport.Name() != docker.Transport.Name() {
+ return false, nil
+ }
+ dref := dest.DockerReference()
+ if dref == nil || reference.Domain(dref) == "" {
+ return false, nil
+ }
+
+ if registrySources, ok := os.LookupEnv("BUILD_REGISTRY_SOURCES"); ok && len(registrySources) > 0 {
+ // Use local struct instead of github.com/openshift/api/config/v1 RegistrySources
+ var sources struct {
+ InsecureRegistries []string `json:"insecureRegistries,omitempty"`
+ BlockedRegistries []string `json:"blockedRegistries,omitempty"`
+ AllowedRegistries []string `json:"allowedRegistries,omitempty"`
+ }
+ if err := json.Unmarshal([]byte(registrySources), &sources); err != nil {
+ return false, fmt.Errorf("parsing $BUILD_REGISTRY_SOURCES (%q) as JSON: %w", registrySources, err)
+ }
+ blocked := false
+ if len(sources.BlockedRegistries) > 0 {
+ for _, blockedDomain := range sources.BlockedRegistries {
+ if blockedDomain == reference.Domain(dref) {
+ blocked = true
+ }
+ }
+ }
+ if blocked {
+ return false, fmt.Errorf("%s registry at %q denied by policy: it is in the blocked registries list", forWhat, reference.Domain(dref))
+ }
+ allowed := true
+ if len(sources.AllowedRegistries) > 0 {
+ allowed = false
+ for _, allowedDomain := range sources.AllowedRegistries {
+ if allowedDomain == reference.Domain(dref) {
+ allowed = true
+ }
+ }
+ }
+ if !allowed {
+ return false, fmt.Errorf("%s registry at %q denied by policy: not in allowed registries list", forWhat, reference.Domain(dref))
+ }
+ if len(sources.InsecureRegistries) > 0 {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func (b *Builder) addManifest(ctx context.Context, manifestName string, imageSpec string) (string, error) {
+ var create bool
+ systemContext := &types.SystemContext{}
+ var list manifests.List
+ runtime, err := libimage.RuntimeFromStore(b.store, &libimage.RuntimeOptions{SystemContext: systemContext})
+ if err != nil {
+ return "", err
+ }
+ manifestList, err := runtime.LookupManifestList(manifestName)
+ if err != nil {
+ create = true
+ list = manifests.Create()
+ } else {
+ locker, err := manifests.LockerForImage(b.store, manifestList.ID())
+ if err != nil {
+ return "", err
+ }
+ locker.Lock()
+ defer locker.Unlock()
+ _, list, err = manifests.LoadFromImage(b.store, manifestList.ID())
+ if err != nil {
+ return "", err
+ }
+ }
+
+ names, err := util.ExpandNames([]string{manifestName}, systemContext, b.store)
+ if err != nil {
+ return "", fmt.Errorf("encountered while expanding manifest list name %q: %w", manifestName, err)
+ }
+
+ ref, err := util.VerifyTagName(imageSpec)
+ if err != nil {
+ // check if the local image exists
+ if ref, _, err = util.FindImage(b.store, "", systemContext, imageSpec); err != nil {
+ return "", err
+ }
+ }
+
+ if _, err = list.Add(ctx, systemContext, ref, true); err != nil {
+ return "", err
+ }
+ var imageID string
+ if create {
+ imageID, err = list.SaveToImage(b.store, "", names, manifest.DockerV2ListMediaType)
+ } else {
+ imageID, err = list.SaveToImage(b.store, manifestList.ID(), nil, "")
+ }
+ return imageID, err
+}
+
+// Commit writes the contents of the container, along with its updated
+// configuration, to a new image in the specified location, and if we know how,
+// add any additional tags that were specified. Returns the ID of the new image
+// if commit was successful and the image destination was local.
+func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options CommitOptions) (string, reference.Canonical, digest.Digest, error) {
+ var (
+ imgID string
+ src types.ImageReference
+ destinationTimestamp *time.Time
+ )
+
+ // If we weren't given a name, build a destination reference using a
+ // temporary name that we'll remove later. The correct thing to do
+ // would be to read the manifest and configuration blob, and ask the
+ // manifest for the ID that we'd give the image, but that computation
+ // requires that we know the digests of the layer blobs, which we don't
+ // want to compute here because we'll have to do it again when
+ // cp.Image() instantiates a source image, and we don't want to do the
+ // work twice.
+ if options.OmitTimestamp {
+ if options.HistoryTimestamp != nil {
+ return imgID, nil, "", fmt.Errorf("OmitTimestamp and HistoryTimestamp can not be used together")
+ }
+ timestamp := time.Unix(0, 0).UTC()
+ options.HistoryTimestamp = ×tamp
+ }
+ destinationTimestamp = options.HistoryTimestamp
+ if options.SourceDateEpoch != nil {
+ destinationTimestamp = options.SourceDateEpoch
+ }
+ nameToRemove := ""
+ if dest == nil {
+ nameToRemove = stringid.GenerateRandomID() + "-tmp"
+ dest2, err := is.Transport.ParseStoreReference(b.store, nameToRemove)
+ if err != nil {
+ return imgID, nil, "", fmt.Errorf("creating temporary destination reference for image: %w", err)
+ }
+ dest = dest2
+ }
+
+ systemContext := getSystemContext(b.store, options.SystemContext, options.SignaturePolicyPath)
+
+ blocked, err := isReferenceBlocked(dest, systemContext)
+ if err != nil {
+ return "", nil, "", fmt.Errorf("checking if committing to registry for %q is blocked: %w", transports.ImageName(dest), err)
+ }
+ if blocked {
+ return "", nil, "", fmt.Errorf("commit access to registry for %q is blocked by configuration", transports.ImageName(dest))
+ }
+
+ // Load the system signing policy.
+ commitPolicy, err := signature.DefaultPolicy(systemContext)
+ if err != nil {
+ return "", nil, "", fmt.Errorf("obtaining default signature policy: %w", err)
+ }
+ // Override the settings for local storage to make sure that we can always read the source "image".
+ commitPolicy.Transports[is.Transport.Name()] = storageAllowedPolicyScopes
+
+ policyContext, err := signature.NewPolicyContext(commitPolicy)
+ if err != nil {
+ return imgID, nil, "", fmt.Errorf("creating new signature policy context: %w", err)
+ }
+ defer func() {
+ if err2 := policyContext.Destroy(); err2 != nil {
+ logrus.Debugf("error destroying signature policy context: %v", err2)
+ }
+ }()
+
+ // Check if the commit is blocked by $BUILDER_REGISTRY_SOURCES.
+ insecure, err := checkRegistrySourcesAllows("commit to", dest)
+ if err != nil {
+ return imgID, nil, "", err
+ }
+ if insecure {
+ if systemContext.DockerInsecureSkipTLSVerify == types.OptionalBoolFalse {
+ return imgID, nil, "", fmt.Errorf("can't require tls verification on an insecured registry")
+ }
+ systemContext.DockerInsecureSkipTLSVerify = types.OptionalBoolTrue
+ systemContext.OCIInsecureSkipTLSVerify = true
+ systemContext.DockerDaemonInsecureSkipTLSVerify = true
+ }
+ logrus.Debugf("committing image with reference %q is allowed by policy", transports.ImageName(dest))
+
+ // If we need to scan the rootfs, do it now.
+ options.ExtraImageContent = maps.Clone(options.ExtraImageContent)
+ var extraImageContent, extraLocalContent map[string]string
+ if len(options.SBOMScanOptions) != 0 {
+ var scansDirectory string
+ if extraImageContent, extraLocalContent, scansDirectory, err = b.sbomScan(ctx, options); err != nil {
+ return imgID, nil, "", fmt.Errorf("scanning rootfs to generate SBOM for container %q: %w", b.ContainerID, err)
+ }
+ if scansDirectory != "" {
+ defer func() {
+ if err := os.RemoveAll(scansDirectory); err != nil {
+ logrus.Warnf("removing temporary directory %q: %v", scansDirectory, err)
+ }
+ }()
+ }
+ if len(extraImageContent) > 0 {
+ if options.ExtraImageContent == nil {
+ options.ExtraImageContent = make(map[string]string, len(extraImageContent))
+ }
+ // merge in the scanner-generated content
+ for k, v := range extraImageContent {
+ if _, set := options.ExtraImageContent[k]; !set {
+ options.ExtraImageContent[k] = v
+ }
+ }
+ }
+ }
+
+ // Build an image reference from which we can copy the finished image.
+ src, err = b.makeContainerImageRef(options)
+ if err != nil {
+ return imgID, nil, "", fmt.Errorf("computing layer digests and building metadata for container %q: %w", b.ContainerID, err)
+ }
+ // In case we're using caching, decide how to handle compression for a cache.
+ // If we're using blob caching, set it up for the source.
+ maybeCachedSrc := src
+ maybeCachedDest := dest
+ if options.BlobDirectory != "" {
+ compress := types.PreserveOriginal
+ if options.Compression != archive.Uncompressed {
+ compress = types.Compress
+ }
+ cache, err := blobcache.NewBlobCache(src, options.BlobDirectory, compress)
+ if err != nil {
+ return imgID, nil, "", fmt.Errorf("wrapping image reference %q in blob cache at %q: %w", transports.ImageName(src), options.BlobDirectory, err)
+ }
+ maybeCachedSrc = cache
+ cache, err = blobcache.NewBlobCache(dest, options.BlobDirectory, compress)
+ if err != nil {
+ return imgID, nil, "", fmt.Errorf("wrapping image reference %q in blob cache at %q: %w", transports.ImageName(dest), options.BlobDirectory, err)
+ }
+ maybeCachedDest = cache
+ }
+ // "Copy" our image to where it needs to be.
+ switch options.Compression {
+ case archive.Uncompressed:
+ systemContext.OCIAcceptUncompressedLayers = true
+ case archive.Gzip:
+ systemContext.DirForceCompress = true
+ }
+
+ if systemContext.ArchitectureChoice != b.Architecture() {
+ systemContext.ArchitectureChoice = b.Architecture()
+ }
+ if systemContext.OSChoice != b.OS() {
+ systemContext.OSChoice = b.OS()
+ }
+
+ var manifestBytes []byte
+ if manifestBytes, err = retryCopyImage(ctx, policyContext, maybeCachedDest, maybeCachedSrc, dest, getCopyOptions(b.store, options.ReportWriter, nil, systemContext, "", false, options.SignBy, options.OciEncryptLayers, options.OciEncryptConfig, nil, destinationTimestamp), options.MaxRetries, options.RetryDelay); err != nil {
+ return imgID, nil, "", fmt.Errorf("copying layers and metadata for container %q: %w", b.ContainerID, err)
+ }
+ // If we've got more names to attach, and we know how to do that for
+ // the transport that we're writing the new image to, add them now.
+ if len(options.AdditionalTags) > 0 {
+ switch dest.Transport().Name() {
+ case is.Transport.Name():
+ _, img, err := is.ResolveReference(dest)
+ if err != nil {
+ return imgID, nil, "", fmt.Errorf("locating just-written image %q: %w", transports.ImageName(dest), err)
+ }
+ if err = util.AddImageNames(b.store, "", systemContext, img, options.AdditionalTags); err != nil {
+ return imgID, nil, "", fmt.Errorf("setting image names to %v: %w", append(img.Names, options.AdditionalTags...), err)
+ }
+ logrus.Debugf("assigned names %v to image %q", img.Names, img.ID)
+ default:
+ logrus.Warnf("don't know how to add tags to images stored in %q transport", dest.Transport().Name())
+ }
+ }
+
+ if dest.Transport().Name() == is.Transport.Name() {
+ dest2, img, err := is.ResolveReference(dest)
+ if err != nil {
+ return imgID, nil, "", fmt.Errorf("locating image %q in local storage: %w", transports.ImageName(dest), err)
+ }
+ dest = dest2
+ imgID = img.ID
+ toPruneNames := make([]string, 0, len(img.Names))
+ for _, name := range img.Names {
+ if nameToRemove != "" && strings.Contains(name, nameToRemove) {
+ toPruneNames = append(toPruneNames, name)
+ }
+ }
+ if len(toPruneNames) > 0 {
+ if err = b.store.RemoveNames(imgID, toPruneNames); err != nil {
+ return imgID, nil, "", fmt.Errorf("failed to remove temporary name from image %q: %w", imgID, err)
+ }
+ logrus.Debugf("removing %v from assigned names to image %q", nameToRemove, img.ID)
+ }
+ if options.IIDFile != "" {
+ if err = os.WriteFile(options.IIDFile, []byte("sha256:"+img.ID), 0o644); err != nil {
+ return imgID, nil, "", err
+ }
+ }
+ }
+ // If we're supposed to store SBOM or PURL information in local files, write them now.
+ for filename, content := range extraLocalContent {
+ err := func() error {
+ output, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644)
+ if err != nil {
+ return err
+ }
+ defer output.Close()
+ input, err := os.Open(content)
+ if err != nil {
+ return err
+ }
+ defer input.Close()
+ if _, err := io.Copy(output, input); err != nil {
+ return fmt.Errorf("copying from %q to %q: %w", content, filename, err)
+ }
+ return nil
+ }()
+ if err != nil {
+ return imgID, nil, "", err
+ }
+ }
+
+ // Calculate the as-written digest of the image's manifest and build the digested
+ // reference for the image.
+ manifestDigest, err := manifest.Digest(manifestBytes)
+ if err != nil {
+ return imgID, nil, "", fmt.Errorf("computing digest of manifest of new image %q: %w", transports.ImageName(dest), err)
+ }
+ if imgID == "" {
+ parsedManifest, err := manifest.FromBlob(manifestBytes, manifest.GuessMIMEType(manifestBytes))
+ if err != nil {
+ return imgID, nil, "", fmt.Errorf("parsing written manifest to determine the image's ID: %w", err)
+ }
+ configInfo := parsedManifest.ConfigInfo()
+ if configInfo.Size > 2 && configInfo.Digest.Validate() == nil { // don't be returning a digest of "" or "{}"
+ imgID = configInfo.Digest.Encoded()
+ }
+ }
+
+ var ref reference.Canonical
+ if name := dest.DockerReference(); name != nil {
+ ref, err = reference.WithDigest(name, manifestDigest)
+ if err != nil {
+ logrus.Warnf("error generating canonical reference with name %q and digest %s: %v", name, manifestDigest.String(), err)
+ }
+ }
+
+ if options.Manifest != "" {
+ manifestID, err := b.addManifest(ctx, options.Manifest, imgID)
+ if err != nil {
+ return imgID, nil, "", err
+ }
+ logrus.Debugf("added imgID %s to manifestID %s", imgID, manifestID)
+ }
+ return imgID, ref, manifestDigest, nil
+}
diff --git a/vendor/github.com/containers/buildah/common.go b/vendor/github.com/containers/buildah/common.go
new file mode 100644
index 00000000..fe734cfc
--- /dev/null
+++ b/vendor/github.com/containers/buildah/common.go
@@ -0,0 +1,100 @@
+package buildah
+
+import (
+ "context"
+ "errors"
+ "io"
+ "path/filepath"
+ "time"
+
+ "github.com/containers/buildah/define"
+ encconfig "github.com/containers/ocicrypt/config"
+ "go.podman.io/common/pkg/retry"
+ cp "go.podman.io/image/v5/copy"
+ "go.podman.io/image/v5/docker"
+ "go.podman.io/image/v5/signature"
+ is "go.podman.io/image/v5/storage"
+ "go.podman.io/image/v5/types"
+ "go.podman.io/storage"
+ "go.podman.io/storage/pkg/fileutils"
+ "go.podman.io/storage/pkg/unshare"
+)
+
+const (
+ // OCI used to define the "oci" image format
+ OCI = define.OCI
+ // DOCKER used to define the "docker" image format
+ DOCKER = define.DOCKER
+)
+
+func getCopyOptions(store storage.Store, reportWriter io.Writer, sourceSystemContext *types.SystemContext, destinationSystemContext *types.SystemContext, manifestType string, removeSignatures bool, addSigner string, ociEncryptLayers *[]int, ociEncryptConfig *encconfig.EncryptConfig, ociDecryptConfig *encconfig.DecryptConfig, destinationTimestamp *time.Time) *cp.Options {
+ sourceCtx := getSystemContext(store, nil, "")
+ if sourceSystemContext != nil {
+ *sourceCtx = *sourceSystemContext
+ }
+
+ destinationCtx := getSystemContext(store, nil, "")
+ if destinationSystemContext != nil {
+ *destinationCtx = *destinationSystemContext
+ }
+ return &cp.Options{
+ ReportWriter: reportWriter,
+ SourceCtx: sourceCtx,
+ DestinationCtx: destinationCtx,
+ ForceManifestMIMEType: manifestType,
+ RemoveSignatures: removeSignatures,
+ SignBy: addSigner,
+ OciEncryptConfig: ociEncryptConfig,
+ OciDecryptConfig: ociDecryptConfig,
+ OciEncryptLayers: ociEncryptLayers,
+ DestinationTimestamp: destinationTimestamp,
+ }
+}
+
+func getSystemContext(store storage.Store, defaults *types.SystemContext, signaturePolicyPath string) *types.SystemContext {
+ sc := &types.SystemContext{}
+ if defaults != nil {
+ *sc = *defaults
+ }
+ if signaturePolicyPath != "" {
+ sc.SignaturePolicyPath = signaturePolicyPath
+ }
+ if store != nil {
+ if sc.SystemRegistriesConfPath == "" && unshare.IsRootless() {
+ userRegistriesFile := filepath.Join(store.GraphRoot(), "registries.conf")
+ if err := fileutils.Exists(userRegistriesFile); err == nil {
+ sc.SystemRegistriesConfPath = userRegistriesFile
+ }
+ }
+ }
+ return sc
+}
+
+func retryCopyImage(ctx context.Context, policyContext *signature.PolicyContext, maybeWrappedDest, maybeWrappedSrc, directDest types.ImageReference, copyOptions *cp.Options, maxRetries int, retryDelay time.Duration) ([]byte, error) {
+ return retryCopyImageWithOptions(ctx, policyContext, maybeWrappedDest, maybeWrappedSrc, directDest, copyOptions, maxRetries, retryDelay, true)
+}
+
+func retryCopyImageWithOptions(ctx context.Context, policyContext *signature.PolicyContext, maybeWrappedDest, maybeWrappedSrc, directDest types.ImageReference, copyOptions *cp.Options, maxRetries int, retryDelay time.Duration, retryOnLayerUnknown bool) ([]byte, error) {
+ var (
+ manifestBytes []byte
+ err error
+ )
+ err = retry.IfNecessary(ctx, func() error {
+ manifestBytes, err = cp.Image(ctx, policyContext, maybeWrappedDest, maybeWrappedSrc, copyOptions)
+ return err
+ }, &retry.RetryOptions{MaxRetry: maxRetries, Delay: retryDelay, IsErrorRetryable: func(err error) bool {
+ if retryOnLayerUnknown && directDest.Transport().Name() == is.Transport.Name() && errors.Is(err, storage.ErrLayerUnknown) {
+ // we were trying to reuse a layer that belonged to an
+ // image that was deleted at just the right (worst
+ // possible) time? yeah, try again
+ return true
+ }
+ if directDest.Transport().Name() != docker.Transport.Name() {
+ // if we're not talking to a registry, then nah
+ return false
+ }
+ // hand it off to the default should-this-be-retried logic
+ return retry.IsErrorRetryable(err)
+ }})
+ return manifestBytes, err
+}
diff --git a/vendor/github.com/containers/buildah/config.go b/vendor/github.com/containers/buildah/config.go
new file mode 100644
index 00000000..a5381972
--- /dev/null
+++ b/vendor/github.com/containers/buildah/config.go
@@ -0,0 +1,821 @@
+package buildah
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "maps"
+ "os"
+ "slices"
+ "strings"
+ "time"
+
+ "github.com/containerd/platforms"
+ "github.com/containers/buildah/define"
+ "github.com/containers/buildah/docker"
+ internalUtil "github.com/containers/buildah/internal/util"
+ ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/sirupsen/logrus"
+ "go.podman.io/image/v5/manifest"
+ "go.podman.io/image/v5/pkg/compression"
+ "go.podman.io/image/v5/transports"
+ "go.podman.io/image/v5/types"
+ "go.podman.io/storage/pkg/stringid"
+)
+
+// unmarshalConvertedConfig obtains the config blob of img valid for the wantedManifestMIMEType format
+// (either as it exists, or converting the image if necessary), and unmarshals it into dest.
+// NOTE: The MIME type is of the _manifest_, not of the _config_ that is returned.
+func unmarshalConvertedConfig(ctx context.Context, dest any, img types.Image, wantedManifestMIMEType string) error {
+ _, actualManifestMIMEType, err := img.Manifest(ctx)
+ if err != nil {
+ return fmt.Errorf("getting manifest MIME type for %q: %w", transports.ImageName(img.Reference()), err)
+ }
+ if wantedManifestMIMEType != actualManifestMIMEType {
+ layerInfos := img.LayerInfos()
+ for i := range layerInfos { // force the "compression" to gzip, which is supported by all of the formats we care about
+ layerInfos[i].CompressionOperation = types.Compress
+ layerInfos[i].CompressionAlgorithm = &compression.Gzip
+ }
+ updatedImg, err := img.UpdatedImage(ctx, types.ManifestUpdateOptions{
+ LayerInfos: layerInfos,
+ })
+ if err != nil {
+ return fmt.Errorf("resetting recorded compression for %q: %w", transports.ImageName(img.Reference()), err)
+ }
+ secondUpdatedImg, err := updatedImg.UpdatedImage(ctx, types.ManifestUpdateOptions{
+ ManifestMIMEType: wantedManifestMIMEType,
+ })
+ if err != nil {
+ return fmt.Errorf("converting image %q from %q to %q: %w", transports.ImageName(img.Reference()), actualManifestMIMEType, wantedManifestMIMEType, err)
+ }
+ img = secondUpdatedImg
+ }
+ config, err := img.ConfigBlob(ctx)
+ if err != nil {
+ return fmt.Errorf("reading %s config from %q: %w", wantedManifestMIMEType, transports.ImageName(img.Reference()), err)
+ }
+ if err := json.Unmarshal(config, dest); err != nil {
+ return fmt.Errorf("parsing %s configuration %q from %q: %w", wantedManifestMIMEType, string(config), transports.ImageName(img.Reference()), err)
+ }
+ return nil
+}
+
+func (b *Builder) initConfig(ctx context.Context, sys *types.SystemContext, img types.Image, options *BuilderOptions) error {
+ if img != nil { // A pre-existing image, as opposed to a "FROM scratch" new one.
+ rawManifest, manifestMIMEType, err := img.Manifest(ctx)
+ if err != nil {
+ return fmt.Errorf("reading image manifest for %q: %w", transports.ImageName(img.Reference()), err)
+ }
+ rawConfig, err := img.ConfigBlob(ctx)
+ if err != nil {
+ return fmt.Errorf("reading image configuration for %q: %w", transports.ImageName(img.Reference()), err)
+ }
+ b.Manifest = rawManifest
+ b.Config = rawConfig
+
+ dimage := docker.V2Image{}
+ if err := unmarshalConvertedConfig(ctx, &dimage, img, manifest.DockerV2Schema2MediaType); err != nil {
+ return err
+ }
+ b.Docker = dimage
+
+ oimage := ociv1.Image{}
+ if err := unmarshalConvertedConfig(ctx, &oimage, img, ociv1.MediaTypeImageManifest); err != nil {
+ return err
+ }
+ b.OCIv1 = oimage
+
+ if manifestMIMEType == ociv1.MediaTypeImageManifest {
+ // Attempt to recover format-specific data from the manifest.
+ v1Manifest := ociv1.Manifest{}
+ if err := json.Unmarshal(b.Manifest, &v1Manifest); err != nil {
+ return fmt.Errorf("parsing OCI manifest %q: %w", string(b.Manifest), err)
+ }
+ if len(v1Manifest.Annotations) > 0 {
+ if b.ImageAnnotations == nil {
+ b.ImageAnnotations = make(map[string]string, len(v1Manifest.Annotations))
+ }
+ maps.Copy(b.ImageAnnotations, v1Manifest.Annotations)
+ }
+ }
+ } else {
+ if options == nil || options.CompatScratchConfig != types.OptionalBoolTrue {
+ b.Docker = docker.V2Image{
+ V1Image: docker.V1Image{
+ Config: &docker.Config{
+ WorkingDir: "/",
+ },
+ },
+ }
+ b.OCIv1 = ociv1.Image{
+ Config: ociv1.ImageConfig{
+ WorkingDir: "/",
+ },
+ }
+ }
+ }
+
+ b.setupLogger()
+ b.fixupConfig(sys)
+ return nil
+}
+
+func (b *Builder) fixupConfig(sys *types.SystemContext) {
+ if b.Docker.Config != nil {
+ // Prefer image-level settings over those from the container it was built from.
+ b.Docker.ContainerConfig = *b.Docker.Config
+ }
+ b.Docker.Config = &b.Docker.ContainerConfig
+ b.Docker.DockerVersion = ""
+ now := time.Now().UTC()
+ if b.Docker.Created.IsZero() {
+ b.Docker.Created = now
+ }
+ if b.OCIv1.Created == nil || b.OCIv1.Created.IsZero() {
+ b.OCIv1.Created = &now
+ }
+ currentPlatformSpecification := platforms.DefaultSpec()
+ if b.OS() == "" {
+ if sys != nil && sys.OSChoice != "" {
+ b.SetOS(sys.OSChoice)
+ } else {
+ b.SetOS(currentPlatformSpecification.OS)
+ }
+ }
+ if b.Architecture() == "" {
+ if sys != nil && sys.ArchitectureChoice != "" {
+ b.SetArchitecture(sys.ArchitectureChoice)
+ b.SetVariant(sys.VariantChoice)
+ } else {
+ b.SetArchitecture(currentPlatformSpecification.Architecture)
+ b.SetVariant(currentPlatformSpecification.Variant)
+ }
+ // in case the arch string we started with was shorthand for a known arch+variant pair, normalize it
+ ps := internalUtil.NormalizePlatform(ociv1.Platform{OS: b.OS(), Architecture: b.Architecture(), Variant: b.Variant()})
+ b.SetArchitecture(ps.Architecture)
+ b.SetVariant(ps.Variant)
+ }
+ if b.Format == define.Dockerv2ImageManifest && b.Hostname() == "" {
+ b.SetHostname(stringid.TruncateID(stringid.GenerateRandomID()))
+ }
+}
+
+func (b *Builder) setupLogger() {
+ if b.Logger == nil {
+ b.Logger = logrus.New()
+ b.Logger.SetOutput(os.Stderr)
+ b.Logger.SetLevel(logrus.GetLevel())
+ }
+}
+
+// Annotations returns a set of key-value pairs from the image's manifest.
+func (b *Builder) Annotations() map[string]string {
+ return maps.Clone(b.ImageAnnotations)
+}
+
+// SetAnnotation adds or overwrites a key's value from the image's manifest.
+// Note: this setting is not present in the Docker v2 image format, so it is
+// discarded when writing images using Docker v2 formats.
+func (b *Builder) SetAnnotation(key, value string) {
+ if b.ImageAnnotations == nil {
+ b.ImageAnnotations = map[string]string{}
+ }
+ b.ImageAnnotations[key] = value
+}
+
+// UnsetAnnotation removes a key and its value from the image's manifest, if
+// it's present.
+func (b *Builder) UnsetAnnotation(key string) {
+ delete(b.ImageAnnotations, key)
+}
+
+// ClearAnnotations removes all keys and their values from the image's
+// manifest.
+func (b *Builder) ClearAnnotations() {
+ b.ImageAnnotations = nil
+}
+
+// CreatedBy returns a description of how this image was built.
+func (b *Builder) CreatedBy() string {
+ return b.ImageCreatedBy
+}
+
+// SetCreatedBy sets the description of how this image was built.
+func (b *Builder) SetCreatedBy(how string) {
+ b.ImageCreatedBy = how
+}
+
+// OS returns a name of the OS on which the container, or a container built
+// using an image built from this container, is intended to be run.
+func (b *Builder) OS() string {
+ return b.OCIv1.OS
+}
+
+// SetOS sets the name of the OS on which the container, or a container built
+// using an image built from this container, is intended to be run.
+func (b *Builder) SetOS(os string) {
+ b.OCIv1.OS = os
+ b.Docker.OS = os
+}
+
+// OSVersion returns a version of the OS on which the container, or a container
+// built using an image built from this container, is intended to be run.
+func (b *Builder) OSVersion() string {
+ return b.OCIv1.OSVersion
+}
+
+// SetOSVersion sets the version of the OS on which the container, or a
+// container built using an image built from this container, is intended to be
+// run.
+func (b *Builder) SetOSVersion(version string) {
+ b.OCIv1.OSVersion = version
+ b.Docker.OSVersion = version
+}
+
+// OSFeatures returns a list of OS features which the container, or a container
+// built using an image built from this container, depends on the OS supplying.
+func (b *Builder) OSFeatures() []string {
+ return slices.Clone(b.OCIv1.OSFeatures)
+}
+
+// SetOSFeature adds a feature of the OS which the container, or a container
+// built using an image built from this container, depends on the OS supplying.
+func (b *Builder) SetOSFeature(feature string) {
+ if !slices.Contains(b.OCIv1.OSFeatures, feature) {
+ b.OCIv1.OSFeatures = append(b.OCIv1.OSFeatures, feature)
+ }
+ if !slices.Contains(b.Docker.OSFeatures, feature) {
+ b.Docker.OSFeatures = append(b.Docker.OSFeatures, feature)
+ }
+}
+
+// UnsetOSFeature removes a feature of the OS which the container, or a
+// container built using an image built from this container, depends on the OS
+// supplying.
+func (b *Builder) UnsetOSFeature(feature string) {
+ if slices.Contains(b.OCIv1.OSFeatures, feature) {
+ features := make([]string, 0, len(b.OCIv1.OSFeatures))
+ for _, f := range b.OCIv1.OSFeatures {
+ if f != feature {
+ features = append(features, f)
+ }
+ }
+ b.OCIv1.OSFeatures = features
+ }
+ if slices.Contains(b.Docker.OSFeatures, feature) {
+ features := make([]string, 0, len(b.Docker.OSFeatures))
+ for _, f := range b.Docker.OSFeatures {
+ if f != feature {
+ features = append(features, f)
+ }
+ }
+ b.Docker.OSFeatures = features
+ }
+}
+
+// ClearOSFeatures clears the list of features of the OS which the container,
+// or a container built using an image built from this container, depends on
+// the OS supplying.
+func (b *Builder) ClearOSFeatures() {
+ b.OCIv1.OSFeatures = []string{}
+ b.Docker.OSFeatures = []string{}
+}
+
+// Architecture returns a name of the architecture on which the container, or a
+// container built using an image built from this container, is intended to be
+// run.
+func (b *Builder) Architecture() string {
+ return b.OCIv1.Architecture
+}
+
+// SetArchitecture sets the name of the architecture on which the container, or
+// a container built using an image built from this container, is intended to
+// be run.
+func (b *Builder) SetArchitecture(arch string) {
+ b.OCIv1.Architecture = arch
+ b.Docker.Architecture = arch
+}
+
+// Variant returns a name of the architecture variant on which the container,
+// or a container built using an image built from this container, is intended
+// to be run.
+func (b *Builder) Variant() string {
+ return b.OCIv1.Variant
+}
+
+// SetVariant sets the name of the architecture variant on which the container,
+// or a container built using an image built from this container, is intended
+// to be run.
+func (b *Builder) SetVariant(variant string) {
+ b.Docker.Variant = variant
+ b.OCIv1.Variant = variant
+}
+
+// Maintainer returns contact information for the person who built the image.
+func (b *Builder) Maintainer() string {
+ return b.OCIv1.Author
+}
+
+// SetMaintainer sets contact information for the person who built the image.
+func (b *Builder) SetMaintainer(who string) {
+ b.OCIv1.Author = who
+ b.Docker.Author = who
+}
+
+// User returns information about the user as whom the container, or a
+// container built using an image built from this container, should be run.
+func (b *Builder) User() string {
+ return b.OCIv1.Config.User
+}
+
+// SetUser sets information about the user as whom the container, or a
+// container built using an image built from this container, should be run.
+// Acceptable forms are a user name or ID, optionally followed by a colon and a
+// group name or ID.
+func (b *Builder) SetUser(spec string) {
+ b.OCIv1.Config.User = spec
+ b.Docker.Config.User = spec
+}
+
+// OnBuild returns the OnBuild value from the container.
+func (b *Builder) OnBuild() []string {
+ return slices.Clone(b.Docker.Config.OnBuild)
+}
+
+// ClearOnBuild removes all values from the OnBuild structure
+func (b *Builder) ClearOnBuild() {
+ b.Docker.Config.OnBuild = []string{}
+}
+
+// SetOnBuild sets a trigger instruction to be executed when the image is used
+// as the base of another image.
+// Note: this setting is not present in the OCIv1 image format, so it is
+// discarded when writing images using OCIv1 formats.
+func (b *Builder) SetOnBuild(onBuild string) {
+ if onBuild != "" && b.Format != define.Dockerv2ImageManifest {
+ b.Logger.Warnf("ONBUILD is not supported for OCI image format, %s will be ignored. Must use `docker` format", onBuild)
+ }
+ b.Docker.Config.OnBuild = append(b.Docker.Config.OnBuild, onBuild)
+}
+
+// WorkDir returns the default working directory for running commands in the
+// container, or in a container built using an image built from this container.
+func (b *Builder) WorkDir() string {
+ return b.OCIv1.Config.WorkingDir
+}
+
+// SetWorkDir sets the location of the default working directory for running
+// commands in the container, or in a container built using an image built from
+// this container.
+func (b *Builder) SetWorkDir(there string) {
+ b.OCIv1.Config.WorkingDir = there
+ b.Docker.Config.WorkingDir = there
+}
+
+// Shell returns the default shell for running commands in the
+// container, or in a container built using an image built from this container.
+func (b *Builder) Shell() []string {
+ return slices.Clone(b.Docker.Config.Shell)
+}
+
+// SetShell sets the default shell for running
+// commands in the container, or in a container built using an image built from
+// this container.
+// Note: this setting is not present in the OCIv1 image format, so it is
+// discarded when writing images using OCIv1 formats.
+func (b *Builder) SetShell(shell []string) {
+ if len(shell) > 0 && b.Format != define.Dockerv2ImageManifest {
+ b.Logger.Warnf("SHELL is not supported for OCI image format, %s will be ignored. Must use `docker` format", shell)
+ }
+
+ b.Docker.Config.Shell = slices.Clone(shell)
+}
+
+// Env returns a list of key-value pairs to be set when running commands in the
+// container, or in a container built using an image built from this container.
+func (b *Builder) Env() []string {
+ return slices.Clone(b.OCIv1.Config.Env)
+}
+
+// SetEnv adds or overwrites a value to the set of environment strings which
+// should be set when running commands in the container, or in a container
+// built using an image built from this container.
+func (b *Builder) SetEnv(k string, v string) {
+ reset := func(s *[]string) {
+ n := []string{}
+ for i := range *s {
+ if !strings.HasPrefix((*s)[i], k+"=") {
+ n = append(n, (*s)[i])
+ }
+ }
+ n = append(n, k+"="+v)
+ *s = n
+ }
+ reset(&b.OCIv1.Config.Env)
+ reset(&b.Docker.Config.Env)
+}
+
+// UnsetEnv removes a value from the set of environment strings which should be
+// set when running commands in this container, or in a container built using
+// an image built from this container.
+func (b *Builder) UnsetEnv(k string) {
+ unset := func(s *[]string) {
+ n := []string{}
+ for i := range *s {
+ if !strings.HasPrefix((*s)[i], k+"=") {
+ n = append(n, (*s)[i])
+ }
+ }
+ *s = n
+ }
+ unset(&b.OCIv1.Config.Env)
+ unset(&b.Docker.Config.Env)
+}
+
+// ClearEnv removes all values from the set of environment strings which should
+// be set when running commands in this container, or in a container built
+// using an image built from this container.
+func (b *Builder) ClearEnv() {
+ b.OCIv1.Config.Env = []string{}
+ b.Docker.Config.Env = []string{}
+}
+
+// Cmd returns the default command, or command parameters if an Entrypoint is
+// set, to use when running a container built from an image built from this
+// container.
+func (b *Builder) Cmd() []string {
+ return slices.Clone(b.OCIv1.Config.Cmd)
+}
+
+// SetCmd sets the default command, or command parameters if an Entrypoint is
+// set, to use when running a container built from an image built from this
+// container.
+func (b *Builder) SetCmd(cmd []string) {
+ b.OCIv1.Config.Cmd = slices.Clone(cmd)
+ b.Docker.Config.Cmd = slices.Clone(cmd)
+}
+
+// Entrypoint returns the command to be run for containers built from images
+// built from this container.
+func (b *Builder) Entrypoint() []string {
+ if len(b.OCIv1.Config.Entrypoint) > 0 {
+ return slices.Clone(b.OCIv1.Config.Entrypoint)
+ }
+ return nil
+}
+
+// SetEntrypoint sets the command to be run for in containers built from images
+// built from this container.
+func (b *Builder) SetEntrypoint(ep []string) {
+ b.OCIv1.Config.Entrypoint = slices.Clone(ep)
+ b.Docker.Config.Entrypoint = slices.Clone(ep)
+}
+
+// Labels returns a set of key-value pairs from the image's runtime
+// configuration.
+func (b *Builder) Labels() map[string]string {
+ return maps.Clone(b.OCIv1.Config.Labels)
+}
+
+// SetLabel adds or overwrites a key's value from the image's runtime
+// configuration.
+func (b *Builder) SetLabel(k string, v string) {
+ if b.OCIv1.Config.Labels == nil {
+ b.OCIv1.Config.Labels = map[string]string{}
+ }
+ b.OCIv1.Config.Labels[k] = v
+ if b.Docker.Config.Labels == nil {
+ b.Docker.Config.Labels = map[string]string{}
+ }
+ b.Docker.Config.Labels[k] = v
+}
+
+// UnsetLabel removes a key and its value from the image's runtime
+// configuration, if it's present.
+func (b *Builder) UnsetLabel(k string) {
+ delete(b.OCIv1.Config.Labels, k)
+ delete(b.Docker.Config.Labels, k)
+}
+
+// ClearLabels removes all keys and their values from the image's runtime
+// configuration.
+func (b *Builder) ClearLabels() {
+ b.OCIv1.Config.Labels = map[string]string{}
+ b.Docker.Config.Labels = map[string]string{}
+}
+
+// Ports returns the set of ports which should be exposed when a container
+// based on an image built from this container is run.
+func (b *Builder) Ports() []string {
+ p := []string{}
+ for k := range b.OCIv1.Config.ExposedPorts {
+ p = append(p, k)
+ }
+ return p
+}
+
+// SetPort adds or overwrites an exported port in the set of ports which should
+// be exposed when a container based on an image built from this container is
+// run.
+func (b *Builder) SetPort(p string) {
+ if b.OCIv1.Config.ExposedPorts == nil {
+ b.OCIv1.Config.ExposedPorts = map[string]struct{}{}
+ }
+ b.OCIv1.Config.ExposedPorts[p] = struct{}{}
+ if b.Docker.Config.ExposedPorts == nil {
+ b.Docker.Config.ExposedPorts = make(docker.PortSet)
+ }
+ b.Docker.Config.ExposedPorts[docker.Port(p)] = struct{}{}
+}
+
+// UnsetPort removes an exposed port from the set of ports which should be
+// exposed when a container based on an image built from this container is run.
+func (b *Builder) UnsetPort(p string) {
+ delete(b.OCIv1.Config.ExposedPorts, p)
+ delete(b.Docker.Config.ExposedPorts, docker.Port(p))
+}
+
+// ClearPorts empties the set of ports which should be exposed when a container
+// based on an image built from this container is run.
+func (b *Builder) ClearPorts() {
+ b.OCIv1.Config.ExposedPorts = map[string]struct{}{}
+ b.Docker.Config.ExposedPorts = docker.PortSet{}
+}
+
+// Volumes returns a list of filesystem locations which should be mounted from
+// outside of the container when a container built from an image built from
+// this container is run.
+func (b *Builder) Volumes() []string {
+ v := []string{}
+ for k := range b.OCIv1.Config.Volumes {
+ v = append(v, k)
+ }
+ if len(v) > 0 {
+ return v
+ }
+ return nil
+}
+
+// CheckVolume returns True if the location exists in the image's list of locations
+// which should be mounted from outside of the container when a container
+// based on an image built from this container is run
+
+func (b *Builder) CheckVolume(v string) bool {
+ _, OCIv1Volume := b.OCIv1.Config.Volumes[v]
+ _, DockerVolume := b.Docker.Config.Volumes[v]
+ return OCIv1Volume || DockerVolume
+}
+
+// AddVolume adds a location to the image's list of locations which should be
+// mounted from outside of the container when a container based on an image
+// built from this container is run.
+func (b *Builder) AddVolume(v string) {
+ if b.OCIv1.Config.Volumes == nil {
+ b.OCIv1.Config.Volumes = map[string]struct{}{}
+ }
+ b.OCIv1.Config.Volumes[v] = struct{}{}
+ if b.Docker.Config.Volumes == nil {
+ b.Docker.Config.Volumes = map[string]struct{}{}
+ }
+ b.Docker.Config.Volumes[v] = struct{}{}
+}
+
+// RemoveVolume removes a location from the list of locations which should be
+// mounted from outside of the container when a container based on an image
+// built from this container is run.
+func (b *Builder) RemoveVolume(v string) {
+ delete(b.OCIv1.Config.Volumes, v)
+ delete(b.Docker.Config.Volumes, v)
+}
+
+// ClearVolumes removes all locations from the image's list of locations which
+// should be mounted from outside of the container when a container based on an
+// image built from this container is run.
+func (b *Builder) ClearVolumes() {
+ b.OCIv1.Config.Volumes = map[string]struct{}{}
+ b.Docker.Config.Volumes = map[string]struct{}{}
+}
+
+// Hostname returns the hostname which will be set in the container and in
+// containers built using images built from the container.
+func (b *Builder) Hostname() string {
+ return b.Docker.Config.Hostname
+}
+
+// SetHostname sets the hostname which will be set in the container and in
+// containers built using images built from the container.
+// Note: this setting is not present in the OCIv1 image format, so it is
+// discarded when writing images using OCIv1 formats.
+func (b *Builder) SetHostname(name string) {
+ b.Docker.Config.Hostname = name
+}
+
+// Domainname returns the domainname which will be set in the container and in
+// containers built using images built from the container.
+func (b *Builder) Domainname() string {
+ return b.Docker.Config.Domainname
+}
+
+// SetDomainname sets the domainname which will be set in the container and in
+// containers built using images built from the container.
+// Note: this setting is not present in the OCIv1 image format, so it is
+// discarded when writing images using OCIv1 formats.
+func (b *Builder) SetDomainname(name string) {
+ if name != "" && b.Format != define.Dockerv2ImageManifest {
+ b.Logger.Warnf("DOMAINNAME is not supported for OCI image format, domainname %s will be ignored. Must use `docker` format", name)
+ }
+ b.Docker.Config.Domainname = name
+}
+
+// SetDefaultMountsFilePath sets the mounts file path for testing purposes
+func (b *Builder) SetDefaultMountsFilePath(path string) {
+ b.DefaultMountsFilePath = path
+}
+
+// Comment returns the comment which will be set in the container and in
+// containers built using images built from the container
+func (b *Builder) Comment() string {
+ return b.Docker.Comment
+}
+
+// SetComment sets the comment which will be set in the container and in
+// containers built using images built from the container.
+// Note: this setting is not present in the OCIv1 image format, so it is
+// discarded when writing images using OCIv1 formats.
+func (b *Builder) SetComment(comment string) {
+ if comment != "" && b.Format != define.Dockerv2ImageManifest {
+ logrus.Warnf("COMMENT is not supported for OCI image format, comment %s will be ignored. Must use `docker` format", comment)
+ }
+ b.Docker.Comment = comment
+}
+
+// HistoryComment returns the comment which will be used in the history item
+// which will describe the latest layer when we commit an image.
+func (b *Builder) HistoryComment() string {
+ return b.ImageHistoryComment
+}
+
+// SetHistoryComment sets the comment which will be used in the history item
+// which will describe the latest layer when we commit an image.
+func (b *Builder) SetHistoryComment(comment string) {
+ b.ImageHistoryComment = comment
+}
+
+// StopSignal returns the signal which will be set in the container and in
+// containers built using images built from the container
+func (b *Builder) StopSignal() string {
+ return b.Docker.Config.StopSignal
+}
+
+// SetStopSignal sets the signal which will be set in the container and in
+// containers built using images built from the container.
+func (b *Builder) SetStopSignal(stopSignal string) {
+ b.OCIv1.Config.StopSignal = stopSignal
+ b.Docker.Config.StopSignal = stopSignal
+}
+
+// Healthcheck returns information that recommends how a container engine
+// should check if a running container is "healthy".
+func (b *Builder) Healthcheck() *docker.HealthConfig {
+ if b.Docker.Config.Healthcheck == nil {
+ return nil
+ }
+ return &docker.HealthConfig{
+ Test: slices.Clone(b.Docker.Config.Healthcheck.Test),
+ Interval: b.Docker.Config.Healthcheck.Interval,
+ Timeout: b.Docker.Config.Healthcheck.Timeout,
+ StartPeriod: b.Docker.Config.Healthcheck.StartPeriod,
+ StartInterval: b.Docker.Config.Healthcheck.StartInterval,
+ Retries: b.Docker.Config.Healthcheck.Retries,
+ }
+}
+
+// SetHealthcheck sets recommended commands to run in order to verify that a
+// running container based on this image is "healthy", along with information
+// specifying how often that test should be run, and how many times the test
+// should fail before the container should be considered unhealthy.
+// Note: this setting is not present in the OCIv1 image format, so it is
+// discarded when writing images using OCIv1 formats.
+func (b *Builder) SetHealthcheck(config *docker.HealthConfig) {
+ b.Docker.Config.Healthcheck = nil
+ if config != nil {
+ if b.Format != define.Dockerv2ImageManifest {
+ b.Logger.Warnf("HEALTHCHECK is not supported for OCI image format and will be ignored. Must use `docker` format")
+ }
+ b.Docker.Config.Healthcheck = &docker.HealthConfig{
+ Test: slices.Clone(config.Test),
+ Interval: config.Interval,
+ Timeout: config.Timeout,
+ StartPeriod: config.StartPeriod,
+ StartInterval: config.StartInterval,
+ Retries: config.Retries,
+ }
+ }
+}
+
+// AddPrependedEmptyLayer adds an item to the history that we'll create when
+// committing the image, after any history we inherit from the base image, but
+// before the history item that we'll use to describe the new layer that we're
+// adding.
+func (b *Builder) AddPrependedEmptyLayer(created *time.Time, createdBy, author, comment string) {
+ if created != nil {
+ copiedTimestamp := *created
+ created = &copiedTimestamp
+ }
+ b.PrependedEmptyLayers = append(b.PrependedEmptyLayers, ociv1.History{
+ Created: created,
+ CreatedBy: createdBy,
+ Author: author,
+ Comment: comment,
+ EmptyLayer: true,
+ })
+}
+
+// ClearPrependedEmptyLayers clears the list of history entries that we'll add
+// to the committed image before the entry for the layer that we're adding.
+func (b *Builder) ClearPrependedEmptyLayers() {
+ b.PrependedEmptyLayers = nil
+}
+
+// AddAppendedEmptyLayer adds an item to the history that we'll create when
+// committing the image, after the history item that we'll use to describe the
+// new layer that we're adding.
+func (b *Builder) AddAppendedEmptyLayer(created *time.Time, createdBy, author, comment string) {
+ if created != nil {
+ copiedTimestamp := *created
+ created = &copiedTimestamp
+ }
+ b.AppendedEmptyLayers = append(b.AppendedEmptyLayers, ociv1.History{
+ Created: created,
+ CreatedBy: createdBy,
+ Author: author,
+ Comment: comment,
+ EmptyLayer: true,
+ })
+}
+
+// ClearAppendedEmptyLayers clears the list of history entries that we'll add
+// to the committed image after the entry for the layer that we're adding.
+func (b *Builder) ClearAppendedEmptyLayers() {
+ b.AppendedEmptyLayers = nil
+}
+
+// AddPrependedLinkedLayer adds an item to the history that we'll create when
+// committing the image, optionally with a layer, after any history we inherit
+// from the base image, but before the history item that we'll use to describe
+// the new layer that we're adding.
+// The blobPath can be either the location of an uncompressed archive, or a
+// directory whose contents will be archived to use as a layer blob. Leaving
+// blobPath empty is functionally similar to calling AddPrependedEmptyLayer().
+func (b *Builder) AddPrependedLinkedLayer(created *time.Time, createdBy, author, comment, blobPath string) {
+ if created != nil {
+ copiedTimestamp := *created
+ created = &copiedTimestamp
+ }
+ b.PrependedLinkedLayers = append(b.PrependedLinkedLayers, LinkedLayer{
+ BlobPath: blobPath,
+ History: ociv1.History{
+ Created: created,
+ CreatedBy: createdBy,
+ Author: author,
+ Comment: comment,
+ EmptyLayer: blobPath == "",
+ },
+ })
+}
+
+// ClearPrependedLinkedLayers clears the list of history entries that we'll add
+// the committed image before the layer that we're adding (if we're adding it).
+func (b *Builder) ClearPrependedLinkedLayers() {
+ b.PrependedLinkedLayers = nil
+}
+
+// AddAppendedLinkedLayer adds an item to the history that we'll create when
+// committing the image, optionally with a layer, after the history item that
+// we'll use to describe the new layer that we're adding.
+// The blobPath can be either the location of an uncompressed archive, or a
+// directory whose contents will be archived to use as a layer blob. Leaving
+// blobPath empty is functionally similar to calling AddAppendedEmptyLayer().
+func (b *Builder) AddAppendedLinkedLayer(created *time.Time, createdBy, author, comment, blobPath string) {
+ if created != nil {
+ copiedTimestamp := *created
+ created = &copiedTimestamp
+ }
+ b.AppendedLinkedLayers = append(b.AppendedLinkedLayers, LinkedLayer{
+ BlobPath: blobPath,
+ History: ociv1.History{
+ Created: created,
+ CreatedBy: createdBy,
+ Author: author,
+ Comment: comment,
+ EmptyLayer: blobPath == "",
+ },
+ })
+}
+
+// ClearAppendedLinkedLayers clears the list of linked layers that we'll add to
+// the committed image after the layer that we're adding (if we're adding it).
+func (b *Builder) ClearAppendedLinkedLayers() {
+ b.AppendedLinkedLayers = nil
+}
diff --git a/vendor/github.com/containers/buildah/convertcw.go b/vendor/github.com/containers/buildah/convertcw.go
new file mode 100644
index 00000000..0793838b
--- /dev/null
+++ b/vendor/github.com/containers/buildah/convertcw.go
@@ -0,0 +1,220 @@
+package buildah
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "time"
+
+ "github.com/containers/buildah/define"
+ "github.com/containers/buildah/internal/mkcw"
+ encconfig "github.com/containers/ocicrypt/config"
+ "github.com/opencontainers/go-digest"
+ "github.com/sirupsen/logrus"
+ "go.podman.io/image/v5/docker/reference"
+ "go.podman.io/image/v5/types"
+ "go.podman.io/storage"
+ "go.podman.io/storage/pkg/archive"
+)
+
+// CWConvertImageOptions provides both required and optional bits of
+// configuration for CWConvertImage().
+type CWConvertImageOptions struct {
+ // Required parameters.
+ InputImage string
+
+ // If supplied, we'll tag the resulting image with the specified name.
+ Tag string
+ OutputImage types.ImageReference
+
+ // If supplied, we'll register the workload with this server.
+ // Practically necessary if DiskEncryptionPassphrase is not set, in
+ // which case we'll generate one and throw it away after.
+ AttestationURL string
+
+ // Used to measure the environment. If left unset (0), defaults will be applied.
+ CPUs int
+ Memory int
+
+ // Can be manually set. If left unset ("", false, nil), reasonable values will be used.
+ TeeType define.TeeType
+ IgnoreAttestationErrors bool
+ WorkloadID string
+ DiskEncryptionPassphrase string
+ Slop string
+ FirmwareLibrary string
+ BaseImage string
+ Logger *logrus.Logger
+ ExtraImageContent map[string]string
+
+ // Passed through to BuilderOptions. Most settings won't make
+ // sense to be made available here because we don't launch a process.
+ ContainerSuffix string
+ PullPolicy PullPolicy
+ BlobDirectory string
+ SignaturePolicyPath string
+ ReportWriter io.Writer
+ IDMappingOptions *IDMappingOptions
+ Format string
+ MaxPullRetries int
+ PullRetryDelay time.Duration
+ OciDecryptConfig *encconfig.DecryptConfig
+ MountLabel string
+}
+
+// CWConvertImage takes the rootfs and configuration from one image, generates a
+// LUKS-encrypted disk image that more or less includes them both, and puts the
+// result into a new container image.
+// Returns the new image's ID and digest on success, along with a canonical
+// reference for it if a repository name was specified.
+func CWConvertImage(ctx context.Context, systemContext *types.SystemContext, store storage.Store, options CWConvertImageOptions) (string, reference.Canonical, digest.Digest, error) {
+ // Apply our defaults if some options aren't set.
+ logger := options.Logger
+ if logger == nil {
+ logger = logrus.StandardLogger()
+ }
+
+ // Now create the target working container, pulling the base image if
+ // there is one and it isn't present.
+ builderOptions := BuilderOptions{
+ FromImage: options.BaseImage,
+ SystemContext: systemContext,
+ Logger: logger,
+
+ ContainerSuffix: options.ContainerSuffix,
+ PullPolicy: options.PullPolicy,
+ BlobDirectory: options.BlobDirectory,
+ SignaturePolicyPath: options.SignaturePolicyPath,
+ ReportWriter: options.ReportWriter,
+ IDMappingOptions: options.IDMappingOptions,
+ Format: options.Format,
+ MaxPullRetries: options.MaxPullRetries,
+ PullRetryDelay: options.PullRetryDelay,
+ OciDecryptConfig: options.OciDecryptConfig,
+ MountLabel: options.MountLabel,
+ }
+ target, err := NewBuilder(ctx, store, builderOptions)
+ if err != nil {
+ return "", nil, "", fmt.Errorf("creating container from target image: %w", err)
+ }
+ defer func() {
+ if err := target.Delete(); err != nil {
+ logrus.Warnf("deleting target container: %v", err)
+ }
+ }()
+ targetDir, err := target.Mount("")
+ if err != nil {
+ return "", nil, "", fmt.Errorf("mounting target container: %w", err)
+ }
+ defer func() {
+ if err := target.Unmount(); err != nil {
+ logrus.Warnf("unmounting target container: %v", err)
+ }
+ }()
+
+ // Mount the source image, pulling it first if necessary.
+ builderOptions = BuilderOptions{
+ FromImage: options.InputImage,
+ SystemContext: systemContext,
+ Logger: logger,
+
+ ContainerSuffix: options.ContainerSuffix,
+ PullPolicy: options.PullPolicy,
+ BlobDirectory: options.BlobDirectory,
+ SignaturePolicyPath: options.SignaturePolicyPath,
+ ReportWriter: options.ReportWriter,
+ IDMappingOptions: options.IDMappingOptions,
+ Format: options.Format,
+ MaxPullRetries: options.MaxPullRetries,
+ PullRetryDelay: options.PullRetryDelay,
+ OciDecryptConfig: options.OciDecryptConfig,
+ MountLabel: options.MountLabel,
+ }
+ source, err := NewBuilder(ctx, store, builderOptions)
+ if err != nil {
+ return "", nil, "", fmt.Errorf("creating container from source image: %w", err)
+ }
+ defer func() {
+ if err := source.Delete(); err != nil {
+ logrus.Warnf("deleting source container: %v", err)
+ }
+ }()
+ sourceInfo := GetBuildInfo(source)
+ if err != nil {
+ return "", nil, "", fmt.Errorf("retrieving info about source image: %w", err)
+ }
+ sourceImageID := sourceInfo.FromImageID
+ sourceSize, err := store.ImageSize(sourceImageID)
+ if err != nil {
+ return "", nil, "", fmt.Errorf("computing size of source image: %w", err)
+ }
+ sourceDir, err := source.Mount("")
+ if err != nil {
+ return "", nil, "", fmt.Errorf("mounting source container: %w", err)
+ }
+ defer func() {
+ if err := source.Unmount(); err != nil {
+ logrus.Warnf("unmounting source container: %v", err)
+ }
+ }()
+
+ // Generate the image contents.
+ archiveOptions := mkcw.ArchiveOptions{
+ AttestationURL: options.AttestationURL,
+ CPUs: options.CPUs,
+ Memory: options.Memory,
+ TempDir: targetDir,
+ TeeType: options.TeeType,
+ IgnoreAttestationErrors: options.IgnoreAttestationErrors,
+ ImageSize: sourceSize,
+ WorkloadID: options.WorkloadID,
+ DiskEncryptionPassphrase: options.DiskEncryptionPassphrase,
+ Slop: options.Slop,
+ FirmwareLibrary: options.FirmwareLibrary,
+ Logger: logger,
+ GraphOptions: store.GraphOptions(),
+ ExtraImageContent: options.ExtraImageContent,
+ }
+ rc, workloadConfig, err := mkcw.Archive(sourceDir, &source.OCIv1, archiveOptions)
+ if err != nil {
+ return "", nil, "", fmt.Errorf("generating encrypted image content: %w", err)
+ }
+ if err = archive.Untar(rc, targetDir, &archive.TarOptions{}); err != nil {
+ if err = rc.Close(); err != nil {
+ logger.Warnf("cleaning up: %v", err)
+ }
+ return "", nil, "", fmt.Errorf("saving encrypted image content: %w", err)
+ }
+ if err = rc.Close(); err != nil {
+ return "", nil, "", fmt.Errorf("cleaning up: %w", err)
+ }
+
+ // Commit the image. Clear out most of the configuration (if there is any — we default
+ // to scratch as a base) so that an engine that doesn't or can't set up a TEE will just
+ // run the static entrypoint. The rest of the configuration which the runtime consults
+ // is in the .krun_config.json file in the encrypted filesystem.
+ logger.Log(logrus.DebugLevel, "committing disk image")
+ target.ClearAnnotations()
+ target.ClearEnv()
+ target.ClearLabels()
+ target.ClearOnBuild()
+ target.ClearPorts()
+ target.ClearVolumes()
+ target.SetCmd(nil)
+ target.SetCreatedBy(fmt.Sprintf(": convert %q for use with %q", sourceImageID, workloadConfig.Type))
+ target.SetDomainname("")
+ target.SetEntrypoint([]string{"/entrypoint"})
+ target.SetHealthcheck(nil)
+ target.SetHostname("")
+ target.SetMaintainer("")
+ target.SetShell(nil)
+ target.SetUser("")
+ target.SetWorkDir("")
+ commitOptions := CommitOptions{
+ SystemContext: systemContext,
+ }
+ if options.Tag != "" {
+ commitOptions.AdditionalTags = append(commitOptions.AdditionalTags, options.Tag)
+ }
+ return target.Commit(ctx, options.OutputImage, commitOptions)
+}
diff --git a/vendor/github.com/containers/buildah/copier/copier.go b/vendor/github.com/containers/buildah/copier/copier.go
new file mode 100644
index 00000000..9aa662cb
--- /dev/null
+++ b/vendor/github.com/containers/buildah/copier/copier.go
@@ -0,0 +1,2560 @@
+package copier
+
+import (
+ "archive/tar"
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "io/fs"
+ "net"
+ "os"
+ "os/user"
+ "path"
+ "path/filepath"
+ "slices"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "syscall"
+ "time"
+ "unicode"
+
+ "github.com/sirupsen/logrus"
+ "go.podman.io/image/v5/pkg/compression"
+ "go.podman.io/storage/pkg/archive"
+ "go.podman.io/storage/pkg/fileutils"
+ "go.podman.io/storage/pkg/idtools"
+ "go.podman.io/storage/pkg/reexec"
+)
+
+const (
+ copierCommand = "buildah-copier"
+ maxLoopsFollowed = 64
+ // See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06, from archive/tar
+ cISUID = 0o4000 // Set uid, from archive/tar
+ cISGID = 0o2000 // Set gid, from archive/tar
+ cISVTX = 0o1000 // Save text (sticky bit), from archive/tar
+ // xattrs in the PAXRecords map are namespaced with this prefix
+ xattrPAXRecordNamespace = "SCHILY.xattr."
+)
+
+func init() {
+ reexec.Register(copierCommand, copierMain)
+}
+
+// extendedGlob calls filepath.Glob() on the passed-in patterns. If there is a
+// "**" component in the pattern, filepath.Glob() will be called with the "**"
+// replaced with all of the subdirectories under that point, and the results
+// will be concatenated.
+// The matched paths are returned in lexical order, which makes the output deterministic.
+func extendedGlob(pattern string) (matches []string, err error) {
+ subdirs := func(dir string) []string {
+ var subdirectories []string
+ if err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return nil
+ }
+ if d.IsDir() {
+ if rel, err := filepath.Rel(dir, path); err == nil {
+ subdirectories = append(subdirectories, rel)
+ }
+ }
+ return nil
+ }); err != nil {
+ subdirectories = []string{"."}
+ }
+ return subdirectories
+ }
+ expandPatterns := func(pattern string) []string {
+ components := []string{}
+ dir := pattern
+ file := ""
+ for dir != filepath.VolumeName(dir) && dir != string(os.PathSeparator) {
+ dir, file = filepath.Split(dir)
+ if file != "" {
+ components = append([]string{file}, components...)
+ }
+ dir = strings.TrimSuffix(dir, string(os.PathSeparator))
+ }
+ patterns := []string{filepath.VolumeName(dir) + string(os.PathSeparator)}
+ for i := range components {
+ var nextPatterns []string
+ if components[i] == "**" {
+ for _, parent := range patterns {
+ nextSubdirs := subdirs(parent)
+ for _, nextSubdir := range nextSubdirs {
+ nextPatterns = append(nextPatterns, filepath.Join(parent, nextSubdir))
+ }
+ }
+ } else {
+ for _, parent := range patterns {
+ nextPattern := filepath.Join(parent, components[i])
+ nextPatterns = append(nextPatterns, nextPattern)
+ }
+ }
+ patterns = []string{}
+ seen := map[string]struct{}{}
+ for _, nextPattern := range nextPatterns {
+ if _, seen := seen[nextPattern]; seen {
+ continue
+ }
+ patterns = append(patterns, nextPattern)
+ seen[nextPattern] = struct{}{}
+ }
+ }
+ return patterns
+ }
+ patterns := expandPatterns(pattern)
+ for _, pattern := range patterns {
+ theseMatches, err := filepath.Glob(pattern)
+ if err != nil {
+ return nil, err
+ }
+ matches = append(matches, theseMatches...)
+ }
+ sort.Strings(matches)
+ return matches, nil
+}
+
+// isArchivePath returns true if the specified path can be read like a (possibly
+// compressed) tarball.
+func isArchivePath(path string) bool {
+ f, err := os.Open(path)
+ if err != nil {
+ return false
+ }
+ defer f.Close()
+ rc, _, err := compression.AutoDecompress(f)
+ if err != nil {
+ return false
+ }
+ defer rc.Close()
+ tr := tar.NewReader(rc)
+ _, err = tr.Next()
+ return err == nil
+}
+
+// requestType encodes exactly what kind of request this is.
+type requestType string
+
+const (
+ requestEval requestType = "EVAL"
+ requestStat requestType = "STAT"
+ requestGet requestType = "GET"
+ requestPut requestType = "PUT"
+ requestMkdir requestType = "MKDIR"
+ requestRemove requestType = "REMOVE"
+ requestQuit requestType = "QUIT"
+ requestEnsure requestType = "ENSURE"
+ requestConditionalRemove requestType = "CONDRM"
+)
+
+// Request encodes a single request.
+type request struct {
+ Request requestType
+ Root string // used by all requests
+ preservedRoot string
+ rootPrefix string // used to reconstruct paths being handed back to the caller
+ Directory string // used by all requests
+ preservedDirectory string
+ Globs []string `json:",omitempty"` // used by stat, get
+ preservedGlobs []string
+ StatOptions StatOptions
+ GetOptions GetOptions
+ PutOptions PutOptions
+ MkdirOptions MkdirOptions
+ RemoveOptions RemoveOptions
+ EnsureOptions EnsureOptions
+ ConditionalRemoveOptions ConditionalRemoveOptions
+}
+
+func (req *request) Excludes() []string {
+ switch req.Request {
+ case requestEval:
+ return nil
+ case requestStat:
+ return req.StatOptions.Excludes
+ case requestGet:
+ return req.GetOptions.Excludes
+ case requestPut:
+ return nil
+ case requestMkdir:
+ return nil
+ case requestRemove:
+ return nil
+ case requestQuit:
+ return nil
+ case requestEnsure:
+ return nil
+ case requestConditionalRemove:
+ return nil
+ default:
+ panic(fmt.Sprintf("not an implemented request type: %q", req.Request))
+ }
+}
+
+func (req *request) UIDMap() []idtools.IDMap {
+ switch req.Request {
+ case requestEval:
+ return nil
+ case requestStat:
+ return req.StatOptions.UIDMap
+ case requestGet:
+ return req.GetOptions.UIDMap
+ case requestPut:
+ return req.PutOptions.UIDMap
+ case requestMkdir:
+ return req.MkdirOptions.UIDMap
+ case requestRemove:
+ return nil
+ case requestQuit:
+ return nil
+ case requestEnsure:
+ return req.EnsureOptions.UIDMap
+ case requestConditionalRemove:
+ return req.ConditionalRemoveOptions.UIDMap
+ default:
+ panic(fmt.Sprintf("not an implemented request type: %q", req.Request))
+ }
+}
+
+func (req *request) GIDMap() []idtools.IDMap {
+ switch req.Request {
+ case requestEval:
+ return nil
+ case requestStat:
+ return req.StatOptions.GIDMap
+ case requestGet:
+ return req.GetOptions.GIDMap
+ case requestPut:
+ return req.PutOptions.GIDMap
+ case requestMkdir:
+ return req.MkdirOptions.GIDMap
+ case requestRemove:
+ return nil
+ case requestQuit:
+ return nil
+ case requestEnsure:
+ return req.EnsureOptions.GIDMap
+ case requestConditionalRemove:
+ return req.ConditionalRemoveOptions.GIDMap
+ default:
+ panic(fmt.Sprintf("not an implemented request type: %q", req.Request))
+ }
+}
+
+// Response encodes a single response.
+type response struct {
+ Error string `json:",omitempty"`
+ Stat statResponse
+ Eval evalResponse
+ Get getResponse
+ Put putResponse
+ Mkdir mkdirResponse
+ Remove removeResponse
+ Ensure ensureResponse
+ ConditionalRemove conditionalRemoveResponse
+}
+
+// statResponse encodes a response for a single Stat request.
+type statResponse struct {
+ Globs []*StatsForGlob
+}
+
+// evalResponse encodes a response for a single Eval request.
+type evalResponse struct {
+ Evaluated string
+}
+
+// StatsForGlob encode results for a single glob pattern passed to Stat().
+type StatsForGlob struct {
+ Error string `json:",omitempty"` // error if the Glob pattern was malformed
+ Glob string // input pattern to which this result corresponds
+ Globbed []string // a slice of zero or more names that match the glob
+ Results map[string]*StatForItem // one for each Globbed value if there are any, or for Glob
+}
+
+// StatForItem encode results for a single filesystem item, as returned by Stat().
+type StatForItem struct {
+ Error string `json:",omitempty"`
+ Name string
+ Size int64 // dereferenced value for symlinks
+ Mode os.FileMode // dereferenced value for symlinks
+ ModTime time.Time // dereferenced value for symlinks
+ UID, GID int64 // usually in the uint32 range, set to -1 if unknown
+ IsSymlink bool
+ IsDir bool // dereferenced value for symlinks
+ IsRegular bool // dereferenced value for symlinks
+ IsArchive bool // dereferenced value for symlinks
+ ImmediateTarget string `json:",omitempty"` // raw link content
+}
+
+// getResponse encodes a response for a single Get request.
+type getResponse struct{}
+
+// putResponse encodes a response for a single Put request.
+type putResponse struct{}
+
+// mkdirResponse encodes a response for a single Mkdir request.
+type mkdirResponse struct{}
+
+// removeResponse encodes a response for a single Remove request.
+type removeResponse struct{}
+
+// ensureResponse encodes a response to an Ensure request.
+type ensureResponse struct {
+ Created []string // paths that were created because they weren't already present
+ Noted []EnsureParentPath // preexisting paths that are parents of created items
+}
+
+// conditionalRemoveResponse encodes a response to a conditionalRemove request.
+type conditionalRemoveResponse struct {
+ Removed []string // paths that were removed
+}
+
+// EvalOptions controls parts of Eval()'s behavior.
+type EvalOptions struct{}
+
+// Eval evaluates the directory's path, including any intermediate symbolic
+// links.
+// If root is specified and the current OS supports it, and the calling process
+// has the necessary privileges, evaluation is performed in a chrooted context.
+// If the directory is specified as an absolute path, it should either be the
+// root directory or a subdirectory of the root directory. Otherwise, the
+// directory is treated as a path relative to the root directory.
+func Eval(root string, directory string, _ EvalOptions) (string, error) {
+ req := request{
+ Request: requestEval,
+ Root: root,
+ Directory: directory,
+ }
+ resp, err := copier(nil, nil, req)
+ if err != nil {
+ return "", err
+ }
+ if resp.Error != "" {
+ return "", errors.New(resp.Error)
+ }
+ return resp.Eval.Evaluated, nil
+}
+
+// StatOptions controls parts of Stat()'s behavior.
+type StatOptions struct {
+ UIDMap, GIDMap []idtools.IDMap // map from hostIDs to containerIDs when returning results
+ CheckForArchives bool // check for and populate the IsArchive bit in returned values
+ Excludes []string // contents to pretend don't exist, using the OS-specific path separator
+}
+
+// Stat globs the specified pattern in the specified directory and returns its
+// results.
+// If root and directory are both not specified, the current root directory is
+// used, and relative names in the globs list are treated as being relative to
+// the current working directory.
+// If root is specified and the current OS supports it, and the calling process
+// has the necessary privileges, the stat() is performed in a chrooted context.
+// If the directory is specified as an absolute path, it should either be the
+// root directory or a subdirectory of the root directory. Otherwise, the
+// directory is treated as a path relative to the root directory.
+// Relative names in the glob list are treated as being relative to the
+// directory.
+func Stat(root string, directory string, options StatOptions, globs []string) ([]*StatsForGlob, error) {
+ req := request{
+ Request: requestStat,
+ Root: root,
+ Directory: directory,
+ Globs: slices.Clone(globs),
+ StatOptions: options,
+ }
+ resp, err := copier(nil, nil, req)
+ if err != nil {
+ return nil, err
+ }
+ if resp.Error != "" {
+ return nil, errors.New(resp.Error)
+ }
+ return resp.Stat.Globs, nil
+}
+
+// GetOptions controls parts of Get()'s behavior.
+type GetOptions struct {
+ UIDMap, GIDMap []idtools.IDMap // map from hostIDs to containerIDs in the output archive
+ Excludes []string // contents to pretend don't exist, using the OS-specific path separator
+ ExpandArchives bool // extract the contents of named items that are archives
+ ChownDirs *idtools.IDPair // set ownership on directories. no effect on archives being extracted
+ ChmodDirs *os.FileMode // set permissions on directories. no effect on archives being extracted
+ ChownFiles *idtools.IDPair // set ownership of files. no effect on archives being extracted
+ ChmodFiles *os.FileMode // set permissions on files. no effect on archives being extracted
+ Parents bool // maintain the sources parent directory in the destination
+ StripSetuidBit bool // strip the setuid bit off of items being copied. no effect on archives being extracted
+ StripSetgidBit bool // strip the setgid bit off of items being copied. no effect on archives being extracted
+ StripStickyBit bool // strip the sticky bit off of items being copied. no effect on archives being extracted
+ StripXattrs bool // don't record extended attributes of items being copied. no effect on archives being extracted
+ KeepDirectoryNames bool // don't strip the top directory's basename from the paths of items in subdirectories
+ Rename map[string]string // rename items with the specified names, or under the specified names
+ NoDerefSymlinks bool // don't follow symlinks when globs match them
+ IgnoreUnreadable bool // ignore errors reading items, instead of returning an error
+ NoCrossDevice bool // if a subdirectory is a mountpoint with a different device number, include it but skip its contents
+ Timestamp *time.Time // timestamp to force on all contents
+}
+
+// Get produces an archive containing items that match the specified glob
+// patterns and writes it to bulkWriter.
+// If root and directory are both not specified, the current root directory is
+// used, and relative names in the globs list are treated as being relative to
+// the current working directory.
+// If root is specified and the current OS supports it, and the calling process
+// has the necessary privileges, the contents are read in a chrooted context.
+// If the directory is specified as an absolute path, it should either be the
+// root directory or a subdirectory of the root directory. Otherwise, the
+// directory is treated as a path relative to the root directory.
+// Relative names in the glob list are treated as being relative to the
+// directory.
+func Get(root string, directory string, options GetOptions, globs []string, bulkWriter io.Writer) error {
+ req := request{
+ Request: requestGet,
+ Root: root,
+ Directory: directory,
+ Globs: slices.Clone(globs),
+ StatOptions: StatOptions{
+ CheckForArchives: options.ExpandArchives,
+ },
+ GetOptions: options,
+ }
+ resp, err := copier(nil, bulkWriter, req)
+ if err != nil {
+ return err
+ }
+ if resp.Error != "" {
+ return errors.New(resp.Error)
+ }
+ return nil
+}
+
+// PutOptions controls parts of Put()'s behavior.
+type PutOptions struct {
+ UIDMap, GIDMap []idtools.IDMap // map from containerIDs to hostIDs when writing contents to disk
+ DefaultDirOwner *idtools.IDPair // set ownership of implicitly-created directories, default is ChownDirs, or 0:0 if ChownDirs not set
+ DefaultDirMode *os.FileMode // set permissions on implicitly-created directories, default is ChmodDirs, or 0755 if ChmodDirs not set
+ ChownDirs *idtools.IDPair // set ownership of newly-created directories
+ ChmodDirs *os.FileMode // set permissions on newly-created directories
+ ChownFiles *idtools.IDPair // set ownership of newly-created files
+ ChmodFiles *os.FileMode // set permissions on newly-created files
+ StripSetuidBit bool // strip the setuid bit off of items being written
+ StripSetgidBit bool // strip the setgid bit off of items being written
+ StripStickyBit bool // strip the sticky bit off of items being written
+ StripXattrs bool // don't bother trying to set extended attributes of items being copied
+ IgnoreXattrErrors bool // ignore any errors encountered when attempting to set extended attributes
+ IgnoreDevices bool // ignore items which are character or block devices
+ NoOverwriteDirNonDir bool // instead of quietly overwriting directories with non-directories, return an error
+ NoOverwriteNonDirDir bool // instead of quietly overwriting non-directories with directories, return an error
+ Rename map[string]string // rename items with the specified names, or under the specified names
+}
+
+// Put extracts an archive from the bulkReader at the specified directory.
+// If root and directory are both not specified, the current root directory is
+// used.
+// If root is specified and the current OS supports it, and the calling process
+// has the necessary privileges, the contents are written in a chrooted
+// context. If the directory is specified as an absolute path, it should
+// either be the root directory or a subdirectory of the root directory.
+// Otherwise, the directory is treated as a path relative to the root
+// directory.
+func Put(root string, directory string, options PutOptions, bulkReader io.Reader) error {
+ req := request{
+ Request: requestPut,
+ Root: root,
+ Directory: directory,
+ PutOptions: options,
+ }
+ resp, err := copier(bulkReader, nil, req)
+ if err != nil {
+ return err
+ }
+ if resp.Error != "" {
+ return errors.New(resp.Error)
+ }
+ return nil
+}
+
+// MkdirOptions controls parts of Mkdir()'s behavior.
+type MkdirOptions struct {
+ UIDMap, GIDMap []idtools.IDMap // map from containerIDs to hostIDs when creating directories
+ ModTimeNew *time.Time // set mtime and atime of newly-created directories
+ ChownNew *idtools.IDPair // set ownership of newly-created directories
+ ChmodNew *os.FileMode // set permissions on newly-created directories
+}
+
+// Mkdir ensures that the specified directory exists. Any directories which
+// need to be created will be given the specified ownership and permissions.
+// If root and directory are both not specified, the current root directory is
+// used.
+// If root is specified and the current OS supports it, and the calling process
+// has the necessary privileges, the directory is created in a chrooted
+// context. If the directory is specified as an absolute path, it should
+// either be the root directory or a subdirectory of the root directory.
+// Otherwise, the directory is treated as a path relative to the root
+// directory.
+func Mkdir(root string, directory string, options MkdirOptions) error {
+ req := request{
+ Request: requestMkdir,
+ Root: root,
+ Directory: directory,
+ MkdirOptions: options,
+ }
+ resp, err := copier(nil, nil, req)
+ if err != nil {
+ return err
+ }
+ if resp.Error != "" {
+ return errors.New(resp.Error)
+ }
+ return nil
+}
+
+// RemoveOptions controls parts of Remove()'s behavior.
+type RemoveOptions struct {
+ All bool // if Directory is a directory, remove its contents as well
+}
+
+// Remove removes the specified directory or item, traversing any intermediate
+// symbolic links.
+// If the root directory is not specified, the current root directory is used.
+// If root is specified and the current OS supports it, and the calling process
+// has the necessary privileges, the remove() is performed in a chrooted context.
+// If the item to remove is specified as an absolute path, it should either be
+// in the root directory or in a subdirectory of the root directory. Otherwise,
+// the directory is treated as a path relative to the root directory.
+func Remove(root string, item string, options RemoveOptions) error {
+ req := request{
+ Request: requestRemove,
+ Root: root,
+ Directory: item,
+ RemoveOptions: options,
+ }
+ resp, err := copier(nil, nil, req)
+ if err != nil {
+ return err
+ }
+ if resp.Error != "" {
+ return errors.New(resp.Error)
+ }
+ return nil
+}
+
+// cleanerReldirectory resolves relative path candidate lexically, attempting
+// to ensure that when joined as a subdirectory of another directory, it does
+// not reference anything outside of that other directory.
+func cleanerReldirectory(candidate string) string {
+ cleaned := strings.TrimPrefix(filepath.Clean(string(os.PathSeparator)+candidate), string(os.PathSeparator))
+ if cleaned == "" {
+ return "."
+ }
+ return cleaned
+}
+
+// convertToRelSubdirectory returns the path of directory, bound and relative to
+// root, as a relative path, or an error if that path can't be computed or if
+// the two directories are on different volumes
+func convertToRelSubdirectory(root, directory string) (relative string, err error) {
+ if root == "" || !filepath.IsAbs(root) {
+ return "", fmt.Errorf("expected root directory to be an absolute path, got %q", root)
+ }
+ if directory == "" || !filepath.IsAbs(directory) {
+ return "", fmt.Errorf("expected directory to be an absolute path, got %q", root)
+ }
+ if filepath.VolumeName(root) != filepath.VolumeName(directory) {
+ return "", fmt.Errorf("%q and %q are on different volumes", root, directory)
+ }
+ rel, err := filepath.Rel(root, directory)
+ if err != nil {
+ return "", fmt.Errorf("computing path of %q relative to %q: %w", directory, root, err)
+ }
+ return cleanerReldirectory(rel), nil
+}
+
+func currentVolumeRoot() (string, error) {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return "", fmt.Errorf("getting current working directory: %w", err)
+ }
+ return filepath.VolumeName(cwd) + string(os.PathSeparator), nil
+}
+
+func isVolumeRoot(candidate string) (bool, error) {
+ abs, err := filepath.Abs(candidate)
+ if err != nil {
+ return false, fmt.Errorf("converting %q to an absolute path: %w", candidate, err)
+ }
+ return abs == filepath.VolumeName(abs)+string(os.PathSeparator), nil
+}
+
+func looksLikeAbs(candidate string) bool {
+ return candidate[0] == os.PathSeparator && (len(candidate) == 1 || candidate[1] != os.PathSeparator)
+}
+
+func copier(bulkReader io.Reader, bulkWriter io.Writer, req request) (*response, error) {
+ if req.Directory == "" {
+ if req.Root == "" {
+ wd, err := os.Getwd()
+ if err != nil {
+ return nil, fmt.Errorf("getting current working directory: %w", err)
+ }
+ req.Directory = wd
+ } else {
+ req.Directory = req.Root
+ }
+ }
+ if req.Root == "" {
+ root, err := currentVolumeRoot()
+ if err != nil {
+ return nil, fmt.Errorf("determining root of current volume: %w", err)
+ }
+ req.Root = root
+ }
+ if filepath.IsAbs(req.Directory) {
+ _, err := convertToRelSubdirectory(req.Root, req.Directory)
+ if err != nil {
+ return nil, fmt.Errorf("rewriting %q to be relative to %q: %w", req.Directory, req.Root, err)
+ }
+ }
+ isAlreadyRoot, err := isVolumeRoot(req.Root)
+ if err != nil {
+ return nil, fmt.Errorf("checking if %q is a root directory: %w", req.Root, err)
+ }
+ if !isAlreadyRoot && canChroot {
+ return copierWithSubprocess(bulkReader, bulkWriter, req)
+ }
+ return copierWithoutSubprocess(bulkReader, bulkWriter, req)
+}
+
+func copierWithoutSubprocess(bulkReader io.Reader, bulkWriter io.Writer, req request) (*response, error) {
+ req.preservedRoot = req.Root
+ req.rootPrefix = string(os.PathSeparator)
+ req.preservedDirectory = req.Directory
+ req.preservedGlobs = slices.Clone(req.Globs)
+ if !filepath.IsAbs(req.Directory) {
+ req.Directory = filepath.Join(req.Root, cleanerReldirectory(req.Directory))
+ }
+ absoluteGlobs := make([]string, 0, len(req.Globs))
+ for _, glob := range req.preservedGlobs {
+ if filepath.IsAbs(glob) {
+ relativeGlob, err := convertToRelSubdirectory(req.preservedRoot, glob)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error rewriting %q to be relative to %q: %v", glob, req.preservedRoot, err)
+ os.Exit(1)
+ }
+ absoluteGlobs = append(absoluteGlobs, filepath.Join(req.Root, string(os.PathSeparator)+relativeGlob))
+ } else {
+ absoluteGlobs = append(absoluteGlobs, filepath.Join(req.Directory, cleanerReldirectory(glob)))
+ }
+ }
+ req.Globs = absoluteGlobs
+ resp, cb, err := copierHandler(bulkReader, bulkWriter, req)
+ if err != nil {
+ return nil, err
+ }
+ if cb != nil {
+ if err = cb(); err != nil {
+ return nil, err
+ }
+ }
+ return resp, nil
+}
+
+func closeIfNotNilYet(f **os.File, what string) {
+ if f != nil && *f != nil {
+ err := (*f).Close()
+ *f = nil
+ if err != nil {
+ logrus.Debugf("error closing %s: %v", what, err)
+ }
+ }
+}
+
+func copierWithSubprocess(bulkReader io.Reader, bulkWriter io.Writer, req request) (resp *response, err error) {
+ if bulkReader == nil {
+ bulkReader = bytes.NewReader([]byte{})
+ }
+ if bulkWriter == nil {
+ bulkWriter = io.Discard
+ }
+ cmd := reexec.Command(copierCommand)
+ stdinRead, stdinWrite, err := os.Pipe()
+ if err != nil {
+ return nil, fmt.Errorf("pipe: %w", err)
+ }
+ defer closeIfNotNilYet(&stdinRead, "stdin pipe reader")
+ defer closeIfNotNilYet(&stdinWrite, "stdin pipe writer")
+ encoder := json.NewEncoder(stdinWrite)
+ stdoutRead, stdoutWrite, err := os.Pipe()
+ if err != nil {
+ return nil, fmt.Errorf("pipe: %w", err)
+ }
+ defer closeIfNotNilYet(&stdoutRead, "stdout pipe reader")
+ defer closeIfNotNilYet(&stdoutWrite, "stdout pipe writer")
+ decoder := json.NewDecoder(stdoutRead)
+ bulkReaderRead, bulkReaderWrite, err := os.Pipe()
+ if err != nil {
+ return nil, fmt.Errorf("pipe: %w", err)
+ }
+ defer closeIfNotNilYet(&bulkReaderRead, "child bulk content reader pipe, read end")
+ defer closeIfNotNilYet(&bulkReaderWrite, "child bulk content reader pipe, write end")
+ bulkWriterRead, bulkWriterWrite, err := os.Pipe()
+ if err != nil {
+ return nil, fmt.Errorf("pipe: %w", err)
+ }
+ defer closeIfNotNilYet(&bulkWriterRead, "child bulk content writer pipe, read end")
+ defer closeIfNotNilYet(&bulkWriterWrite, "child bulk content writer pipe, write end")
+ cmd.Dir = "/"
+ cmd.Env = append([]string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())}, os.Environ()...)
+
+ errorBuffer := bytes.Buffer{}
+ cmd.Stdin = stdinRead
+ cmd.Stdout = stdoutWrite
+ cmd.Stderr = &errorBuffer
+ cmd.ExtraFiles = []*os.File{bulkReaderRead, bulkWriterWrite}
+ if err = cmd.Start(); err != nil {
+ return nil, fmt.Errorf("starting subprocess: %w", err)
+ }
+ cmdToWaitFor := cmd
+ defer func() {
+ if cmdToWaitFor != nil {
+ if err := cmdToWaitFor.Wait(); err != nil {
+ if errorBuffer.String() != "" {
+ logrus.Debug(errorBuffer.String())
+ }
+ }
+ }
+ }()
+ stdinRead.Close()
+ stdinRead = nil
+ stdoutWrite.Close()
+ stdoutWrite = nil
+ bulkReaderRead.Close()
+ bulkReaderRead = nil
+ bulkWriterWrite.Close()
+ bulkWriterWrite = nil
+ killAndReturn := func(err error, step string) error {
+ if err2 := cmd.Process.Kill(); err2 != nil {
+ return fmt.Errorf("killing subprocess: %v; %s: %w", err2, step, err)
+ }
+ if errors.Is(err, io.ErrClosedPipe) || errors.Is(err, syscall.EPIPE) {
+ err2 := cmd.Wait()
+ if errorText := strings.TrimFunc(errorBuffer.String(), unicode.IsSpace); errorText != "" {
+ err = fmt.Errorf("%s: %w", errorText, err)
+ }
+ if err2 != nil {
+ return fmt.Errorf("waiting on subprocess: %v; %s: %w", err2, step, err)
+ }
+ }
+ return fmt.Errorf("%v: %w", step, err)
+ }
+ if err = encoder.Encode(req); err != nil {
+ return nil, killAndReturn(err, "error encoding work request for copier subprocess")
+ }
+ if err = decoder.Decode(&resp); err != nil {
+ if errors.Is(err, io.EOF) && errorBuffer.Len() > 0 {
+ return nil, killAndReturn(errors.New(errorBuffer.String()), "error in copier subprocess")
+ }
+ return nil, killAndReturn(err, "error decoding response from copier subprocess")
+ }
+ if err = encoder.Encode(&request{Request: requestQuit}); err != nil {
+ return nil, killAndReturn(err, "error encoding quit request for copier subprocess")
+ }
+ stdinWrite.Close()
+ stdinWrite = nil
+ stdoutRead.Close()
+ stdoutRead = nil
+ var wg sync.WaitGroup
+ var readError, writeError error
+ wg.Add(1)
+ go func() {
+ _, writeError = io.Copy(bulkWriter, bulkWriterRead)
+ bulkWriterRead.Close()
+ bulkWriterRead = nil
+ wg.Done()
+ }()
+ wg.Add(1)
+ go func() {
+ _, readError = io.Copy(bulkReaderWrite, bulkReader)
+ bulkReaderWrite.Close()
+ bulkReaderWrite = nil
+ wg.Done()
+ }()
+ wg.Wait()
+ cmdToWaitFor = nil
+ if err = cmd.Wait(); err != nil {
+ if errorBuffer.String() != "" {
+ err = fmt.Errorf("%s", errorBuffer.String())
+ }
+ return nil, err
+ }
+ if cmd.ProcessState.Exited() && !cmd.ProcessState.Success() {
+ err = fmt.Errorf("subprocess exited with error")
+ if errorBuffer.String() != "" {
+ err = fmt.Errorf("%s", errorBuffer.String())
+ }
+ return nil, err
+ }
+ loggedOutput := strings.TrimSuffix(errorBuffer.String(), "\n")
+ if len(loggedOutput) > 0 {
+ for output := range strings.SplitSeq(loggedOutput, "\n") {
+ logrus.Debug(output)
+ }
+ }
+ if readError != nil {
+ return nil, fmt.Errorf("passing bulk input to subprocess: %w", readError)
+ }
+ if writeError != nil {
+ return nil, fmt.Errorf("passing bulk output from subprocess: %w", writeError)
+ }
+ return resp, nil
+}
+
+func copierMain() {
+ var chrooted bool
+ decoder := json.NewDecoder(os.Stdin)
+ encoder := json.NewEncoder(os.Stdout)
+ previousRequestRoot := ""
+
+ // Attempt a user and host lookup to force libc (glibc, and possibly others that use dynamic
+ // modules to handle looking up user and host information) to load modules that match the libc
+ // our binary is currently using. Hopefully they're loaded on first use, so that they won't
+ // need to be loaded after we've chrooted into the rootfs, which could include modules that
+ // don't match our libc and which can't be loaded, or modules which we don't want to execute
+ // because we don't trust their code.
+ _, _ = user.Lookup("buildah")
+ _, _ = net.LookupHost("localhost")
+
+ // Set logging.
+ if level := os.Getenv("LOGLEVEL"); level != "" {
+ if ll, err := strconv.Atoi(level); err == nil {
+ logrus.SetLevel(logrus.Level(ll))
+ }
+ }
+
+ // Set up descriptors for receiving and sending tarstreams.
+ bulkReader := os.NewFile(3, "bulk-reader")
+ bulkWriter := os.NewFile(4, "bulk-writer")
+
+ for {
+ // Read a request.
+ req := new(request)
+ if err := decoder.Decode(req); err != nil {
+ fmt.Fprintf(os.Stderr, "error decoding request from copier parent process: %v", err)
+ os.Exit(1)
+ }
+ if req.Request == requestQuit {
+ // Making Quit a specific request means that we could
+ // run Stat() at a caller's behest before using the
+ // same process for Get() or Put(). Maybe later.
+ break
+ }
+
+ // Multiple requests should list the same root, because we
+ // can't un-chroot to chroot to some other location.
+ if previousRequestRoot != "" {
+ // Check that we got the same input value for
+ // where-to-chroot-to.
+ if req.Root != previousRequestRoot {
+ fmt.Fprintf(os.Stderr, "error: can't change location of chroot from %q to %q", previousRequestRoot, req.Root)
+ os.Exit(1)
+ }
+ previousRequestRoot = req.Root
+ } else {
+ // Figure out where to chroot to, if we weren't told.
+ if req.Root == "" {
+ root, err := currentVolumeRoot()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error determining root of current volume: %v", err)
+ os.Exit(1)
+ }
+ req.Root = root
+ }
+ // Change to the specified root directory.
+ var err error
+ chrooted, err = chroot(req.Root)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%v", err)
+ os.Exit(1)
+ }
+ }
+
+ req.preservedRoot = req.Root
+ req.rootPrefix = string(os.PathSeparator)
+ req.preservedDirectory = req.Directory
+ req.preservedGlobs = slices.Clone(req.Globs)
+ if chrooted {
+ // We'll need to adjust some things now that the root
+ // directory isn't what it was. Make the directory and
+ // globs absolute paths for simplicity's sake.
+ absoluteDirectory := req.Directory
+ if !filepath.IsAbs(req.Directory) {
+ absoluteDirectory = filepath.Join(req.Root, cleanerReldirectory(req.Directory))
+ }
+ relativeDirectory, err := convertToRelSubdirectory(req.preservedRoot, absoluteDirectory)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error rewriting %q to be relative to %q: %v", absoluteDirectory, req.preservedRoot, err)
+ os.Exit(1)
+ }
+ req.Directory = filepath.Clean(string(os.PathSeparator) + relativeDirectory)
+ absoluteGlobs := make([]string, 0, len(req.Globs))
+ for i, glob := range req.preservedGlobs {
+ if filepath.IsAbs(glob) {
+ relativeGlob, err := convertToRelSubdirectory(req.preservedRoot, glob)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error rewriting %q to be relative to %q: %v", glob, req.preservedRoot, err)
+ os.Exit(1)
+ }
+ absoluteGlobs = append(absoluteGlobs, filepath.Clean(string(os.PathSeparator)+relativeGlob))
+ } else {
+ absoluteGlobs = append(absoluteGlobs, filepath.Join(req.Directory, cleanerReldirectory(req.Globs[i])))
+ }
+ }
+ req.Globs = absoluteGlobs
+ req.rootPrefix = req.Root
+ req.Root = string(os.PathSeparator)
+ } else {
+ // Make the directory and globs absolute paths for
+ // simplicity's sake.
+ if !filepath.IsAbs(req.Directory) {
+ req.Directory = filepath.Join(req.Root, cleanerReldirectory(req.Directory))
+ }
+ absoluteGlobs := make([]string, 0, len(req.Globs))
+ for i, glob := range req.preservedGlobs {
+ if filepath.IsAbs(glob) {
+ absoluteGlobs = append(absoluteGlobs, req.Globs[i])
+ } else {
+ absoluteGlobs = append(absoluteGlobs, filepath.Join(req.Directory, cleanerReldirectory(req.Globs[i])))
+ }
+ }
+ req.Globs = absoluteGlobs
+ }
+ resp, cb, err := copierHandler(bulkReader, bulkWriter, *req)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error handling request %#v from copier parent process: %v", *req, err)
+ os.Exit(1)
+ }
+ // Encode the response.
+ if err := encoder.Encode(resp); err != nil {
+ fmt.Fprintf(os.Stderr, "error encoding response %#v for copier parent process: %v", *req, err)
+ os.Exit(1)
+ }
+ // If there's bulk data to transfer, run the callback to either
+ // read or write it.
+ if cb != nil {
+ if err = cb(); err != nil {
+ fmt.Fprintf(os.Stderr, "error during bulk transfer for %#v: %v", *req, err)
+ os.Exit(1)
+ }
+ }
+ }
+}
+
+func copierHandler(bulkReader io.Reader, bulkWriter io.Writer, req request) (*response, func() error, error) {
+ // NewPatternMatcher splits patterns into components using
+ // os.PathSeparator, implying that it expects OS-specific naming
+ // conventions.
+ excludes := req.Excludes()
+ pm, err := fileutils.NewPatternMatcher(excludes)
+ if err != nil {
+ return nil, nil, fmt.Errorf("processing excludes list %v: %w", excludes, err)
+ }
+
+ var idMappings *idtools.IDMappings
+ uidMap, gidMap := req.UIDMap(), req.GIDMap()
+ if len(uidMap) > 0 && len(gidMap) > 0 {
+ idMappings = idtools.NewIDMappingsFromMaps(uidMap, gidMap)
+ }
+
+ switch req.Request {
+ default:
+ return nil, nil, fmt.Errorf("not an implemented request type: %q", req.Request)
+ case requestEval:
+ resp := copierHandlerEval(req)
+ return resp, nil, nil
+ case requestStat:
+ resp := copierHandlerStat(req, pm, idMappings)
+ return resp, nil, nil
+ case requestGet:
+ return copierHandlerGet(bulkWriter, req, pm, idMappings)
+ case requestPut:
+ return copierHandlerPut(bulkReader, req, idMappings)
+ case requestMkdir:
+ return copierHandlerMkdir(req, idMappings)
+ case requestRemove:
+ resp := copierHandlerRemove(req)
+ return resp, nil, nil
+ case requestEnsure:
+ resp := copierHandlerEnsure(req, idMappings)
+ return resp, nil, nil
+ case requestConditionalRemove:
+ resp := copierHandlerConditionalRemove(req, idMappings)
+ return resp, nil, nil
+ case requestQuit:
+ return nil, nil, nil
+ }
+}
+
+// pathIsExcluded computes path relative to root, then asks the pattern matcher
+// if the result is excluded. Returns the relative path and the matcher's
+// results.
+func pathIsExcluded(root, path string, pm *fileutils.PatternMatcher) (string, bool, error) {
+ rel, err := convertToRelSubdirectory(root, path)
+ if err != nil {
+ return "", false, fmt.Errorf("copier: error computing path of %q relative to root %q: %w", path, root, err)
+ }
+ if pm == nil {
+ return rel, false, nil
+ }
+ if rel == "." {
+ // special case
+ return rel, false, nil
+ }
+ // Matches uses filepath.FromSlash() to convert candidates before
+ // checking if they match the patterns it's been given, implying that
+ // it expects Unix-style paths.
+ matches, err := pm.Matches(filepath.ToSlash(rel)) //nolint:staticcheck
+ if err != nil {
+ return rel, false, fmt.Errorf("copier: error checking if %q is excluded: %w", rel, err)
+ }
+ if matches {
+ return rel, true, nil
+ }
+ return rel, false, nil
+}
+
+// resolvePath resolves symbolic links in paths, treating the specified
+// directory as the root.
+// Resolving the path this way, and using the result, is in no way secure
+// against another process manipulating the content that we're looking at, and
+// it is not expected to be.
+// This helps us approximate chrooted behavior on systems and in test cases
+// where chroot isn't available.
+func resolvePath(root, path string, evaluateFinalComponent bool, pm *fileutils.PatternMatcher) (string, error) {
+ rel, err := convertToRelSubdirectory(root, path)
+ if err != nil {
+ return "", fmt.Errorf("making path %q relative to %q", path, root)
+ }
+ workingPath := root
+ followed := 0
+ components := strings.Split(rel, string(os.PathSeparator))
+ excluded := false
+ for len(components) > 0 {
+ // if anything we try to examine is excluded, then resolution has to "break"
+ _, thisExcluded, err := pathIsExcluded(root, filepath.Join(workingPath, components[0]), pm)
+ if err != nil {
+ return "", err
+ }
+ excluded = excluded || thisExcluded
+ if !excluded {
+ if target, err := os.Readlink(filepath.Join(workingPath, components[0])); err == nil && (len(components) != 1 || evaluateFinalComponent) {
+ followed++
+ if followed > maxLoopsFollowed {
+ return "", &os.PathError{
+ Op: "open",
+ Path: path,
+ Err: syscall.ELOOP,
+ }
+ }
+ if filepath.IsAbs(target) || looksLikeAbs(target) {
+ // symlink to an absolute path - prepend the
+ // root directory to that absolute path to
+ // replace the current location, and resolve
+ // the remaining components
+ workingPath = root
+ components = append(strings.Split(target, string(os.PathSeparator)), components[1:]...)
+ continue
+ }
+ // symlink to a relative path - add the link target to
+ // the current location to get the next location, and
+ // resolve the remaining components
+ rel, err := convertToRelSubdirectory(root, filepath.Join(workingPath, target))
+ if err != nil {
+ return "", fmt.Errorf("making path %q relative to %q", filepath.Join(workingPath, target), root)
+ }
+ workingPath = root
+ components = append(strings.Split(filepath.Clean(string(os.PathSeparator)+rel), string(os.PathSeparator)), components[1:]...)
+ continue
+ }
+ }
+ // append the current component's name to get the next location
+ workingPath = filepath.Join(workingPath, components[0])
+ if workingPath == filepath.Join(root, "..") {
+ // attempted to go above the root using a relative path .., scope it
+ workingPath = root
+ }
+ // ready to handle the next component
+ components = components[1:]
+ }
+ return workingPath, nil
+}
+
+func copierHandlerEval(req request) *response {
+ errorResponse := func(fmtspec string, args ...any) *response {
+ return &response{Error: fmt.Sprintf(fmtspec, args...), Eval: evalResponse{}}
+ }
+ resolvedTarget, err := resolvePath(req.Root, req.Directory, true, nil)
+ if err != nil {
+ return errorResponse("copier: eval: error resolving %q: %v", req.Directory, err)
+ }
+ return &response{Eval: evalResponse{Evaluated: filepath.Join(req.rootPrefix, resolvedTarget)}}
+}
+
+func copierHandlerStat(req request, pm *fileutils.PatternMatcher, idMappings *idtools.IDMappings) *response {
+ errorResponse := func(fmtspec string, args ...any) *response {
+ return &response{Error: fmt.Sprintf(fmtspec, args...), Stat: statResponse{}}
+ }
+ if len(req.Globs) == 0 {
+ return errorResponse("copier: stat: expected at least one glob pattern, got none")
+ }
+ var stats []*StatsForGlob
+ for i, glob := range req.Globs {
+ s := StatsForGlob{
+ Glob: req.preservedGlobs[i],
+ }
+ // glob this pattern
+ globMatched, err := extendedGlob(glob)
+ if err != nil {
+ s.Error = fmt.Sprintf("copier: stat: %q while matching glob pattern %q", err.Error(), glob)
+ }
+
+ if len(globMatched) == 0 && strings.ContainsAny(glob, "*?[") {
+ continue
+ }
+ // collect the matches
+ s.Globbed = make([]string, 0, len(globMatched))
+ s.Results = make(map[string]*StatForItem)
+ for _, globbed := range globMatched {
+ rel, excluded, err := pathIsExcluded(req.Root, globbed, pm)
+ if err != nil {
+ return errorResponse("copier: stat: %v", err)
+ }
+ if excluded {
+ continue
+ }
+ // if the glob was an absolute path, reconstruct the
+ // path that we should hand back for the match
+ var resultName string
+ if filepath.IsAbs(req.preservedGlobs[i]) {
+ resultName = filepath.Join(req.rootPrefix, globbed)
+ } else {
+ relResult := rel
+ if req.Directory != req.Root {
+ relResult, err = convertToRelSubdirectory(req.Directory, globbed)
+ if err != nil {
+ return errorResponse("copier: stat: error making %q relative to %q: %v", globbed, req.Directory, err)
+ }
+ }
+ resultName = relResult
+ }
+ result := StatForItem{Name: resultName}
+ s.Globbed = append(s.Globbed, resultName)
+ s.Results[resultName] = &result
+ // lstat the matched value
+ linfo, err := os.Lstat(globbed)
+ if err != nil {
+ result.Error = err.Error()
+ continue
+ }
+ result.Size = linfo.Size()
+ result.Mode = linfo.Mode()
+ result.UID, result.GID = -1, -1
+ if uid, gid, err := owner(linfo); err == nil {
+ if idMappings != nil && !idMappings.Empty() {
+ hostPair := idtools.IDPair{UID: uid, GID: gid}
+ uid, gid, err = idMappings.ToContainer(hostPair)
+ if err != nil {
+ return errorResponse("copier: stat: mapping host filesystem owners %#v to container filesystem owners: %w", hostPair, err)
+ }
+ }
+ result.UID, result.GID = int64(uid), int64(gid)
+ }
+ result.ModTime = linfo.ModTime()
+ result.IsDir = linfo.IsDir()
+ result.IsRegular = result.Mode.IsRegular()
+ result.IsSymlink = (linfo.Mode() & os.ModeType) == os.ModeSymlink
+ checkForArchive := req.StatOptions.CheckForArchives
+ if result.IsSymlink {
+ // if the match was a symbolic link, read it
+ immediateTarget, err := os.Readlink(globbed)
+ if err != nil {
+ result.Error = err.Error()
+ continue
+ }
+ // record where it points, both by itself (it
+ // could be a relative link) and in the context
+ // of the chroot
+ result.ImmediateTarget = immediateTarget
+ resolvedTarget, err := resolvePath(req.Root, globbed, true, pm)
+ if err != nil {
+ return errorResponse("copier: stat: error resolving %q: %v", globbed, err)
+ }
+ // lstat the thing that we point to
+ info, err := os.Lstat(resolvedTarget)
+ if err != nil {
+ result.Error = err.Error()
+ continue
+ }
+ // replace IsArchive/IsDir/IsRegular with info about the target
+ if info.Mode().IsRegular() && req.StatOptions.CheckForArchives {
+ result.IsArchive = isArchivePath(resolvedTarget)
+ checkForArchive = false
+ }
+ result.IsDir = info.IsDir()
+ result.IsRegular = info.Mode().IsRegular()
+ }
+ if result.IsRegular && checkForArchive {
+ // we were asked to check on this, and it
+ // wasn't a symlink, in which case we'd have
+ // already checked what the link points to
+ result.IsArchive = isArchivePath(globbed)
+ }
+ }
+ // no unskipped matches -> error
+ if len(s.Globbed) == 0 {
+ s.Globbed = nil
+ s.Results = nil
+ s.Error = fmt.Sprintf("copier: stat: %q: %v", glob, syscall.ENOENT)
+ }
+ stats = append(stats, &s)
+ }
+ // no matches -> error
+ if len(stats) == 0 {
+ s := StatsForGlob{
+ Error: fmt.Sprintf("copier: stat: %q: %v", req.Globs, syscall.ENOENT),
+ }
+ stats = append(stats, &s)
+ }
+ return &response{Stat: statResponse{Globs: stats}}
+}
+
+func errorIsPermission(err error) bool {
+ if err == nil {
+ return false
+ }
+ return errors.Is(err, os.ErrPermission) || strings.Contains(err.Error(), "permission denied")
+}
+
+func getParents(path string, stopPath string) []string {
+ out := []string{}
+ for path != "/" && path != "." && path != stopPath {
+ path = filepath.Dir(path)
+ if path == stopPath {
+ continue
+ }
+ out = append(out, path)
+ }
+ slices.Reverse(out)
+ return out
+}
+
+func checkLinks(item string, req request, info os.FileInfo) (string, os.FileInfo, error) {
+ // chase links. if we hit a dead end, we should just fail
+ oldItem := item
+ followedLinks := 0
+ const maxFollowedLinks = 16
+ for !req.GetOptions.NoDerefSymlinks && info.Mode()&os.ModeType == os.ModeSymlink && followedLinks < maxFollowedLinks {
+ path, err := os.Readlink(item)
+ if err != nil {
+ continue
+ }
+ if filepath.IsAbs(path) || looksLikeAbs(path) {
+ path = filepath.Join(req.Root, path)
+ } else {
+ path = filepath.Join(filepath.Dir(item), path)
+ }
+ item = path
+ if _, err = convertToRelSubdirectory(req.Root, item); err != nil {
+ return "", nil, fmt.Errorf("copier: get: computing path of %q(%q) relative to %q: %w", oldItem, item, req.Root, err)
+ }
+ if info, err = os.Lstat(item); err != nil {
+ return "", nil, fmt.Errorf("copier: get: lstat %q(%q): %w", oldItem, item, err)
+ }
+ followedLinks++
+ }
+ if followedLinks >= maxFollowedLinks {
+ return "", nil, fmt.Errorf("copier: get: resolving symlink %q(%q): %w", oldItem, item, syscall.ELOOP)
+ }
+ return item, info, nil
+}
+
+func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMatcher, idMappings *idtools.IDMappings) (*response, func() error, error) {
+ statRequest := req
+ statRequest.Request = requestStat
+ statResponse := copierHandlerStat(req, pm, idMappings)
+ errorResponse := func(fmtspec string, args ...any) (*response, func() error, error) {
+ return &response{Error: fmt.Sprintf(fmtspec, args...), Stat: statResponse.Stat, Get: getResponse{}}, nil, nil
+ }
+ if statResponse.Error != "" {
+ return errorResponse("%s", statResponse.Error)
+ }
+ if len(req.Globs) == 0 {
+ return errorResponse("copier: get: expected at least one glob pattern, got 0")
+ }
+ // build a queue of items by globbing
+ type queueItem struct {
+ glob string
+ parents []string
+ }
+ var queue []queueItem
+ globMatchedCount := 0
+ for _, glob := range req.Globs {
+ globMatched, err := extendedGlob(glob)
+ if err != nil {
+ return errorResponse("copier: get: glob %q: %v", glob, err)
+ }
+ for _, path := range globMatched {
+ var parents []string
+ if req.GetOptions.Parents {
+ parents = getParents(path, req.Directory)
+ }
+ globMatchedCount++
+ queue = append(queue, queueItem{glob: path, parents: parents})
+ }
+ }
+ // no matches -> error
+ if len(queue) == 0 {
+ return errorResponse("copier: get: globs %v matched nothing (%d filtered out): %v", req.Globs, globMatchedCount, syscall.ENOENT)
+ }
+ topInfo, err := os.Stat(req.Directory)
+ if err != nil {
+ return errorResponse("copier: get: error reading info about directory %q: %v", req.Directory, err)
+ }
+ cb := func() error {
+ tw := tar.NewWriter(bulkWriter)
+ defer tw.Close()
+ hardlinkChecker := new(hardlinkChecker)
+ itemsCopied := 0
+ addedParents := map[string]struct{}{}
+ for i, qItem := range queue {
+ item := qItem.glob
+ // if we're not discarding the names of individual directories, keep track of this one
+ relNamePrefix := ""
+ if req.GetOptions.KeepDirectoryNames {
+ relNamePrefix = filepath.Base(item)
+ }
+ // if the named thing-to-read is a symlink, dereference it
+ info, err := os.Lstat(item)
+ if err != nil {
+ return fmt.Errorf("copier: get: lstat %q: %w", item, err)
+ }
+ if req.GetOptions.Parents && info.Mode().IsDir() {
+ if !slices.Contains(qItem.parents, item) {
+ qItem.parents = append(qItem.parents, item)
+ }
+ }
+ // Copy parents in to tarball first if exists
+ for _, parent := range qItem.parents {
+ oldParent := parent
+ parentInfo, err := os.Lstat(parent)
+ if err != nil {
+ return fmt.Errorf("copier: get: lstat %q: %w", parent, err)
+ }
+ parent, parentInfo, err = checkLinks(parent, req, parentInfo)
+ if err != nil {
+ return err
+ }
+ parentName, err := convertToRelSubdirectory(req.Directory, oldParent)
+ if err != nil {
+ return fmt.Errorf("copier: get: error computing path of %q relative to %q: %w", parent, req.Directory, err)
+ }
+ if parentName == "" || parentName == "." {
+ // skip the "." entry
+ continue
+ }
+
+ if _, ok := addedParents[parentName]; ok {
+ continue
+ }
+ addedParents[parentName] = struct{}{}
+
+ if err := copierHandlerGetOne(parentInfo, "", parentName, parent, req.GetOptions, tw, hardlinkChecker, idMappings); err != nil {
+ if req.GetOptions.IgnoreUnreadable && errorIsPermission(err) {
+ continue
+ } else if errors.Is(err, os.ErrNotExist) {
+ logrus.Warningf("copier: file disappeared while reading: %q", parent)
+ return nil
+ }
+ return fmt.Errorf("copier: get: %q: %w", queue[i].glob, err)
+ }
+ itemsCopied++
+ }
+
+ item, info, err = checkLinks(item, req, info)
+ if err != nil {
+ return err
+ }
+
+ // evaluate excludes relative to the root directory
+ if info.Mode().IsDir() {
+ // we don't expand any of the contents that are archives
+ options := req.GetOptions
+ options.ExpandArchives = false
+ walkfn := func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ if options.IgnoreUnreadable && errorIsPermission(err) {
+ if info != nil && d.IsDir() {
+ return filepath.SkipDir
+ }
+ return nil
+ } else if errors.Is(err, os.ErrNotExist) {
+ logrus.Warningf("copier: file disappeared while reading: %q", path)
+ return nil
+ }
+ return fmt.Errorf("copier: get: error reading %q: %w", path, err)
+ }
+ if d.Type() == os.ModeSocket {
+ logrus.Warningf("copier: skipping socket %q", d.Name())
+ return nil
+ }
+ // compute the path of this item
+ // relative to the top-level directory,
+ // for the tar header
+ rel, relErr := convertToRelSubdirectory(item, path)
+ if relErr != nil {
+ return fmt.Errorf("copier: get: error computing path of %q relative to top directory %q: %w", path, item, relErr)
+ }
+ // prefix the original item's name if we're keeping it
+ if relNamePrefix != "" {
+ rel = filepath.Join(relNamePrefix, rel)
+ }
+ if rel == "" || rel == "." {
+ // skip the "." entry
+ return nil
+ }
+ skippedPath, skip, err := pathIsExcluded(req.Root, path, pm)
+ if err != nil {
+ return err
+ }
+ if skip {
+ if d.IsDir() {
+ // if there are no "include
+ // this anyway" patterns at
+ // all, we don't need to
+ // descend into this particular
+ // directory if it's a directory
+ if !pm.Exclusions() {
+ return filepath.SkipDir
+ }
+ // if there are exclusion
+ // patterns for which this
+ // path is a prefix, we
+ // need to keep descending
+ for _, pattern := range pm.Patterns() {
+ if !pattern.Exclusion() {
+ continue
+ }
+ spec := strings.Trim(pattern.String(), string(os.PathSeparator))
+ trimmedPath := strings.Trim(skippedPath, string(os.PathSeparator))
+ if strings.HasPrefix(spec+string(os.PathSeparator), trimmedPath) {
+ // we can't just skip over
+ // this directory
+ return nil
+ }
+ }
+ // there are exclusions, but
+ // none of them apply here
+ return filepath.SkipDir
+ }
+ // skip this item, but if we're
+ // a directory, a more specific
+ // but-include-this for
+ // something under it might
+ // also be in the excludes list
+ return nil
+ }
+ // if it's a symlink, read its target
+ symlinkTarget := ""
+ if d.Type() == os.ModeSymlink {
+ target, err := os.Readlink(path)
+ if err != nil {
+ return fmt.Errorf("copier: get: readlink(%q(%q)): %w", rel, path, err)
+ }
+ symlinkTarget = target
+ }
+ info, err := d.Info()
+ if err != nil {
+ return err
+ }
+ // if it's a directory and we're staying on one device, and it's on a
+ // different device than the one we started from, skip its contents
+ var ok error
+ if d.IsDir() && req.GetOptions.NoCrossDevice {
+ if !sameDevice(topInfo, info) {
+ ok = filepath.SkipDir
+ }
+ }
+ if req.GetOptions.Parents {
+ rel, err = convertToRelSubdirectory(req.Directory, path)
+ if err != nil {
+ return fmt.Errorf("copier: get: error computing path of %q relative to %q: %w", path, req.Root, err)
+ }
+ }
+ // add the item to the outgoing tar stream
+ if err := copierHandlerGetOne(info, symlinkTarget, rel, path, options, tw, hardlinkChecker, idMappings); err != nil {
+ if req.GetOptions.IgnoreUnreadable && errorIsPermission(err) {
+ return ok
+ } else if errors.Is(err, os.ErrNotExist) {
+ logrus.Warningf("copier: file disappeared while reading: %q", path)
+ return nil
+ }
+ return err
+ }
+ return ok
+ }
+ // walk the directory tree, checking/adding items individually
+ if err := filepath.WalkDir(item, walkfn); err != nil {
+ return fmt.Errorf("copier: get: %q(%q): %w", queue[i].glob, item, err)
+ }
+ itemsCopied++
+ } else {
+ _, skip, err := pathIsExcluded(req.Root, item, pm)
+ if err != nil {
+ return err
+ }
+ if skip {
+ continue
+ }
+
+ name := filepath.Base(queue[i].glob)
+ if req.GetOptions.Parents {
+ name, err = convertToRelSubdirectory(req.Directory, queue[i].glob)
+ if err != nil {
+ return fmt.Errorf("copier: get: error computing path of %q relative to %q: %w", item, req.Root, err)
+ }
+ if name == "" || name == "." {
+ // skip the "." entry
+ continue
+ }
+ }
+
+ if err := copierHandlerGetOne(info, "", name, item, req.GetOptions, tw, hardlinkChecker, idMappings); err != nil {
+ if req.GetOptions.IgnoreUnreadable && errorIsPermission(err) {
+ continue
+ }
+ return fmt.Errorf("copier: get: %q: %w", queue[i].glob, err)
+ }
+ itemsCopied++
+ }
+ }
+ if itemsCopied == 0 {
+ return fmt.Errorf("copier: get: copied no items: %w", syscall.ENOENT)
+ }
+ return nil
+ }
+ return &response{Stat: statResponse.Stat, Get: getResponse{}}, cb, nil
+}
+
+func handleRename(rename map[string]string, name string) string {
+ if rename == nil {
+ return name
+ }
+ // header names always use '/', so use path instead of filepath to manipulate it
+ if directMapping, ok := rename[name]; ok {
+ return directMapping
+ }
+ prefix, remainder := path.Split(name)
+ for prefix != "" {
+ if mappedPrefix, ok := rename[prefix]; ok {
+ return path.Join(mappedPrefix, remainder)
+ }
+ if prefix[len(prefix)-1] == '/' {
+ prefix = prefix[:len(prefix)-1]
+ if mappedPrefix, ok := rename[prefix]; ok {
+ return path.Join(mappedPrefix, remainder)
+ }
+ }
+ newPrefix, middlePart := path.Split(prefix)
+ if newPrefix == prefix {
+ return name
+ }
+ prefix = newPrefix
+ remainder = path.Join(middlePart, remainder)
+ }
+ return name
+}
+
+// mapWithPrefixedKeysWithoutKeyPrefix returns a map containing every element
+// of m that had p as a prefix in its (string) key, with that prefix stripped
+// from its key. items are shallow-copied using assignment. if m is nil, the
+// returned map will be nil, otherwise it will at least have been allocated
+func mapWithPrefixedKeysWithoutKeyPrefix[K any](m map[string]K, p string) map[string]K {
+ if m == nil {
+ return m
+ }
+ cloned := make(map[string]K, len(m))
+ for k, v := range m {
+ if after, ok := strings.CutPrefix(k, p); ok {
+ cloned[after] = v
+ }
+ }
+ return cloned
+}
+
+func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath string, options GetOptions, tw *tar.Writer, hardlinkChecker *hardlinkChecker, idMappings *idtools.IDMappings) error {
+ // build the header using the name provided
+ hdr, err := tar.FileInfoHeader(srcfi, symlinkTarget)
+ if err != nil {
+ return fmt.Errorf("generating tar header for %s (%s): %w", contentPath, symlinkTarget, err)
+ }
+ if name != "" {
+ hdr.Name = filepath.ToSlash(name)
+ }
+ hdr.Uname, hdr.Gname = "", ""
+ if options.Rename != nil {
+ hdr.Name = handleRename(options.Rename, hdr.Name)
+ }
+ if options.StripSetuidBit {
+ hdr.Mode &^= cISUID
+ }
+ if options.StripSetgidBit {
+ hdr.Mode &^= cISGID
+ }
+ if options.StripStickyBit {
+ hdr.Mode &^= cISVTX
+ }
+ // read extended attributes
+ var xattrs map[string]string
+ if !options.StripXattrs {
+ xattrs, err = Lgetxattrs(contentPath)
+ if err != nil {
+ return fmt.Errorf("getting extended attributes for %q: %w", contentPath, err)
+ }
+ if len(xattrs) > 0 && hdr.PAXRecords == nil {
+ hdr.PAXRecords = make(map[string]string, len(xattrs))
+ }
+ }
+ for k, v := range xattrs {
+ hdr.PAXRecords[xattrPAXRecordNamespace+k] = v
+ }
+ if hdr.Typeflag == tar.TypeReg {
+ // if it's an archive and we're extracting archives, read the
+ // file and spool out its contents in-line. (if we just
+ // inlined the whole file, we'd also be inlining the EOF marker
+ // it contains)
+ if options.ExpandArchives && isArchivePath(contentPath) {
+ f, err := os.Open(contentPath)
+ if err != nil {
+ return fmt.Errorf("opening file for reading archive contents: %w", err)
+ }
+ defer f.Close()
+ rc, _, err := compression.AutoDecompress(f)
+ if err != nil {
+ return fmt.Errorf("decompressing %s: %w", contentPath, err)
+ }
+ defer rc.Close()
+ tr := tar.NewReader(rc)
+ hdr, err := tr.Next()
+ for err == nil {
+ if options.Rename != nil {
+ hdr.Name = handleRename(options.Rename, hdr.Name)
+ }
+ if options.Timestamp != nil {
+ timestamp := options.Timestamp.UTC()
+ hdr.ModTime = timestamp
+ if !hdr.AccessTime.IsZero() {
+ hdr.AccessTime = timestamp
+ }
+ if !hdr.ChangeTime.IsZero() {
+ hdr.ChangeTime = timestamp
+ }
+ }
+ if err = tw.WriteHeader(hdr); err != nil {
+ return fmt.Errorf("writing tar header from %q to pipe: %w", contentPath, err)
+ }
+ if hdr.Size != 0 {
+ n, err := io.Copy(tw, tr)
+ if err != nil {
+ return fmt.Errorf("extracting content from archive %s: %s: %w", contentPath, hdr.Name, err)
+ }
+ if n != hdr.Size {
+ return fmt.Errorf("extracting contents of archive %s: incorrect length for %q", contentPath, hdr.Name)
+ }
+ tw.Flush()
+ }
+ hdr, err = tr.Next()
+ }
+ if err != io.EOF {
+ return fmt.Errorf("extracting contents of archive %s: %w", contentPath, err)
+ }
+ return nil
+ }
+ // if this regular file is hard linked to something else we've
+ // already added, set up to output a TypeLink entry instead of
+ // a TypeReg entry
+ target := hardlinkChecker.Check(srcfi)
+ if target != "" {
+ hdr.Typeflag = tar.TypeLink
+ hdr.Linkname = filepath.ToSlash(target)
+ hdr.Size = 0
+ } else {
+ // note the device/inode pair for this file
+ hardlinkChecker.Add(srcfi, name)
+ }
+ }
+ // map the ownership for the archive
+ if idMappings != nil && !idMappings.Empty() {
+ hostPair := idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
+ hdr.Uid, hdr.Gid, err = idMappings.ToContainer(hostPair)
+ if err != nil {
+ return fmt.Errorf("mapping host filesystem owners %#v to container filesystem owners: %w", hostPair, err)
+ }
+ }
+ // force ownership and/or permissions, if requested
+ if hdr.Typeflag == tar.TypeDir {
+ if options.ChownDirs != nil {
+ hdr.Uid, hdr.Gid = options.ChownDirs.UID, options.ChownDirs.GID
+ }
+ if options.ChmodDirs != nil {
+ hdr.Mode = int64(*options.ChmodDirs)
+ }
+ if !strings.HasSuffix(hdr.Name, "/") {
+ hdr.Name += "/"
+ }
+ } else {
+ if options.ChownFiles != nil {
+ hdr.Uid, hdr.Gid = options.ChownFiles.UID, options.ChownFiles.GID
+ }
+ if options.ChmodFiles != nil {
+ hdr.Mode = int64(*options.ChmodFiles)
+ }
+ }
+ // read fflags, if any
+ if err := archive.ReadFileFlagsToTarHeader(contentPath, hdr); err != nil {
+ return fmt.Errorf("getting fflags: %w", err)
+ }
+ var f *os.File
+ switch hdr.Typeflag {
+ case tar.TypeReg:
+ // open the file first so that we don't write a header for it if we can't actually read it
+ f, err = os.Open(contentPath)
+ if err != nil {
+ return fmt.Errorf("opening file for adding its contents to archive: %w", err)
+ }
+ defer f.Close()
+ case tar.TypeDir:
+ // open the directory file first to make sure we can access it.
+ f, err = os.Open(contentPath)
+ if err != nil {
+ return fmt.Errorf("opening directory for adding its contents to archive: %w", err)
+ }
+ defer f.Close()
+ }
+ if options.Timestamp != nil {
+ timestamp := options.Timestamp.UTC()
+ hdr.ModTime = timestamp
+ if !hdr.AccessTime.IsZero() {
+ hdr.AccessTime = timestamp
+ }
+ if !hdr.ChangeTime.IsZero() {
+ hdr.ChangeTime = timestamp
+ }
+ }
+ // output the header
+ if err = tw.WriteHeader(hdr); err != nil {
+ return fmt.Errorf("writing header for %s (%s): %w", contentPath, hdr.Name, err)
+ }
+ if hdr.Typeflag == tar.TypeReg {
+ // output the content
+ n, err := io.Copy(tw, f)
+ if err != nil {
+ return fmt.Errorf("copying %s: %w", contentPath, err)
+ }
+ if n != hdr.Size {
+ return fmt.Errorf("copying %s: incorrect size (expected %d bytes, read %d bytes)", contentPath, n, hdr.Size)
+ }
+ tw.Flush()
+ }
+ return nil
+}
+
+func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDMappings) (*response, func() error, error) {
+ errorResponse := func(fmtspec string, args ...any) (*response, func() error, error) {
+ return &response{Error: fmt.Sprintf(fmtspec, args...), Put: putResponse{}}, nil, nil
+ }
+ dirUID, dirGID, defaultDirUID, defaultDirGID := 0, 0, 0, 0
+ if req.PutOptions.ChownDirs != nil {
+ dirUID, dirGID = req.PutOptions.ChownDirs.UID, req.PutOptions.ChownDirs.GID
+ defaultDirUID, defaultDirGID = dirUID, dirGID
+ }
+ defaultDirMode := os.FileMode(0o755)
+ if req.PutOptions.ChmodDirs != nil {
+ defaultDirMode = *req.PutOptions.ChmodDirs
+ }
+ if req.PutOptions.DefaultDirOwner != nil {
+ defaultDirUID, defaultDirGID = req.PutOptions.DefaultDirOwner.UID, req.PutOptions.DefaultDirOwner.GID
+ }
+ if req.PutOptions.DefaultDirMode != nil {
+ defaultDirMode = *req.PutOptions.DefaultDirMode
+ }
+ var fileUID, fileGID *int
+ if req.PutOptions.ChownFiles != nil {
+ fileUID, fileGID = &req.PutOptions.ChownFiles.UID, &req.PutOptions.ChownFiles.GID
+ }
+ if idMappings != nil && !idMappings.Empty() {
+ containerDirPair := idtools.IDPair{UID: dirUID, GID: dirGID}
+ hostDirPair, err := idMappings.ToHost(containerDirPair)
+ if err != nil {
+ return errorResponse("copier: put: error mapping container filesystem owner %d:%d to host filesystem owners: %v", dirUID, dirGID, err)
+ }
+ dirUID, dirGID = hostDirPair.UID, hostDirPair.GID
+ defaultDirUID, defaultDirGID = hostDirPair.UID, hostDirPair.GID
+ if req.PutOptions.ChownFiles != nil {
+ containerFilePair := idtools.IDPair{UID: *fileUID, GID: *fileGID}
+ hostFilePair, err := idMappings.ToHost(containerFilePair)
+ if err != nil {
+ return errorResponse("copier: put: error mapping container filesystem owner %d:%d to host filesystem owners: %v", fileUID, fileGID, err)
+ }
+ fileUID, fileGID = &hostFilePair.UID, &hostFilePair.GID
+ }
+ }
+ directoryModes := make(map[string]os.FileMode)
+ ensureDirectoryUnderRoot := func(directory string) error {
+ rel, err := convertToRelSubdirectory(req.Root, directory)
+ if err != nil {
+ return fmt.Errorf("%q is not a subdirectory of %q: %w", directory, req.Root, err)
+ }
+ subdir := ""
+ for component := range strings.SplitSeq(rel, string(os.PathSeparator)) {
+ subdir = filepath.Join(subdir, component)
+ path := filepath.Join(req.Root, subdir)
+ if err := os.Mkdir(path, 0o700); err == nil {
+ if err = lchown(path, defaultDirUID, defaultDirGID); err != nil {
+ return fmt.Errorf("copier: put: error setting owner of %q to %d:%d: %w", path, defaultDirUID, defaultDirGID, err)
+ }
+ // make a conditional note to set this directory's permissions
+ // later, but not if we already had an explicitly-provided mode
+ if _, ok := directoryModes[path]; !ok {
+ directoryModes[path] = defaultDirMode
+ }
+ } else {
+ // FreeBSD can return EISDIR for "mkdir /":
+ // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=59739.
+ if !errors.Is(err, os.ErrExist) && !errors.Is(err, syscall.EISDIR) {
+ return fmt.Errorf("copier: put: error checking directory %q: %w", path, err)
+ }
+ }
+ }
+ return nil
+ }
+ makeDirectoryWriteable := func(directory string) error {
+ if _, ok := directoryModes[directory]; !ok {
+ st, err := os.Lstat(directory)
+ if err != nil {
+ return fmt.Errorf("copier: put: error reading permissions of directory %q: %w", directory, err)
+ }
+ mode := st.Mode()
+ directoryModes[directory] = mode
+ }
+ if err := os.Chmod(directory, 0o700); err != nil {
+ return fmt.Errorf("copier: put: error making directory %q writable: %w", directory, err)
+ }
+ return nil
+ }
+ createFile := func(path string, tr *tar.Reader) (int64, error) {
+ f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_EXCL, 0o600)
+ if err != nil && errors.Is(err, os.ErrExist) {
+ if req.PutOptions.NoOverwriteDirNonDir {
+ if st, err2 := os.Lstat(path); err2 == nil && st.IsDir() {
+ return 0, fmt.Errorf("copier: put: error creating file at %q: %w", path, err)
+ }
+ }
+ if err = os.RemoveAll(path); err != nil {
+ if os.IsPermission(err) {
+ if err := makeDirectoryWriteable(filepath.Dir(path)); err != nil {
+ return 0, err
+ }
+ err = os.RemoveAll(path)
+ }
+ if err != nil {
+ return 0, fmt.Errorf("copier: put: error removing item to be overwritten %q: %w", path, err)
+ }
+ }
+ f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_EXCL, 0o600)
+ }
+ if err != nil && os.IsPermission(err) {
+ if err = makeDirectoryWriteable(filepath.Dir(path)); err != nil {
+ return 0, err
+ }
+ f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_EXCL, 0o600)
+ }
+ if err != nil {
+ return 0, fmt.Errorf("copier: put: error opening file %q for writing: %w", path, err)
+ }
+ defer f.Close()
+ n, err := io.Copy(f, tr)
+ if err != nil {
+ return n, fmt.Errorf("copier: put: error writing file %q: %w", path, err)
+ }
+ return n, nil
+ }
+ targetDirectory, err := resolvePath(req.Root, req.Directory, true, nil)
+ if err != nil {
+ return errorResponse("copier: put: error resolving %q: %v", req.Directory, err)
+ }
+ info, err := os.Lstat(targetDirectory)
+ if err == nil {
+ if !info.IsDir() {
+ return errorResponse("copier: put: %s (%s): exists but is not a directory", req.Directory, targetDirectory)
+ }
+ } else {
+ if !errors.Is(err, os.ErrNotExist) {
+ return errorResponse("copier: put: %s: %v", req.Directory, err)
+ }
+ if err := ensureDirectoryUnderRoot(req.Directory); err != nil {
+ return errorResponse("copier: put: %v", err)
+ }
+ }
+ cb := func() error {
+ type directoryAndTimes struct {
+ directory string
+ atime, mtime time.Time
+ }
+ var directoriesAndTimes []directoryAndTimes
+ defer func() {
+ for i := range directoriesAndTimes {
+ directoryAndTimes := directoriesAndTimes[len(directoriesAndTimes)-i-1]
+ if err := lutimes(false, directoryAndTimes.directory, directoryAndTimes.atime, directoryAndTimes.mtime); err != nil {
+ logrus.Debugf("error setting access and modify timestamps on %q to %s and %s: %v", directoryAndTimes.directory, directoryAndTimes.atime, directoryAndTimes.mtime, err)
+ }
+ }
+ for directory, mode := range directoryModes {
+ if err := os.Chmod(directory, mode); err != nil {
+ logrus.Debugf("error setting permissions of %q to 0%o: %v", directory, uint32(mode), err)
+ }
+ }
+ }()
+ ignoredItems := make(map[string]struct{})
+ tr := tar.NewReader(bulkReader)
+ hdr, err := tr.Next()
+ for err == nil {
+ nameBeforeRenaming := hdr.Name
+ if len(hdr.Name) == 0 {
+ // no name -> ignore the entry
+ ignoredItems[nameBeforeRenaming] = struct{}{}
+ hdr, err = tr.Next()
+ continue
+ }
+ if req.PutOptions.Rename != nil {
+ hdr.Name = handleRename(req.PutOptions.Rename, hdr.Name)
+ }
+ // figure out who should own this new item
+ if idMappings != nil && !idMappings.Empty() {
+ containerPair := idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
+ hostPair, err := idMappings.ToHost(containerPair)
+ if err != nil {
+ return fmt.Errorf("mapping container filesystem owner 0,0 to host filesystem owners: %w", err)
+ }
+ hdr.Uid, hdr.Gid = hostPair.UID, hostPair.GID
+ }
+ if hdr.Typeflag == tar.TypeDir {
+ if req.PutOptions.ChownDirs != nil {
+ hdr.Uid, hdr.Gid = dirUID, dirGID
+ }
+ } else {
+ if req.PutOptions.ChownFiles != nil {
+ hdr.Uid, hdr.Gid = *fileUID, *fileGID
+ }
+ }
+ // make sure the parent directory exists, including for tar.TypeXGlobalHeader entries
+ // that we otherwise ignore, because that's what docker build does with them
+ path := filepath.Join(targetDirectory, cleanerReldirectory(filepath.FromSlash(hdr.Name)))
+ if err := ensureDirectoryUnderRoot(filepath.Dir(path)); err != nil {
+ return err
+ }
+ // figure out what the permissions should be
+ if req.PutOptions.StripSetuidBit && hdr.Mode&cISUID == cISUID {
+ hdr.Mode &^= cISUID
+ }
+ if req.PutOptions.StripSetgidBit && hdr.Mode&cISGID == cISGID {
+ hdr.Mode &^= cISGID
+ }
+ if req.PutOptions.StripStickyBit && hdr.Mode&cISVTX == cISVTX {
+ hdr.Mode &^= cISVTX
+ }
+ if hdr.Typeflag == tar.TypeDir {
+ if req.PutOptions.ChmodDirs != nil {
+ hdr.Mode = int64(*req.PutOptions.ChmodDirs)
+ }
+ } else {
+ if req.PutOptions.ChmodFiles != nil {
+ hdr.Mode = int64(*req.PutOptions.ChmodFiles)
+ }
+ }
+ // create the new item
+ devMajor := uint32(hdr.Devmajor)
+ devMinor := uint32(hdr.Devminor)
+ mode := os.FileMode(hdr.Mode) & os.ModePerm
+ switch hdr.Typeflag {
+ // no type flag for sockets
+ default:
+ return fmt.Errorf("unrecognized Typeflag %c", hdr.Typeflag)
+ case tar.TypeReg:
+ var written int64
+ written, err = createFile(path, tr)
+ // only check the length if there wasn't an error, which we'll
+ // check along with errors for other types of entries
+ if err == nil && written != hdr.Size {
+ return fmt.Errorf("copier: put: error creating regular file %q: incorrect length (%d != %d)", path, written, hdr.Size)
+ }
+ case tar.TypeLink:
+ var linkTarget string
+ if _, ignoredTarget := ignoredItems[hdr.Linkname]; ignoredTarget {
+ // hard link to an ignored item: skip this, too
+ ignoredItems[nameBeforeRenaming] = struct{}{}
+ goto nextHeader
+ }
+ if req.PutOptions.Rename != nil {
+ hdr.Linkname = handleRename(req.PutOptions.Rename, hdr.Linkname)
+ }
+ if linkTarget, err = resolvePath(targetDirectory, filepath.Join(req.Root, filepath.FromSlash(hdr.Linkname)), true, nil); err != nil {
+ return fmt.Errorf("resolving hardlink target path %q under root %q", hdr.Linkname, req.Root)
+ }
+ if err = os.Link(linkTarget, path); err != nil && errors.Is(err, os.ErrExist) {
+ if req.PutOptions.NoOverwriteDirNonDir {
+ if st, err := os.Lstat(path); err == nil && st.IsDir() {
+ break
+ }
+ }
+ if err = os.RemoveAll(path); err == nil {
+ err = os.Link(linkTarget, path)
+ }
+ }
+ case tar.TypeSymlink:
+ // if req.PutOptions.Rename != nil {
+ // todo: the general solution requires resolving to an absolute path, handling
+ // renaming, and then possibly converting back to a relative symlink
+ // }
+ if err = os.Symlink(filepath.FromSlash(hdr.Linkname), filepath.FromSlash(path)); err != nil && errors.Is(err, os.ErrExist) {
+ if req.PutOptions.NoOverwriteDirNonDir {
+ if st, err := os.Lstat(path); err == nil && st.IsDir() {
+ break
+ }
+ }
+ if err = os.RemoveAll(path); err == nil {
+ err = os.Symlink(filepath.FromSlash(hdr.Linkname), filepath.FromSlash(path))
+ }
+ }
+ case tar.TypeChar:
+ if req.PutOptions.IgnoreDevices {
+ ignoredItems[nameBeforeRenaming] = struct{}{}
+ goto nextHeader
+ }
+ if err = mknod(path, chrMode(0o600), int(mkdev(devMajor, devMinor))); err != nil && errors.Is(err, os.ErrExist) {
+ if req.PutOptions.NoOverwriteDirNonDir {
+ if st, err := os.Lstat(path); err == nil && st.IsDir() {
+ break
+ }
+ }
+ if err = os.RemoveAll(path); err == nil {
+ err = mknod(path, chrMode(0o600), int(mkdev(devMajor, devMinor)))
+ }
+ }
+ case tar.TypeBlock:
+ if req.PutOptions.IgnoreDevices {
+ ignoredItems[nameBeforeRenaming] = struct{}{}
+ goto nextHeader
+ }
+ if err = mknod(path, blkMode(0o600), int(mkdev(devMajor, devMinor))); err != nil && errors.Is(err, os.ErrExist) {
+ if req.PutOptions.NoOverwriteDirNonDir {
+ if st, err := os.Lstat(path); err == nil && st.IsDir() {
+ break
+ }
+ }
+ if err = os.RemoveAll(path); err == nil {
+ err = mknod(path, blkMode(0o600), int(mkdev(devMajor, devMinor)))
+ }
+ }
+ case tar.TypeDir:
+ // FreeBSD can return EISDIR for "mkdir /":
+ // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=59739.
+ if err = os.Mkdir(path, 0o700); err != nil && (errors.Is(err, os.ErrExist) || errors.Is(err, syscall.EISDIR)) {
+ if st, stErr := os.Lstat(path); stErr == nil && !st.IsDir() {
+ if req.PutOptions.NoOverwriteNonDirDir {
+ break
+ }
+ if err = os.Remove(path); err == nil {
+ err = os.Mkdir(path, 0o700)
+ }
+ } else {
+ err = stErr
+ }
+ // either we removed it and retried, or it was a directory,
+ // in which case we want to just add the new stuff under it
+ }
+ // make a note of the directory's times. we
+ // might create items under it, which will
+ // cause the mtime to change after we correct
+ // it, so we'll need to correct it again later
+ directoriesAndTimes = append(directoriesAndTimes, directoryAndTimes{
+ directory: path,
+ atime: hdr.AccessTime,
+ mtime: hdr.ModTime,
+ })
+ // set the mode here unconditionally, in case the directory is in
+ // the archive more than once for whatever reason
+ directoryModes[path] = mode
+ case tar.TypeFifo:
+ if err = mkfifo(path, 0o600); err != nil && errors.Is(err, os.ErrExist) {
+ if req.PutOptions.NoOverwriteDirNonDir {
+ if st, err := os.Lstat(path); err == nil && st.IsDir() {
+ break
+ }
+ }
+ if err = os.RemoveAll(path); err == nil {
+ err = mkfifo(path, 0o600)
+ }
+ }
+ case tar.TypeXGlobalHeader:
+ // Per archive/tar, PAX uses these to specify key=value information
+ // applies to all subsequent entries. The one in reported in #2717,
+ // https://www.openssl.org/source/openssl-1.1.1g.tar.gz, includes a
+ // comment=(40 byte hex string) at the start, possibly a digest.
+ // Don't try to create whatever path was used for the header.
+ goto nextHeader
+ }
+ // check for errors
+ if err != nil {
+ return fmt.Errorf("copier: put: error creating %q: %w", path, err)
+ }
+ // set ownership
+ if err = lchown(path, hdr.Uid, hdr.Gid); err != nil {
+ return fmt.Errorf("copier: put: error setting ownership of %q to %d:%d: %w", path, hdr.Uid, hdr.Gid, err)
+ }
+ // set permissions, except for symlinks, since we don't
+ // have an lchmod, and directories, which we'll fix up
+ // on our way out so that we don't get tripped up by
+ // directories which we're not supposed to be able to
+ // write to, but which we'll need to create content in
+ if hdr.Typeflag != tar.TypeSymlink && hdr.Typeflag != tar.TypeDir {
+ if err = os.Chmod(path, mode); err != nil {
+ return fmt.Errorf("copier: put: error setting permissions on %q to 0%o: %w", path, mode, err)
+ }
+ }
+ // set other bits that might have been reset by chown()
+ if hdr.Typeflag != tar.TypeSymlink {
+ if hdr.Mode&cISUID == cISUID {
+ mode |= os.ModeSetuid
+ }
+ if hdr.Mode&cISGID == cISGID {
+ mode |= os.ModeSetgid
+ }
+ if hdr.Mode&cISVTX == cISVTX {
+ mode |= os.ModeSticky
+ }
+ if hdr.Typeflag == tar.TypeDir {
+ // if/when we do the final setting of permissions on this
+ // directory, make sure to incorporate these bits, too
+ directoryModes[path] = mode
+ }
+ if err = os.Chmod(path, mode); err != nil {
+ return fmt.Errorf("copier: put: setting additional permissions on %q to 0%o: %w", path, mode, err)
+ }
+ }
+ // set xattrs, including some that might have been reset by chown()
+ if !req.PutOptions.StripXattrs {
+ xattrs := mapWithPrefixedKeysWithoutKeyPrefix(hdr.PAXRecords, xattrPAXRecordNamespace)
+ if err = Lsetxattrs(path, xattrs); err != nil {
+ if !req.PutOptions.IgnoreXattrErrors {
+ return fmt.Errorf("copier: put: error setting extended attributes on %q: %w", path, err)
+ }
+ }
+ }
+ // set time
+ if hdr.AccessTime.IsZero() || hdr.AccessTime.Before(hdr.ModTime) {
+ hdr.AccessTime = hdr.ModTime
+ }
+ if err = lutimes(hdr.Typeflag == tar.TypeSymlink, path, hdr.AccessTime, hdr.ModTime); err != nil {
+ return fmt.Errorf("setting access and modify timestamps on %q to %s and %s: %w", path, hdr.AccessTime, hdr.ModTime, err)
+ }
+ // set fflags if supported
+ if err := archive.WriteFileFlagsFromTarHeader(path, hdr); err != nil {
+ return fmt.Errorf("copier: put: error setting fflags on %q: %w", path, err)
+ }
+ nextHeader:
+ hdr, err = tr.Next()
+ }
+ if err != io.EOF {
+ return fmt.Errorf("reading tar stream: expected EOF: %w", err)
+ }
+ return nil
+ }
+ return &response{Error: "", Put: putResponse{}}, cb, nil
+}
+
+func copierHandlerMkdir(req request, idMappings *idtools.IDMappings) (*response, func() error, error) {
+ errorResponse := func(fmtspec string, args ...any) (*response, func() error, error) { //nolint:unparam
+ return &response{Error: fmt.Sprintf(fmtspec, args...), Mkdir: mkdirResponse{}}, nil, nil
+ }
+ dirUID, dirGID := 0, 0
+ if req.MkdirOptions.ChownNew != nil {
+ dirUID, dirGID = req.MkdirOptions.ChownNew.UID, req.MkdirOptions.ChownNew.GID
+ }
+ dirMode := os.FileMode(0o755)
+ if req.MkdirOptions.ChmodNew != nil {
+ dirMode = *req.MkdirOptions.ChmodNew
+ }
+ if idMappings != nil && !idMappings.Empty() {
+ containerDirPair := idtools.IDPair{UID: dirUID, GID: dirGID}
+ hostDirPair, err := idMappings.ToHost(containerDirPair)
+ if err != nil {
+ return errorResponse("copier: mkdir: error mapping container filesystem owner %d:%d to host filesystem owners: %v", dirUID, dirGID, err)
+ }
+ dirUID, dirGID = hostDirPair.UID, hostDirPair.GID
+ }
+
+ directory, err := resolvePath(req.Root, req.Directory, true, nil)
+ if err != nil {
+ return errorResponse("copier: mkdir: error resolving %q: %v", req.Directory, err)
+ }
+
+ rel, err := convertToRelSubdirectory(req.Root, directory)
+ if err != nil {
+ return errorResponse("copier: mkdir: error computing path of %q relative to %q: %v", directory, req.Root, err)
+ }
+
+ subdir := ""
+ var created []string
+ for component := range strings.SplitSeq(rel, string(os.PathSeparator)) {
+ subdir = filepath.Join(subdir, component)
+ path := filepath.Join(req.Root, subdir)
+ if err := os.Mkdir(path, 0o700); err == nil {
+ if err = chown(path, dirUID, dirGID); err != nil {
+ return errorResponse("copier: mkdir: error setting owner of %q to %d:%d: %v", path, dirUID, dirGID, err)
+ }
+ if err = chmod(path, dirMode); err != nil {
+ return errorResponse("copier: mkdir: error setting permissions on %q to 0%o: %v", path, dirMode)
+ }
+ created = append(created, path)
+ } else {
+ // FreeBSD can return EISDIR for "mkdir /":
+ // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=59739.
+ if !errors.Is(err, os.ErrExist) && !errors.Is(err, syscall.EISDIR) {
+ return errorResponse("copier: mkdir: error checking directory %q: %v", path, err)
+ }
+ }
+ }
+ // set timestamps last, in case we needed to create some nested directories, which would
+ // update the timestamps on directories that we'd just set timestamps on, if we had done
+ // that immediately
+ if req.MkdirOptions.ModTimeNew != nil {
+ when := *req.MkdirOptions.ModTimeNew
+ for _, newDirectory := range created {
+ if err = lutimes(false, newDirectory, when, when); err != nil {
+ return errorResponse("copier: mkdir: error setting datestamp on %q: %v", newDirectory, err)
+ }
+ }
+ }
+
+ return &response{Error: "", Mkdir: mkdirResponse{}}, nil, nil
+}
+
+func copierHandlerRemove(req request) *response {
+ errorResponse := func(fmtspec string, args ...any) *response {
+ return &response{Error: fmt.Sprintf(fmtspec, args...), Remove: removeResponse{}}
+ }
+ resolvedTarget, err := resolvePath(req.Root, req.Directory, false, nil)
+ if err != nil {
+ return errorResponse("copier: remove: %v", err)
+ }
+ if req.RemoveOptions.All {
+ err = os.RemoveAll(resolvedTarget)
+ } else {
+ err = os.Remove(resolvedTarget)
+ }
+ if err != nil {
+ return errorResponse("copier: remove %q: %v", req.Directory, err)
+ }
+ return &response{Error: "", Remove: removeResponse{}}
+}
+
+// EnsurePath is a single item being passed to an Ensure() call.
+type EnsurePath struct {
+ Path string // a pathname, relative to the Directory, possibly relative to the root
+ Typeflag byte // can be either TypeReg or TypeDir, everything else is currently ignored
+ ModTime *time.Time // mtime to set on newly-created items, default is to leave them be
+ Chmod *os.FileMode // mode, defaults to 000 for files and 700 for directories
+ Chown *idtools.IDPair // owner settings to set on newly-created items, defaults to 0:0
+}
+
+// EnsureOptions controls parts of Ensure()'s behavior.
+type EnsureOptions struct {
+ UIDMap, GIDMap []idtools.IDMap // map from containerIDs to hostIDs in the chroot
+ Paths []EnsurePath
+}
+
+// EnsureParentPath is a parent (or grandparent, or...) directory of an item
+// created by Ensure(), along with information about it, from before the item
+// in question was created. If the information about this directory hasn't
+// changed when commit-time rolls around, it's most likely that this directory
+// is only being considered for inclusion in the layer because it was pulled
+// up, and it was not actually changed.
+type EnsureParentPath = ConditionalRemovePath
+
+// Ensure ensures that the specified mount point targets exist under the root.
+// If the root directory is not specified, the current root directory is used.
+// If root is specified and the current OS supports it, and the calling process
+// has the necessary privileges, the operation is performed in a chrooted
+// context.
+// Returns a slice with the pathnames of items that needed to be created and a
+// slice of affected parent directories and information about them.
+func Ensure(root, directory string, options EnsureOptions) ([]string, []EnsureParentPath, error) {
+ req := request{
+ Request: requestEnsure,
+ Root: root,
+ Directory: directory,
+ EnsureOptions: options,
+ }
+ resp, err := copier(nil, nil, req)
+ if err != nil {
+ return nil, nil, err
+ }
+ if resp.Error != "" {
+ return nil, nil, errors.New(resp.Error)
+ }
+ return resp.Ensure.Created, resp.Ensure.Noted, nil
+}
+
+func copierHandlerEnsure(req request, idMappings *idtools.IDMappings) *response {
+ errorResponse := func(fmtspec string, args ...any) *response {
+ return &response{Error: fmt.Sprintf(fmtspec, args...), Ensure: ensureResponse{}}
+ }
+ slices.SortFunc(req.EnsureOptions.Paths, func(a, b EnsurePath) int { return strings.Compare(a.Path, b.Path) })
+ var created []string
+ notedByName := map[string]EnsureParentPath{}
+ for _, item := range req.EnsureOptions.Paths {
+ uid, gid := 0, 0
+ if item.Chown != nil {
+ uid, gid = item.Chown.UID, item.Chown.UID
+ }
+ var mode os.FileMode
+ switch item.Typeflag {
+ case tar.TypeReg:
+ mode = 0o000
+ case tar.TypeDir:
+ mode = 0o700
+ default:
+ continue
+ }
+ if item.Chmod != nil {
+ mode = *item.Chmod
+ }
+ if idMappings != nil && !idMappings.Empty() {
+ containerDirPair := idtools.IDPair{UID: uid, GID: gid}
+ hostDirPair, err := idMappings.ToHost(containerDirPair)
+ if err != nil {
+ return errorResponse("copier: ensure: error mapping container filesystem owner %d:%d to host filesystem owners: %v", uid, gid, err)
+ }
+ uid, gid = hostDirPair.UID, hostDirPair.GID
+ }
+ directory, err := resolvePath(req.Root, req.Directory, true, nil)
+ if err != nil {
+ return errorResponse("copier: ensure: error resolving %q: %v", req.Directory, err)
+ }
+
+ rel, err := convertToRelSubdirectory(req.Root, directory)
+ if err != nil {
+ return errorResponse("copier: ensure: error computing path of %q relative to %q: %v", directory, req.Root, err)
+ }
+
+ subdir := ""
+ components := strings.Split(filepath.Join(rel, item.Path), string(os.PathSeparator))
+ components = slices.DeleteFunc(components, func(s string) bool { return s == "" || s == "." })
+ for i, component := range components {
+ parentPath := subdir
+ if parentPath == "" {
+ parentPath = "."
+ }
+ leaf := filepath.Join(parentPath, component)
+ parentInfo, err := os.Stat(filepath.Join(req.Root, parentPath))
+ if err != nil {
+ return errorResponse("copier: ensure: checking datestamps on %q (%d: %v): %v", parentPath, i, components, err)
+ }
+ if parentPath != "." {
+ parentModTime := parentInfo.ModTime().UTC()
+ parentMode := parentInfo.Mode()
+ uid, gid, err := owner(parentInfo)
+ if err != nil {
+ return errorResponse("copier: ensure: error reading owner of %q: %v", parentPath, err)
+ }
+ notedByName[parentPath] = EnsureParentPath{
+ Path: parentPath,
+ ModTime: &parentModTime,
+ Mode: &parentMode,
+ Owner: &idtools.IDPair{UID: uid, GID: gid},
+ }
+ }
+ if i < len(components)-1 || item.Typeflag == tar.TypeDir {
+ err = os.Mkdir(filepath.Join(req.Root, leaf), mode)
+ subdir = leaf
+ } else if item.Typeflag == tar.TypeReg {
+ var f *os.File
+ if f, err = os.OpenFile(filepath.Join(req.Root, leaf), os.O_CREATE|os.O_EXCL|os.O_RDWR, mode); err == nil {
+ f.Close()
+ }
+ } else {
+ continue
+ }
+ if err == nil {
+ createdLeaf := leaf
+ if len(createdLeaf) > 1 {
+ createdLeaf = strings.TrimPrefix(createdLeaf, string(os.PathSeparator))
+ }
+ created = append(created, createdLeaf)
+ if err = chown(filepath.Join(req.Root, leaf), uid, uid); err != nil {
+ return errorResponse("copier: ensure: error setting owner of %q to %d:%d: %v", leaf, uid, gid, err)
+ }
+ if err = chmod(filepath.Join(req.Root, leaf), mode); err != nil {
+ return errorResponse("copier: ensure: error setting permissions on %q to 0%o: %v", leaf, mode)
+ }
+ if item.ModTime != nil {
+ if err := os.Chtimes(filepath.Join(req.Root, leaf), *item.ModTime, *item.ModTime); err != nil {
+ return errorResponse("copier: ensure: resetting datestamp on %q: %v", leaf, err)
+ }
+ }
+ } else {
+ // FreeBSD can return EISDIR for "mkdir /":
+ // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=59739.
+ if !errors.Is(err, os.ErrExist) && !errors.Is(err, syscall.EISDIR) {
+ return errorResponse("copier: ensure: error checking item %q: %v", leaf, err)
+ }
+ }
+ if err := os.Chtimes(filepath.Join(req.Root, parentPath), parentInfo.ModTime(), parentInfo.ModTime()); err != nil {
+ return errorResponse("copier: ensure: resetting datestamp on %q: %v", parentPath, err)
+ }
+ }
+ }
+ slices.Sort(created)
+ noted := make([]EnsureParentPath, 0, len(notedByName))
+ for _, n := range notedByName {
+ if slices.Contains(created, n.Path) {
+ continue
+ }
+ noted = append(noted, n)
+ }
+ slices.SortFunc(noted, func(a, b EnsureParentPath) int { return strings.Compare(a.Path, b.Path) })
+ return &response{Error: "", Ensure: ensureResponse{Created: created, Noted: noted}}
+}
+
+// ConditionalRemovePath is a single item being passed to an ConditionalRemove() call.
+type ConditionalRemovePath struct {
+ Path string // a pathname, relative to the Directory, possibly relative to the root
+ ModTime *time.Time // mtime to expect this item to have, if it's a condition
+ Mode *os.FileMode // mode to expect this item to have, if it's a condition
+ Owner *idtools.IDPair // owner to expect this item to have, if it's a condition
+}
+
+// ConditionalRemoveOptions controls parts of ConditionalRemove()'s behavior.
+type ConditionalRemoveOptions struct {
+ UIDMap, GIDMap []idtools.IDMap // map from containerIDs to hostIDs in the chroot
+ Paths []ConditionalRemovePath
+}
+
+// ConditionalRemove removes the set of named items if they're present and
+// currently match the additional conditions, returning the list of items it
+// removed. Directories will also only be removed if they have no contents,
+// and will be left in place otherwise.
+func ConditionalRemove(root, directory string, options ConditionalRemoveOptions) ([]string, error) {
+ req := request{
+ Request: requestConditionalRemove,
+ Root: root,
+ Directory: directory,
+ ConditionalRemoveOptions: options,
+ }
+ resp, err := copier(nil, nil, req)
+ if err != nil {
+ return nil, err
+ }
+ if resp.Error != "" {
+ return nil, errors.New(resp.Error)
+ }
+ return resp.ConditionalRemove.Removed, nil
+}
+
+func copierHandlerConditionalRemove(req request, idMappings *idtools.IDMappings) *response {
+ errorResponse := func(fmtspec string, args ...any) *response {
+ return &response{Error: fmt.Sprintf(fmtspec, args...), ConditionalRemove: conditionalRemoveResponse{}}
+ }
+ slices.SortFunc(req.ConditionalRemoveOptions.Paths, func(a, b ConditionalRemovePath) int { return strings.Compare(b.Path, a.Path) })
+ var removed []string
+ for _, item := range req.ConditionalRemoveOptions.Paths {
+ uid, gid := 0, 0
+ if item.Owner != nil {
+ uid, gid = item.Owner.UID, item.Owner.GID
+ }
+ if idMappings != nil && !idMappings.Empty() {
+ containerDirPair := idtools.IDPair{UID: uid, GID: gid}
+ hostDirPair, err := idMappings.ToHost(containerDirPair)
+ if err != nil {
+ return errorResponse("copier: conditionalRemove: error mapping container filesystem owner %d:%d to host filesystem owners: %v", uid, gid, err)
+ }
+ uid, gid = hostDirPair.UID, hostDirPair.GID
+ }
+ directory, err := resolvePath(req.Root, req.Directory, true, nil)
+ if err != nil {
+ return errorResponse("copier: conditionalRemove: error resolving %q: %v", req.Directory, err)
+ }
+
+ rel, err := convertToRelSubdirectory(req.Root, directory)
+ if err != nil {
+ return errorResponse("copier: conditionalRemove: error computing path of %q relative to %q: %v", directory, req.Root, err)
+ }
+
+ components := strings.Split(filepath.Join(rel, item.Path), string(os.PathSeparator))
+ components = slices.DeleteFunc(components, func(s string) bool { return s == "" || s == "." })
+ if len(components) == 0 {
+ continue
+ }
+ itemPath := filepath.Join(append([]string{req.Root}, components...)...)
+ itemInfo, err := os.Lstat(itemPath)
+ if err != nil {
+ if !errors.Is(err, os.ErrNotExist) {
+ return errorResponse("copier: conditionalRemove: checking on candidate %q: %v", itemPath, err)
+ }
+ // okay?
+ removed = append(removed, item.Path)
+ continue
+ }
+ parentPath := filepath.Dir(itemPath)
+ parentInfo, err := os.Stat(parentPath)
+ if err != nil {
+ return errorResponse("copier: conditionalRemove: checking on parent directory %q: %v", parentPath, err)
+ }
+
+ if item.Mode != nil && itemInfo.Mode().Perm()&fs.ModePerm != *item.Mode&fs.ModePerm {
+ // mismatch, modified? ignore
+ continue
+ }
+ if item.ModTime != nil && !item.ModTime.Equal(itemInfo.ModTime()) {
+ // mismatch, modified? ignore
+ continue
+ }
+ if item.Owner != nil {
+ ownerUID, ownerGID, err := owner(itemInfo)
+ if err != nil {
+ return errorResponse("copier: conditionalRemove: checking ownership of %q: %v", itemPath, err)
+ }
+ if uid != ownerUID || gid != ownerGID {
+ // mismatch, modified? ignore
+ continue
+ }
+ }
+ if err := os.Remove(itemPath); err != nil && !errors.Is(err, os.ErrNotExist) {
+ if !errors.Is(err, syscall.EEXIST) && !errors.Is(err, syscall.ENOTEMPTY) {
+ return errorResponse("copier: conditionalRemove: removing %q: %v", itemPath, err)
+ }
+ // okay? not removed, but it wasn't empty, so okay?
+ continue
+ }
+ removed = append(removed, item.Path)
+ if err := os.Chtimes(parentPath, parentInfo.ModTime(), parentInfo.ModTime()); err != nil {
+ return errorResponse("copier: conditionalRemove: resetting datestamp on %q: %v", parentPath, err)
+ }
+ }
+ slices.Sort(removed)
+ return &response{Error: "", ConditionalRemove: conditionalRemoveResponse{Removed: removed}}
+}
diff --git a/vendor/github.com/containers/buildah/copier/hardlink_not_uint64.go b/vendor/github.com/containers/buildah/copier/hardlink_not_uint64.go
new file mode 100644
index 00000000..cee34aca
--- /dev/null
+++ b/vendor/github.com/containers/buildah/copier/hardlink_not_uint64.go
@@ -0,0 +1,14 @@
+//go:build darwin || (linux && mips) || (linux && mipsle) || (linux && mips64) || (linux && mips64le)
+
+package copier
+
+import (
+ "syscall"
+)
+
+func makeHardlinkDeviceAndInode(st *syscall.Stat_t) hardlinkDeviceAndInode {
+ return hardlinkDeviceAndInode{
+ device: uint64(st.Dev),
+ inode: uint64(st.Ino),
+ }
+}
diff --git a/vendor/github.com/containers/buildah/copier/hardlink_uint64.go b/vendor/github.com/containers/buildah/copier/hardlink_uint64.go
new file mode 100644
index 00000000..e88e1899
--- /dev/null
+++ b/vendor/github.com/containers/buildah/copier/hardlink_uint64.go
@@ -0,0 +1,14 @@
+//go:build (linux && !mips && !mipsle && !mips64 && !mips64le) || freebsd || netbsd
+
+package copier
+
+import (
+ "syscall"
+)
+
+func makeHardlinkDeviceAndInode(st *syscall.Stat_t) hardlinkDeviceAndInode {
+ return hardlinkDeviceAndInode{
+ device: st.Dev,
+ inode: st.Ino,
+ }
+}
diff --git a/vendor/github.com/containers/buildah/copier/hardlink_unix.go b/vendor/github.com/containers/buildah/copier/hardlink_unix.go
new file mode 100644
index 00000000..f4d45fe9
--- /dev/null
+++ b/vendor/github.com/containers/buildah/copier/hardlink_unix.go
@@ -0,0 +1,32 @@
+//go:build !windows
+
+package copier
+
+import (
+ "os"
+ "sync"
+ "syscall"
+)
+
+type hardlinkDeviceAndInode struct {
+ device, inode uint64
+}
+
+type hardlinkChecker struct {
+ hardlinks sync.Map
+}
+
+func (h *hardlinkChecker) Check(fi os.FileInfo) string {
+ if st, ok := fi.Sys().(*syscall.Stat_t); ok && fi.Mode().IsRegular() && st.Nlink > 1 {
+ if name, ok := h.hardlinks.Load(makeHardlinkDeviceAndInode(st)); ok && name.(string) != "" {
+ return name.(string)
+ }
+ }
+ return ""
+}
+
+func (h *hardlinkChecker) Add(fi os.FileInfo, name string) {
+ if st, ok := fi.Sys().(*syscall.Stat_t); ok && fi.Mode().IsRegular() && st.Nlink > 1 {
+ h.hardlinks.Store(makeHardlinkDeviceAndInode(st), name)
+ }
+}
diff --git a/vendor/github.com/containers/buildah/copier/hardlink_windows.go b/vendor/github.com/containers/buildah/copier/hardlink_windows.go
new file mode 100644
index 00000000..c2c44458
--- /dev/null
+++ b/vendor/github.com/containers/buildah/copier/hardlink_windows.go
@@ -0,0 +1,16 @@
+//go:build !linux && !darwin
+
+package copier
+
+import (
+ "os"
+)
+
+type hardlinkChecker struct{}
+
+func (h *hardlinkChecker) Check(fi os.FileInfo) string {
+ return ""
+}
+
+func (h *hardlinkChecker) Add(fi os.FileInfo, name string) {
+}
diff --git a/vendor/github.com/containers/buildah/copier/mknod_int.go b/vendor/github.com/containers/buildah/copier/mknod_int.go
new file mode 100644
index 00000000..e23e7171
--- /dev/null
+++ b/vendor/github.com/containers/buildah/copier/mknod_int.go
@@ -0,0 +1,11 @@
+//go:build !windows && !freebsd
+
+package copier
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+func mknod(path string, mode uint32, dev int) error {
+ return unix.Mknod(path, mode, dev)
+}
diff --git a/vendor/github.com/containers/buildah/copier/mknod_uint64.go b/vendor/github.com/containers/buildah/copier/mknod_uint64.go
new file mode 100644
index 00000000..10bc4c2e
--- /dev/null
+++ b/vendor/github.com/containers/buildah/copier/mknod_uint64.go
@@ -0,0 +1,11 @@
+//go:build freebsd
+
+package copier
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+func mknod(path string, mode uint32, dev int) error {
+ return unix.Mknod(path, mode, uint64(dev))
+}
diff --git a/vendor/github.com/containers/buildah/copier/syscall_unix.go b/vendor/github.com/containers/buildah/copier/syscall_unix.go
new file mode 100644
index 00000000..ccde63ed
--- /dev/null
+++ b/vendor/github.com/containers/buildah/copier/syscall_unix.go
@@ -0,0 +1,93 @@
+//go:build !windows
+
+package copier
+
+import (
+ "fmt"
+ "os"
+ "syscall"
+ "time"
+
+ "golang.org/x/sys/unix"
+)
+
+var canChroot = os.Getuid() == 0
+
+func chroot(root string) (bool, error) {
+ if canChroot {
+ if err := os.Chdir(root); err != nil {
+ return false, fmt.Errorf("changing to intended-new-root directory %q: %w", root, err)
+ }
+ if err := unix.Chroot(root); err != nil {
+ return false, fmt.Errorf("chrooting to directory %q: %w", root, err)
+ }
+ if err := os.Chdir(string(os.PathSeparator)); err != nil {
+ return false, fmt.Errorf("changing to just-became-root directory %q: %w", root, err)
+ }
+ return true, nil
+ }
+ return false, nil
+}
+
+func chrMode(mode os.FileMode) uint32 {
+ return uint32(unix.S_IFCHR | mode)
+}
+
+func blkMode(mode os.FileMode) uint32 {
+ return uint32(unix.S_IFBLK | mode)
+}
+
+func mkdev(major, minor uint32) uint64 {
+ return unix.Mkdev(major, minor)
+}
+
+func mkfifo(path string, mode uint32) error {
+ return unix.Mkfifo(path, mode)
+}
+
+func chmod(path string, mode os.FileMode) error {
+ return os.Chmod(path, mode)
+}
+
+func chown(path string, uid, gid int) error {
+ return os.Chown(path, uid, gid)
+}
+
+func lchown(path string, uid, gid int) error {
+ return os.Lchown(path, uid, gid)
+}
+
+func lutimes(_ bool, path string, atime, mtime time.Time) error {
+ if atime.IsZero() || mtime.IsZero() {
+ now := time.Now()
+ if atime.IsZero() {
+ atime = now
+ }
+ if mtime.IsZero() {
+ mtime = now
+ }
+ }
+ return unix.Lutimes(path, []unix.Timeval{unix.NsecToTimeval(atime.UnixNano()), unix.NsecToTimeval(mtime.UnixNano())})
+}
+
+func owner(info os.FileInfo) (int, int, error) {
+ if st, ok := info.Sys().(*syscall.Stat_t); ok {
+ return int(st.Uid), int(st.Gid), nil
+ }
+ return -1, -1, syscall.ENOSYS
+}
+
+// sameDevice returns true unless we're sure that they're not on the same device
+func sameDevice(a, b os.FileInfo) bool {
+ aSys := a.Sys()
+ bSys := b.Sys()
+ if aSys == nil || bSys == nil {
+ return true
+ }
+ uA, okA := aSys.(*syscall.Stat_t)
+ uB, okB := bSys.(*syscall.Stat_t)
+ if !okA || !okB {
+ return true
+ }
+ return uA.Dev == uB.Dev
+}
diff --git a/vendor/github.com/containers/buildah/copier/syscall_windows.go b/vendor/github.com/containers/buildah/copier/syscall_windows.go
new file mode 100644
index 00000000..f5fd62b9
--- /dev/null
+++ b/vendor/github.com/containers/buildah/copier/syscall_windows.go
@@ -0,0 +1,87 @@
+//go:build windows
+
+package copier
+
+import (
+ "errors"
+ "os"
+ "syscall"
+ "time"
+
+ "golang.org/x/sys/windows"
+)
+
+var canChroot = false
+
+func chroot(path string) (bool, error) {
+ return false, nil
+}
+
+func chrMode(mode os.FileMode) uint32 {
+ return windows.S_IFCHR | uint32(mode)
+}
+
+func blkMode(mode os.FileMode) uint32 {
+ return windows.S_IFBLK | uint32(mode)
+}
+
+func mkdev(major, minor uint32) uint64 {
+ return 0
+}
+
+func mkfifo(path string, mode uint32) error {
+ return syscall.ENOSYS
+}
+
+func mknod(path string, mode uint32, dev int) error {
+ return syscall.ENOSYS
+}
+
+func chmod(path string, mode os.FileMode) error {
+ err := os.Chmod(path, mode)
+ if err != nil && errors.Is(err, syscall.EWINDOWS) {
+ return nil
+ }
+ return err
+}
+
+func chown(path string, uid, gid int) error {
+ err := os.Chown(path, uid, gid)
+ if err != nil && errors.Is(err, syscall.EWINDOWS) {
+ return nil
+ }
+ return err
+}
+
+func lchown(path string, uid, gid int) error {
+ err := os.Lchown(path, uid, gid)
+ if err != nil && errors.Is(err, syscall.EWINDOWS) {
+ return nil
+ }
+ return err
+}
+
+func lutimes(isSymlink bool, path string, atime, mtime time.Time) error {
+ if isSymlink {
+ return nil
+ }
+ if atime.IsZero() || mtime.IsZero() {
+ now := time.Now()
+ if atime.IsZero() {
+ atime = now
+ }
+ if mtime.IsZero() {
+ mtime = now
+ }
+ }
+ return windows.UtimesNano(path, []windows.Timespec{windows.NsecToTimespec(atime.UnixNano()), windows.NsecToTimespec(mtime.UnixNano())})
+}
+
+func owner(info os.FileInfo) (int, int, error) {
+ return -1, -1, syscall.ENOSYS
+}
+
+// sameDevice returns true since we can't be sure that they're not on the same device
+func sameDevice(a, b os.FileInfo) bool {
+ return true
+}
diff --git a/vendor/github.com/containers/buildah/copier/xattrs.go b/vendor/github.com/containers/buildah/copier/xattrs.go
new file mode 100644
index 00000000..040a6603
--- /dev/null
+++ b/vendor/github.com/containers/buildah/copier/xattrs.go
@@ -0,0 +1,115 @@
+//go:build linux || netbsd || freebsd || darwin
+
+package copier
+
+import (
+ "errors"
+ "fmt"
+ "path/filepath"
+ "strings"
+ "syscall"
+
+ "github.com/sirupsen/logrus"
+ "go.podman.io/storage/pkg/unshare"
+ "golang.org/x/sys/unix"
+)
+
+const (
+ xattrsSupported = true
+ imaXattr = "security.ima"
+)
+
+var (
+ relevantAttributes = []string{"security.capability", imaXattr, "user.*"} // the attributes that we preserve - we discard others
+ irrelevantAttributes = []string{"user.overlay.*"} // the attributes that we discard, even from the relevantAttributes list
+ initialXattrListSize = 64 * 1024
+ initialXattrValueSize = 64 * 1024
+)
+
+// isRelevantXattr checks if "attribute" matches one of the attribute patterns
+// listed in the "relevantAttributes" list.
+func isRelevantXattr(attribute string) bool {
+ for _, relevant := range relevantAttributes {
+ matched, err := filepath.Match(relevant, attribute)
+ if err != nil || !matched {
+ continue
+ }
+ for _, irrelevant := range irrelevantAttributes {
+ matched, err := filepath.Match(irrelevant, attribute)
+ if err != nil || !matched {
+ continue
+ }
+ return false
+ }
+ return true
+ }
+ return false
+}
+
+// Lgetxattrs returns a map of the relevant extended attributes set on the given file.
+func Lgetxattrs(path string) (map[string]string, error) {
+ maxSize := 64 * 1024 * 1024
+ listSize := initialXattrListSize
+ var list []byte
+ for listSize < maxSize {
+ list = make([]byte, listSize)
+ size, err := unix.Llistxattr(path, list)
+ if err != nil {
+ if errors.Is(err, syscall.ERANGE) {
+ listSize *= 2
+ continue
+ }
+ if errors.Is(err, syscall.ENOTSUP) || errors.Is(err, syscall.ENOSYS) {
+ // treat these errors listing xattrs as equivalent to "no xattrs"
+ list = list[:0]
+ break
+ }
+ return nil, fmt.Errorf("listing extended attributes of %q: %w", path, err)
+ }
+ list = list[:size]
+ break
+ }
+ if listSize >= maxSize {
+ return nil, fmt.Errorf("unable to read list of attributes for %q: size would have been too big", path)
+ }
+ m := make(map[string]string)
+ for attribute := range strings.SplitSeq(string(list), string('\000')) {
+ if isRelevantXattr(attribute) {
+ attributeSize := initialXattrValueSize
+ var attributeValue []byte
+ for attributeSize < maxSize {
+ attributeValue = make([]byte, attributeSize)
+ size, err := unix.Lgetxattr(path, attribute, attributeValue)
+ if err != nil {
+ if errors.Is(err, syscall.ERANGE) {
+ attributeSize *= 2
+ continue
+ }
+ return nil, fmt.Errorf("getting value of extended attribute %q on %q: %w", attribute, path, err)
+ }
+ m[attribute] = string(attributeValue[:size])
+ break
+ }
+ if attributeSize >= maxSize {
+ return nil, fmt.Errorf("unable to read attribute %q of %q: size would have been too big", attribute, path)
+ }
+ }
+ }
+ return m, nil
+}
+
+// Lsetxattrs sets the relevant members of the specified extended attributes on the given file.
+func Lsetxattrs(path string, xattrs map[string]string) error {
+ for attribute, value := range xattrs {
+ if isRelevantXattr(attribute) {
+ if err := unix.Lsetxattr(path, attribute, []byte(value), 0); err != nil {
+ if unshare.IsRootless() && attribute == imaXattr {
+ logrus.Warnf("Unable to set %q xattr on %q: %v", attribute, path, err)
+ } else {
+ return fmt.Errorf("setting value of extended attribute %q on %q: %w", attribute, path, err)
+ }
+ }
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/containers/buildah/copier/xattrs_unsupported.go b/vendor/github.com/containers/buildah/copier/xattrs_unsupported.go
new file mode 100644
index 00000000..59444ff6
--- /dev/null
+++ b/vendor/github.com/containers/buildah/copier/xattrs_unsupported.go
@@ -0,0 +1,15 @@
+//go:build !linux && !netbsd && !freebsd && !darwin
+
+package copier
+
+const (
+ xattrsSupported = false
+)
+
+func Lgetxattrs(path string) (map[string]string, error) {
+ return nil, nil
+}
+
+func Lsetxattrs(path string, xattrs map[string]string) error {
+ return nil
+}
diff --git a/vendor/github.com/containers/buildah/define/build.go b/vendor/github.com/containers/buildah/define/build.go
new file mode 100644
index 00000000..0b1d2080
--- /dev/null
+++ b/vendor/github.com/containers/buildah/define/build.go
@@ -0,0 +1,421 @@
+package define
+
+import (
+ "io"
+ "time"
+
+ encconfig "github.com/containers/ocicrypt/config"
+ "go.podman.io/common/libimage/manifests"
+ nettypes "go.podman.io/common/libnetwork/types"
+ "go.podman.io/image/v5/docker/reference"
+ "go.podman.io/image/v5/types"
+ "go.podman.io/storage/pkg/archive"
+ "golang.org/x/sync/semaphore"
+)
+
+// AdditionalBuildContext contains verbose details about a parsed build context from --build-context
+type AdditionalBuildContext struct {
+ // Value is the URL of an external tar archive.
+ IsURL bool
+ // Value is the name of an image which may or may not have already been pulled.
+ IsImage bool
+ // Value holds a URL (if IsURL), an image name (if IsImage), or an absolute filesystem path.
+ Value string
+ // Absolute filesystem path to a downloaded and exported build context
+ // from an external tar archive. This will be populated only if the
+ // build context was a URL and its contents have been downloaded.
+ DownloadedCache string
+}
+
+// CommonBuildOptions are resources that can be defined by flags for both buildah from and build
+type CommonBuildOptions struct {
+ // AddHost is the list of hostnames to add to the build container's /etc/hosts.
+ AddHost []string
+ // OmitHistory tells the builder to ignore the history of build layers and
+ // base while preparing image-spec, setting this to true will ensure no history
+ // is added to the image-spec. (default false)
+ OmitHistory bool
+ // CgroupParent is the path to cgroups under which the cgroup for the container will be created.
+ CgroupParent string
+ // CPUPeriod limits the CPU CFS (Completely Fair Scheduler) period
+ CPUPeriod uint64
+ // CPUQuota limits the CPU CFS (Completely Fair Scheduler) quota
+ CPUQuota int64
+ // CPUShares (relative weight
+ CPUShares uint64
+ // CPUSetCPUs in which to allow execution (0-3, 0,1)
+ CPUSetCPUs string
+ // CPUSetMems memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.
+ CPUSetMems string
+ // HTTPProxy determines whether *_proxy env vars from the build host are passed into the container.
+ HTTPProxy bool
+ // IdentityLabel if set controls whether or not a `io.buildah.version` label is added to the built image.
+ // Setting this to false does not clear the label if it would be inherited from the base image.
+ IdentityLabel types.OptionalBool
+ // Memory is the upper limit (in bytes) on how much memory running containers can use.
+ Memory int64
+ // DNSSearch is the list of DNS search domains to add to the build container's /etc/resolv.conf
+ DNSSearch []string
+ // DNSServers is the list of DNS servers to add to the build container's /etc/resolv.conf
+ DNSServers []string
+ // DNSOptions is the list of DNS
+ DNSOptions []string
+ // LabelOpts is a slice of the fields of an SELinux context, given in "field:pair" format, or "disable".
+ // Recognized field names are "role", "type", and "level".
+ LabelOpts []string
+ // Paths to mask
+ Masks []string
+ // MemorySwap limits the amount of memory and swap together.
+ MemorySwap int64
+ // NoHostname tells the builder not to create /etc/hostname content when running
+ // containers.
+ NoHostname bool
+ // NoHosts tells the builder not to create /etc/hosts content when running
+ // containers.
+ NoHosts bool
+ // NoNewPrivileges removes the ability for the container to gain privileges
+ NoNewPrivileges bool
+ // OmitTimestamp forces epoch 0 as created timestamp to allow for
+ // deterministic, content-addressable builds.
+ OmitTimestamp bool
+ // SeccompProfilePath is the pathname of a seccomp profile.
+ SeccompProfilePath string
+ // ApparmorProfile is the name of an apparmor profile.
+ ApparmorProfile string
+ // ShmSize is the "size" value to use when mounting an shmfs on the container's /dev/shm directory.
+ ShmSize string
+ // Ulimit specifies resource limit options, in the form type:softlimit[:hardlimit].
+ // These types are recognized:
+ // "core": maximum core dump size (ulimit -c)
+ // "cpu": maximum CPU time (ulimit -t)
+ // "data": maximum size of a process's data segment (ulimit -d)
+ // "fsize": maximum size of new files (ulimit -f)
+ // "locks": maximum number of file locks (ulimit -x)
+ // "memlock": maximum amount of locked memory (ulimit -l)
+ // "msgqueue": maximum amount of data in message queues (ulimit -q)
+ // "nice": niceness adjustment (nice -n, ulimit -e)
+ // "nofile": maximum number of open files (ulimit -n)
+ // "nproc": maximum number of processes (ulimit -u)
+ // "rss": maximum size of a process's (ulimit -m)
+ // "rtprio": maximum real-time scheduling priority (ulimit -r)
+ // "rttime": maximum amount of real-time execution between blocking syscalls
+ // "sigpending": maximum number of pending signals (ulimit -i)
+ // "stack": maximum stack size (ulimit -s)
+ Ulimit []string
+ // Volumes to bind mount into the container
+ Volumes []string
+ // Secrets are the available secrets to use in a build. Each item in the
+ // slice takes the form "id=foo,src=bar", where both "id" and "src" are
+ // required, in that order, and "bar" is the name of a file.
+ Secrets []string
+ // SSHSources is the available ssh agent connections to forward in the build
+ SSHSources []string
+ // OCIHooksDir is the location of OCI hooks for the build containers
+ OCIHooksDir []string
+ // Paths to unmask
+ Unmasks []string
+}
+
+// BuildOptions can be used to alter how an image is built.
+type BuildOptions struct {
+ // ContainerSuffix it the name to suffix containers with
+ ContainerSuffix string
+ // ContextDirectory is the default source location for COPY and ADD
+ // commands.
+ ContextDirectory string
+ // PullPolicy controls whether or not we pull images. It should be one
+ // of PullIfMissing, PullAlways, PullIfNewer, or PullNever.
+ PullPolicy PullPolicy
+ // Registry is a value which is prepended to the image's name, if it
+ // needs to be pulled and the image name alone can not be resolved to a
+ // reference to a source image. No separator is implicitly added.
+ Registry string
+ // IgnoreUnrecognizedInstructions tells us to just log instructions we
+ // don't recognize, and try to keep going.
+ IgnoreUnrecognizedInstructions bool
+ // Manifest Name to which the image will be added.
+ Manifest string
+ // Quiet tells us whether or not to announce steps as we go through them.
+ Quiet bool
+ // Isolation controls how Run() runs things.
+ Isolation Isolation
+ // Runtime is the name of the command to run for RUN instructions when
+ // Isolation is either IsolationDefault or IsolationOCI. It should
+ // accept the same arguments and flags that runc does.
+ Runtime string
+ // RuntimeArgs adds global arguments for the runtime.
+ RuntimeArgs []string
+ // TransientMounts is a list of unparsed mounts that will be provided to
+ // RUN instructions.
+ TransientMounts []string
+ // CacheFrom specifies any remote repository which can be treated as
+ // potential cache source.
+ CacheFrom []reference.Named
+ // CacheTo specifies any remote repository which can be treated as
+ // potential cache destination.
+ CacheTo []reference.Named
+ // CacheTTL specifies duration, if specified using `--cache-ttl` then
+ // cache intermediate images under this duration will be considered as
+ // valid cache sources and images outside this duration will be ignored.
+ CacheTTL time.Duration
+ // Compression specifies the type of compression which is applied to
+ // layer blobs. The default is to not use compression, but
+ // archive.Gzip is recommended.
+ Compression archive.Compression
+ // Arguments which can be interpolated into Dockerfiles
+ Args map[string]string
+ // Map of external additional build contexts
+ AdditionalBuildContexts map[string]*AdditionalBuildContext
+ // Name of the image to write to.
+ Output string
+ // BuildOutputs specifies if any custom build output is selected for
+ // following build. It allows the end user to export the image's
+ // rootfs to a directory or a tar archive. See the documentation of
+ // 'buildah build --output' for the details of the syntax.
+ BuildOutputs []string
+ // Deprecated: use BuildOutputs instead.
+ BuildOutput string
+ // ConfidentialWorkload controls whether or not, and if so, how, we produce an
+ // image that's meant to be run using krun as a VM instead of a conventional
+ // process-type container.
+ ConfidentialWorkload ConfidentialWorkloadOptions
+ // Additional tags to add to the image that we write, if we know of a
+ // way to add them.
+ AdditionalTags []string
+ // Logfile specifies if log output is redirected to an external file
+ // instead of stdout, stderr.
+ LogFile string
+ // LogByPlatform tells imagebuildah to split log to different log files
+ // for each platform if logging to external file was selected.
+ LogSplitByPlatform bool
+ // Log is a callback that will print a progress message. If no value
+ // is supplied, the message will be sent to Err (or os.Stderr, if Err
+ // is nil) by default.
+ Log func(format string, args ...any)
+ // In is connected to stdin for RUN instructions.
+ In io.Reader
+ // Out is a place where non-error log messages are sent.
+ Out io.Writer
+ // Err is a place where error log messages should be sent.
+ Err io.Writer
+ // SignaturePolicyPath specifies an override location for the signature
+ // policy which should be used for verifying the new image as it is
+ // being written. Except in specific circumstances, no value should be
+ // specified, indicating that the shared, system-wide default policy
+ // should be used.
+ SignaturePolicyPath string
+ // SkipUnusedStages allows users to skip stages in a multi-stage builds
+ // which do not contribute anything to the target stage. Expected default
+ // value is true.
+ SkipUnusedStages types.OptionalBool
+ // ReportWriter is an io.Writer which will be used to report the
+ // progress of the (possible) pulling of the source image and the
+ // writing of the new image.
+ ReportWriter io.Writer
+ // OutputFormat is the format of the output image's manifest and
+ // configuration data.
+ // Accepted values are buildah.OCIv1ImageManifest and buildah.Dockerv2ImageManifest.
+ OutputFormat string
+ // SystemContext holds parameters used for authentication.
+ SystemContext *types.SystemContext
+ // NamespaceOptions controls how we set up namespaces processes that we
+ // might need when handling RUN instructions.
+ NamespaceOptions []NamespaceOption
+ // ConfigureNetwork controls whether or not network interfaces and
+ // routing are configured for a new network namespace (i.e., when not
+ // joining another's namespace and not just using the host's
+ // namespace), effectively deciding whether or not the process has a
+ // usable network.
+ ConfigureNetwork NetworkConfigurationPolicy
+ // CNIPluginPath is the location of CNI plugin helpers, if they should be
+ // run from a location other than the default location.
+ CNIPluginPath string
+ // CNIConfigDir is the location of CNI configuration files, if the files in
+ // the default configuration directory shouldn't be used.
+ CNIConfigDir string
+
+ // NetworkInterface is the libnetwork network interface used to setup CNI or netavark networks.
+ NetworkInterface nettypes.ContainerNetwork `json:"-"`
+
+ // ID mapping options to use if we're setting up our own user namespace
+ // when handling RUN instructions.
+ IDMappingOptions *IDMappingOptions
+ // InheritLabels controls whether or not built images will retain the labels
+ // which were set in their base images
+ InheritLabels types.OptionalBool
+ // InheritAnnotations controls whether or not built images will retain the annotations
+ // which were set in their base images
+ InheritAnnotations types.OptionalBool
+ // AddCapabilities is a list of capabilities to add to the default set when
+ // handling RUN instructions.
+ AddCapabilities []string
+ // DropCapabilities is a list of capabilities to remove from the default set
+ // when handling RUN instructions. If a capability appears in both lists, it
+ // will be dropped.
+ DropCapabilities []string
+ // CommonBuildOpts is *required*.
+ CommonBuildOpts *CommonBuildOptions
+ // CPPFlags are additional arguments to pass to the C Preprocessor (cpp).
+ CPPFlags []string
+ // DefaultMountsFilePath is the file path holding the mounts to be mounted for RUN
+ // instructions in "host-path:container-path" format
+ DefaultMountsFilePath string
+ // IIDFile tells the builder to write the image ID to the specified file
+ IIDFile string
+ // Squash tells the builder to produce an image with a single layer instead of with
+ // possibly more than one layer, by only committing a new layer after processing the
+ // final instruction.
+ Squash bool
+ // Labels to set in a committed image.
+ Labels []string
+ // LayerLabels metadata for an intermediate image
+ LayerLabels []string
+ // Annotations to set in a committed image, in OCI format.
+ Annotations []string
+ // OnBuild commands to be run by builds that use the image we'll commit as a base image.
+ OnBuild []string
+ // Layers tells the builder to commit an image for each step in the Dockerfile.
+ Layers bool
+ // NoCache tells the builder to build the image from scratch without checking for a cache.
+ // It creates a new set of cached images for the build.
+ NoCache bool
+ // RemoveIntermediateCtrs tells the builder whether to remove intermediate containers used
+ // during the build process. Default is true.
+ RemoveIntermediateCtrs bool
+ // ForceRmIntermediateCtrs tells the builder to remove all intermediate containers even if
+ // the build was unsuccessful.
+ ForceRmIntermediateCtrs bool
+ // BlobDirectory is a directory which we'll use for caching layer blobs.
+ //
+ // This option will be overridden for cache pulls if
+ // CachePullDestinationLookupReferenceFunc is set, and overridden for cache pushes if
+ // CachePushSourceLookupReferenceFunc is set.
+ BlobDirectory string
+ // Target the targeted FROM in the Dockerfile to build.
+ Target string
+ // Devices are unparsed devices to provide to RUN instructions.
+ Devices []string
+ // SignBy is the fingerprint of a GPG key to use for signing images.
+ SignBy string
+ // Architecture specifies the target architecture of the image to be built.
+ Architecture string
+ // Timestamp specifies a timestamp to use for the image's created-on
+ // date, the corresponding field in new history entries, the timestamps
+ // to set on contents in new layer diffs, and the timestamps to set on
+ // contents written as specified in the BuildOutput field. If left
+ // unset, the current time is used for the configuration and manifest,
+ // and layer contents are recorded as-is.
+ Timestamp *time.Time
+ // SourceDateEpoch specifies a timestamp to use for the image's
+ // created-on date and the corresponding field in new history entries,
+ // and any content written as specified in the BuildOutput field. If
+ // left unset, the current time is used for the configuration and
+ // manifest, and layer and BuildOutput contents retain their original
+ // timestamps.
+ SourceDateEpoch *time.Time
+ // RewriteTimestamp, if set, forces timestamps in generated layers to
+ // not be later than the SourceDateEpoch, if it is also set.
+ RewriteTimestamp bool
+ // OS is the specifies the operating system of the image to be built.
+ OS string
+ // MaxPullPushRetries is the maximum number of attempts we'll make to pull or push any one
+ // image from or to an external registry if the first attempt fails.
+ MaxPullPushRetries int
+ // PullPushRetryDelay is how long to wait before retrying a pull or push attempt.
+ PullPushRetryDelay time.Duration
+ // OciDecryptConfig contains the config that can be used to decrypt an image if it is
+ // encrypted if non-nil. If nil, it does not attempt to decrypt an image.
+ OciDecryptConfig *encconfig.DecryptConfig
+ // Jobs is the number of stages to run in parallel. If not specified it defaults to 1.
+ // Ignored if a JobSemaphore is provided.
+ Jobs *int
+ // JobSemaphore, for when you want Jobs to be shared with more than just this build.
+ JobSemaphore *semaphore.Weighted
+ // LogRusage logs resource usage for each step.
+ LogRusage bool
+ // File to which the Rusage logs will be saved to instead of stdout.
+ RusageLogFile string
+ // Excludes is a list of excludes to be used instead of the .dockerignore file.
+ Excludes []string
+ // IgnoreFile is a name of the .containerignore file
+ IgnoreFile string
+ // From is the image name to use to replace the value specified in the first
+ // FROM instruction in the Containerfile.
+ From string
+ // GroupAdd is a list of groups to add to the primary process when handling RUN
+ // instructions. The magic 'keep-groups' value indicates that the process should
+ // be allowed to inherit the current set of supplementary groups.
+ GroupAdd []string
+ // Platforms is the list of parsed OS/Arch/Variant triples that we want
+ // to build the image for. If this slice has items in it, the OS and
+ // Architecture fields above are ignored.
+ Platforms []struct{ OS, Arch, Variant string }
+ // AllPlatforms tells the builder to set the list of target platforms
+ // to match the set of platforms for which all of the build's base
+ // images are available. If this field is set, Platforms is ignored.
+ AllPlatforms bool
+ // UnsetEnvs is a list of environments to not add to final image.
+ UnsetEnvs []string
+ // UnsetLabels is a list of labels to not add to final image from base image.
+ UnsetLabels []string
+ // UnsetAnnotations is a list of annotations to not add to final image from base image.
+ UnsetAnnotations []string
+ // Envs is a list of environment variables to set in the final image.
+ Envs []string
+ // OSFeatures specifies operating system features the image requires.
+ // It is typically only set when the OS is "windows".
+ OSFeatures []string
+ // OSVersion specifies the exact operating system version the image
+ // requires. It is typically only set when the OS is "windows". Any
+ // value set in a base image will be preserved, so this does not
+ // frequently need to be set.
+ OSVersion string
+ // SBOMScanOptions encapsulates options which control whether or not we
+ // run scanners on the rootfs that we're about to commit, and how.
+ SBOMScanOptions []SBOMScanOptions
+ // CDIConfigDir is the location of CDI configuration files, if the files in
+ // the default configuration locations shouldn't be used.
+ CDIConfigDir string
+ // CachePullSourceLookupReferenceFunc is an optional LookupReferenceFunc
+ // used to look up source references for cache pulls.
+ CachePullSourceLookupReferenceFunc manifests.LookupReferenceFunc
+ // CachePullDestinationLookupReferenceFunc is an optional generator
+ // function which provides a LookupReferenceFunc used to look up
+ // destination references for cache pulls.
+ //
+ // BlobDirectory will be ignored for cache pulls if this option is set.
+ CachePullDestinationLookupReferenceFunc func(srcRef types.ImageReference) manifests.LookupReferenceFunc
+ // CachePushSourceLookupReferenceFunc is an optional generator function
+ // which provides a LookupReferenceFunc used to look up source
+ // references for cache pushes.
+ //
+ // BlobDirectory will be ignored for cache pushes if this option is set.
+ CachePushSourceLookupReferenceFunc func(dest types.ImageReference) manifests.LookupReferenceFunc
+ // CachePushDestinationLookupReferenceFunc is an optional
+ // LookupReferenceFunc used to look up destination references for cache
+ // pushes
+ CachePushDestinationLookupReferenceFunc manifests.LookupReferenceFunc
+ // CompatSetParent causes the "parent" field to be set in the image's
+ // configuration when committing in Docker format. Newer
+ // BuildKit-based docker build doesn't set this field.
+ CompatSetParent types.OptionalBool
+ // CompatVolumes causes the contents of locations marked as volumes in
+ // base images or by a VOLUME instruction to be preserved during RUN
+ // instructions. Newer BuildKit-based docker build doesn't bother.
+ CompatVolumes types.OptionalBool
+ // CompatScratchConfig causes the image, if it does not have a base
+ // image, to begin with a truly empty default configuration instead of
+ // a minimal default configuration. Newer BuildKit-based docker build
+ // provides a minimal initial configuration with a working directory
+ // set in it.
+ CompatScratchConfig types.OptionalBool
+ // CompatLayerOmissions causes the "/dev", "/proc", and "/sys"
+ // directories to be omitted from the image and related output. Newer
+ // BuildKit-based builds include them in the built image by default.
+ CompatLayerOmissions types.OptionalBool
+ // NoPivotRoot inhibits the usage of pivot_root when setting up the rootfs
+ NoPivotRoot bool
+ // CreatedAnnotation controls whether or not an "org.opencontainers.image.created"
+ // annotation is present in the output image.
+ CreatedAnnotation types.OptionalBool
+}
diff --git a/vendor/github.com/containers/buildah/define/isolation.go b/vendor/github.com/containers/buildah/define/isolation.go
new file mode 100644
index 00000000..53bea85f
--- /dev/null
+++ b/vendor/github.com/containers/buildah/define/isolation.go
@@ -0,0 +1,32 @@
+package define
+
+import (
+ "fmt"
+)
+
+type Isolation int
+
+const (
+ // IsolationDefault is whatever we think will work best.
+ IsolationDefault Isolation = iota
+ // IsolationOCI is a proper OCI runtime.
+ IsolationOCI
+ // IsolationChroot is a more chroot-like environment: less isolation,
+ // but with fewer requirements.
+ IsolationChroot
+ // IsolationOCIRootless is a proper OCI runtime in rootless mode.
+ IsolationOCIRootless
+)
+
+// String converts a Isolation into a string.
+func (i Isolation) String() string {
+ switch i {
+ case IsolationDefault, IsolationOCI:
+ return "oci"
+ case IsolationChroot:
+ return "chroot"
+ case IsolationOCIRootless:
+ return "rootless"
+ }
+ return fmt.Sprintf("unrecognized isolation type %d", i)
+}
diff --git a/vendor/github.com/containers/buildah/define/mount_freebsd.go b/vendor/github.com/containers/buildah/define/mount_freebsd.go
new file mode 100644
index 00000000..c0ac6991
--- /dev/null
+++ b/vendor/github.com/containers/buildah/define/mount_freebsd.go
@@ -0,0 +1,14 @@
+//go:build freebsd
+
+package define
+
+const (
+ // TypeBind is the type for mounting host dir
+ TypeBind = "nullfs"
+
+ // TempDir is the default for storing temporary files
+ TempDir = "/var/tmp"
+)
+
+// Mount potions for bind
+var BindOptions = []string{}
diff --git a/vendor/github.com/containers/buildah/define/mount_linux.go b/vendor/github.com/containers/buildah/define/mount_linux.go
new file mode 100644
index 00000000..33530910
--- /dev/null
+++ b/vendor/github.com/containers/buildah/define/mount_linux.go
@@ -0,0 +1,14 @@
+//go:build linux
+
+package define
+
+const (
+ // TypeBind is the type for mounting host dir
+ TypeBind = "bind"
+
+ // TempDir is the default for storing temporary files
+ TempDir = "/dev/shm"
+)
+
+// Mount potions for bind
+var BindOptions = []string{"bind"}
diff --git a/vendor/github.com/containers/buildah/define/mount_unsupported.go b/vendor/github.com/containers/buildah/define/mount_unsupported.go
new file mode 100644
index 00000000..3feab28c
--- /dev/null
+++ b/vendor/github.com/containers/buildah/define/mount_unsupported.go
@@ -0,0 +1,14 @@
+//go:build darwin || windows || netbsd
+
+package define
+
+const (
+ // TypeBind is the type for mounting host dir
+ TypeBind = "bind"
+
+ // TempDir is the default for storing temporary files
+ TempDir = "/var/tmp"
+)
+
+// Mount potions for bind
+var BindOptions = []string{""}
diff --git a/vendor/github.com/containers/buildah/define/namespace.go b/vendor/github.com/containers/buildah/define/namespace.go
new file mode 100644
index 00000000..d0247fe9
--- /dev/null
+++ b/vendor/github.com/containers/buildah/define/namespace.go
@@ -0,0 +1,87 @@
+package define
+
+import (
+ "fmt"
+)
+
+// NamespaceOption controls how we set up a namespace when launching processes.
+type NamespaceOption struct {
+ // Name specifies the type of namespace, typically matching one of the
+ // ...Namespace constants defined in
+ // github.com/opencontainers/runtime-spec/specs-go.
+ Name string
+ // Host is used to force our processes to use the host's namespace of
+ // this type.
+ Host bool
+ // Path is the path of the namespace to attach our process to, if Host
+ // is not set. If Host is not set and Path is also empty, a new
+ // namespace will be created for the process that we're starting.
+ // If Name is specs.NetworkNamespace, if Path doesn't look like an
+ // absolute path, it is treated as a comma-separated list of CNI
+ // configuration names which will be selected from among all of the CNI
+ // network configurations which we find.
+ Path string
+}
+
+// NamespaceOptions provides some helper methods for a slice of NamespaceOption
+// structs.
+type NamespaceOptions []NamespaceOption
+
+// Find the configuration for the namespace of the given type. If there are
+// duplicates, find the _last_ one of the type, since we assume it was appended
+// more recently.
+func (n *NamespaceOptions) Find(namespace string) *NamespaceOption {
+ for i := range *n {
+ j := len(*n) - 1 - i
+ if (*n)[j].Name == namespace {
+ return &((*n)[j])
+ }
+ }
+ return nil
+}
+
+// AddOrReplace either adds or replaces the configuration for a given namespace.
+func (n *NamespaceOptions) AddOrReplace(options ...NamespaceOption) {
+nextOption:
+ for _, option := range options {
+ for i := range *n {
+ j := len(*n) - 1 - i
+ if (*n)[j].Name == option.Name {
+ (*n)[j] = option
+ continue nextOption
+ }
+ }
+ *n = append(*n, option)
+ }
+}
+
+// NetworkConfigurationPolicy takes the value NetworkDefault, NetworkDisabled,
+// or NetworkEnabled.
+type NetworkConfigurationPolicy int
+
+const (
+ // NetworkDefault is one of the values that BuilderOptions.ConfigureNetwork
+ // can take, signalling that the default behavior should be used.
+ NetworkDefault NetworkConfigurationPolicy = iota
+ // NetworkDisabled is one of the values that BuilderOptions.ConfigureNetwork
+ // can take, signalling that network interfaces should NOT be configured for
+ // newly-created network namespaces.
+ NetworkDisabled
+ // NetworkEnabled is one of the values that BuilderOptions.ConfigureNetwork
+ // can take, signalling that network interfaces should be configured for
+ // newly-created network namespaces.
+ NetworkEnabled
+)
+
+// String formats a NetworkConfigurationPolicy as a string.
+func (p NetworkConfigurationPolicy) String() string {
+ switch p {
+ case NetworkDefault:
+ return "NetworkDefault"
+ case NetworkDisabled:
+ return "NetworkDisabled"
+ case NetworkEnabled:
+ return "NetworkEnabled"
+ }
+ return fmt.Sprintf("unknown NetworkConfigurationPolicy %d", p)
+}
diff --git a/vendor/github.com/containers/buildah/define/pull.go b/vendor/github.com/containers/buildah/define/pull.go
new file mode 100644
index 00000000..a98cc3d2
--- /dev/null
+++ b/vendor/github.com/containers/buildah/define/pull.go
@@ -0,0 +1,53 @@
+package define
+
+import (
+ "fmt"
+)
+
+// PullPolicy takes the value PullIfMissing, PullAlways, PullIfNewer, or PullNever.
+// N.B.: the enumeration values for this type differ from those used by
+// github.com/containers/common/pkg/config.PullPolicy (their zero values
+// indicate different policies), so they are not interchangeable.
+type PullPolicy int
+
+const (
+ // PullIfMissing is one of the values that BuilderOptions.PullPolicy
+ // can take, signalling that the source image should be pulled from a
+ // registry if a local copy of it is not already present.
+ PullIfMissing PullPolicy = iota
+ // PullAlways is one of the values that BuilderOptions.PullPolicy can
+ // take, signalling that a fresh, possibly updated, copy of the image
+ // should be pulled from a registry before the build proceeds.
+ PullAlways
+ // PullIfNewer is one of the values that BuilderOptions.PullPolicy
+ // can take, signalling that the source image should only be pulled
+ // from a registry if a local copy is not already present or if a
+ // newer version the image is present on the repository.
+ PullIfNewer
+ // PullNever is one of the values that BuilderOptions.PullPolicy can
+ // take, signalling that the source image should not be pulled from a
+ // registry.
+ PullNever
+)
+
+// String converts a PullPolicy into a string.
+func (p PullPolicy) String() string {
+ switch p {
+ case PullIfMissing:
+ return "missing"
+ case PullAlways:
+ return "always"
+ case PullIfNewer:
+ return "ifnewer"
+ case PullNever:
+ return "never"
+ }
+ return fmt.Sprintf("unrecognized policy %d", p)
+}
+
+var PolicyMap = map[string]PullPolicy{
+ "missing": PullIfMissing,
+ "always": PullAlways,
+ "never": PullNever,
+ "ifnewer": PullIfNewer,
+}
diff --git a/vendor/github.com/containers/buildah/define/types.go b/vendor/github.com/containers/buildah/define/types.go
new file mode 100644
index 00000000..47cea19c
--- /dev/null
+++ b/vendor/github.com/containers/buildah/define/types.go
@@ -0,0 +1,354 @@
+package define
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ urlpkg "net/url"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "strings"
+
+ v1 "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/sirupsen/logrus"
+ "go.podman.io/image/v5/manifest"
+ "go.podman.io/storage/pkg/archive"
+ "go.podman.io/storage/pkg/chrootarchive"
+ "go.podman.io/storage/pkg/ioutils"
+ "go.podman.io/storage/types"
+)
+
+const (
+ // Package is the name of this package, used in help output and to
+ // identify working containers.
+ Package = "buildah"
+ // Version for the Package. Also used by .packit.sh for Packit builds.
+ Version = "1.42.2"
+
+ // DefaultRuntime if containers.conf fails.
+ DefaultRuntime = "runc"
+
+ // OCIv1ImageManifest is the MIME type of an OCIv1 image manifest,
+ // suitable for specifying as a value of the PreferredManifestType
+ // member of a CommitOptions structure. It is also the default.
+ OCIv1ImageManifest = v1.MediaTypeImageManifest
+ // Dockerv2ImageManifest is the MIME type of a Docker v2s2 image
+ // manifest, suitable for specifying as a value of the
+ // PreferredManifestType member of a CommitOptions structure.
+ Dockerv2ImageManifest = manifest.DockerV2Schema2MediaType
+
+ // OCI used to define the "oci" image format
+ OCI = "oci"
+ // DOCKER used to define the "docker" image format
+ DOCKER = "docker"
+
+ // SEV is a known trusted execution environment type: AMD-SEV (secure encrypted virtualization using encrypted state, requires epyc 1000 "naples")
+ SEV TeeType = "sev"
+ // SNP is a known trusted execution environment type: AMD-SNP (SEV secure nested pages) (requires epyc 3000 "milan")
+ SNP TeeType = "snp"
+)
+
+// DefaultRlimitValue is the value set by default for nofile and nproc
+const RLimitDefaultValue = uint64(1048576)
+
+// TeeType is a supported trusted execution environment type.
+type TeeType string
+
+var (
+ // Deprecated: DefaultCapabilities values should be retrieved from
+ // github.com/containers/common/pkg/config
+ DefaultCapabilities = []string{
+ "CAP_AUDIT_WRITE",
+ "CAP_CHOWN",
+ "CAP_DAC_OVERRIDE",
+ "CAP_FOWNER",
+ "CAP_FSETID",
+ "CAP_KILL",
+ "CAP_MKNOD",
+ "CAP_NET_BIND_SERVICE",
+ "CAP_SETFCAP",
+ "CAP_SETGID",
+ "CAP_SETPCAP",
+ "CAP_SETUID",
+ "CAP_SYS_CHROOT",
+ }
+ // Deprecated: DefaultNetworkSysctl values should be retrieved from
+ // github.com/containers/common/pkg/config
+ DefaultNetworkSysctl = map[string]string{
+ "net.ipv4.ping_group_range": "0 0",
+ }
+
+ Gzip = archive.Gzip
+ Bzip2 = archive.Bzip2
+ Xz = archive.Xz
+ Zstd = archive.Zstd
+ Uncompressed = archive.Uncompressed
+)
+
+// IDMappingOptions controls how we set up UID/GID mapping when we set up a
+// user namespace.
+type IDMappingOptions struct {
+ HostUIDMapping bool
+ HostGIDMapping bool
+ UIDMap []specs.LinuxIDMapping
+ GIDMap []specs.LinuxIDMapping
+ AutoUserNs bool
+ AutoUserNsOpts types.AutoUserNsOptions
+}
+
+// Secret is a secret source that can be used in a RUN
+type Secret struct {
+ ID string
+ Source string
+ SourceType string
+}
+
+// BuildOutputOptions contains the the outcome of parsing the value of a build --output flag
+type BuildOutputOption struct {
+ Path string // Only valid if !IsStdout
+ IsDir bool
+ IsStdout bool
+}
+
+// ConfidentialWorkloadOptions encapsulates options which control whether or not
+// we output an image whose rootfs contains a LUKS-compatibly-encrypted disk image
+// instead of the usual rootfs contents.
+type ConfidentialWorkloadOptions struct {
+ Convert bool
+ AttestationURL string
+ CPUs int
+ Memory int
+ TempDir string // used for the temporary plaintext copy of the disk image
+ TeeType TeeType
+ IgnoreAttestationErrors bool
+ WorkloadID string
+ DiskEncryptionPassphrase string
+ Slop string
+ FirmwareLibrary string
+}
+
+// SBOMMergeStrategy tells us how to merge multiple SBOM documents into one.
+type SBOMMergeStrategy string
+
+const (
+ // SBOMMergeStrategyCat literally concatenates the documents.
+ SBOMMergeStrategyCat SBOMMergeStrategy = "cat"
+ // SBOMMergeStrategyCycloneDXByComponentNameAndVersion adds components
+ // from the second document to the first, so long as they have a
+ // name+version combination which is not already present in the
+ // components array.
+ SBOMMergeStrategyCycloneDXByComponentNameAndVersion SBOMMergeStrategy = "merge-cyclonedx-by-component-name-and-version"
+ // SBOMMergeStrategySPDXByPackageNameAndVersionInfo adds packages from
+ // the second document to the first, so long as they have a
+ // name+versionInfo combination which is not already present in the
+ // first document's packages array, and adds hasExtractedLicensingInfos
+ // items from the second document to the first, so long as they include
+ // a licenseId value which is not already present in the first
+ // document's hasExtractedLicensingInfos array.
+ SBOMMergeStrategySPDXByPackageNameAndVersionInfo SBOMMergeStrategy = "merge-spdx-by-package-name-and-versioninfo"
+)
+
+// SBOMScanOptions encapsulates options which control whether or not we run a
+// scanner on the rootfs that we're about to commit, and how.
+type SBOMScanOptions struct {
+ Type []string // a shorthand name for a defined group of these options
+ Image string // the scanner image to use
+ PullPolicy PullPolicy // how to get the scanner image
+ Commands []string // one or more commands to invoke for the image rootfs or ContextDir locations
+ ContextDir []string // one or more "source" directory locations
+ SBOMOutput string // where to save SBOM scanner output outside of the image (i.e., the local filesystem)
+ PURLOutput string // where to save PURL list outside of the image (i.e., the local filesystem)
+ ImageSBOMOutput string // where to save SBOM scanner output in the image
+ ImagePURLOutput string // where to save PURL list in the image
+ MergeStrategy SBOMMergeStrategy // how to merge the outputs of multiple scans
+}
+
+// TempDirForURL checks if the passed-in string looks like a URL or "-". If it
+// is, TempDirForURL creates a temporary directory, arranges for its contents
+// to be the contents of that URL, and returns the temporary directory's path,
+// along with the relative name of a subdirectory which should be used as the
+// build context (which may be empty or "."). Removal of the temporary
+// directory is the responsibility of the caller. If the string doesn't look
+// like a URL or "-", TempDirForURL returns empty strings and a nil error code.
+func TempDirForURL(dir, prefix, url string) (name string, subdir string, err error) {
+ if !strings.HasPrefix(url, "http://") &&
+ !strings.HasPrefix(url, "https://") &&
+ !strings.HasPrefix(url, "git://") &&
+ !strings.HasPrefix(url, "github.com/") &&
+ url != "-" {
+ return "", "", nil
+ }
+ name, err = os.MkdirTemp(dir, prefix)
+ if err != nil {
+ return "", "", fmt.Errorf("creating temporary directory for %q: %w", url, err)
+ }
+ downloadDir := filepath.Join(name, "download")
+ if err = os.MkdirAll(downloadDir, 0o700); err != nil {
+ return "", "", fmt.Errorf("creating directory %q for %q: %w", downloadDir, url, err)
+ }
+ urlParsed, err := urlpkg.Parse(url)
+ if err != nil {
+ return "", "", fmt.Errorf("parsing url %q: %w", url, err)
+ }
+ if strings.HasPrefix(url, "git://") || strings.HasSuffix(urlParsed.Path, ".git") {
+ combinedOutput, gitSubDir, err := cloneToDirectory(url, downloadDir)
+ if err != nil {
+ if err2 := os.RemoveAll(name); err2 != nil {
+ logrus.Debugf("error removing temporary directory %q: %v", name, err2)
+ }
+ return "", "", fmt.Errorf("cloning %q to %q:\n%s: %w", url, name, string(combinedOutput), err)
+ }
+ logrus.Debugf("Build context is at %q", filepath.Join(downloadDir, gitSubDir))
+ return name, filepath.Join(filepath.Base(downloadDir), gitSubDir), nil
+ }
+ if strings.HasPrefix(url, "github.com/") {
+ ghurl := url
+ url = fmt.Sprintf("https://%s/archive/master.tar.gz", ghurl)
+ logrus.Debugf("resolving url %q to %q", ghurl, url)
+ subdir = path.Base(ghurl) + "-master"
+ }
+ if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
+ err = downloadToDirectory(url, downloadDir)
+ if err != nil {
+ if err2 := os.RemoveAll(name); err2 != nil {
+ logrus.Debugf("error removing temporary directory %q: %v", name, err2)
+ }
+ return "", "", err
+ }
+ logrus.Debugf("Build context is at %q", filepath.Join(downloadDir, subdir))
+ return name, filepath.Join(filepath.Base(downloadDir), subdir), nil
+ }
+ if url == "-" {
+ err = stdinToDirectory(downloadDir)
+ if err != nil {
+ if err2 := os.RemoveAll(name); err2 != nil {
+ logrus.Debugf("error removing temporary directory %q: %v", name, err2)
+ }
+ return "", "", err
+ }
+ logrus.Debugf("Build context is at %q", filepath.Join(downloadDir, subdir))
+ return name, filepath.Join(filepath.Base(downloadDir), subdir), nil
+ }
+ logrus.Debugf("don't know how to retrieve %q", url)
+ if err2 := os.RemoveAll(name); err2 != nil {
+ logrus.Debugf("error removing temporary directory %q: %v", name, err2)
+ }
+ return "", "", errors.New("unreachable code reached")
+}
+
+// parseGitBuildContext parses git build context to `repo`, `sub-dir`
+// `branch/commit`, accepts GitBuildContext in the format of
+// `repourl.git[#[branch-or-commit]:subdir]`.
+func parseGitBuildContext(url string) (string, string, string) {
+ gitSubdir := ""
+ gitBranch := ""
+ gitBranchPart := strings.Split(url, "#")
+ if len(gitBranchPart) > 1 {
+ // check if string contains path to a subdir
+ gitSubDirPart := strings.Split(gitBranchPart[1], ":")
+ if len(gitSubDirPart) > 1 {
+ gitSubdir = gitSubDirPart[1]
+ }
+ gitBranch = gitSubDirPart[0]
+ }
+ return gitBranchPart[0], gitSubdir, gitBranch
+}
+
+func cloneToDirectory(url, dir string) ([]byte, string, error) {
+ var cmd *exec.Cmd
+ gitRepo, gitSubdir, gitRef := parseGitBuildContext(url)
+ // init repo
+ cmd = exec.Command("git", "init", dir)
+ combinedOutput, err := cmd.CombinedOutput()
+ if err != nil {
+ // Return err.Error() instead of err as we want buildah to override error code with more predictable
+ // value.
+ return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git init`: %s", err.Error())
+ }
+ // add origin
+ cmd = exec.Command("git", "remote", "add", "origin", gitRepo)
+ cmd.Dir = dir
+ combinedOutput, err = cmd.CombinedOutput()
+ if err != nil {
+ // Return err.Error() instead of err as we want buildah to override error code with more predictable
+ // value.
+ return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git remote add`: %s", err.Error())
+ }
+
+ logrus.Debugf("fetching repo %q and branch (or commit ID) %q to %q", gitRepo, gitRef, dir)
+ args := []string{"fetch", "-u", "--depth=1", "origin", "--", gitRef}
+ cmd = exec.Command("git", args...)
+ cmd.Dir = dir
+ combinedOutput, err = cmd.CombinedOutput()
+ if err != nil {
+ // Return err.Error() instead of err as we want buildah to override error code with more predictable
+ // value.
+ return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git fetch`: %s", err.Error())
+ }
+
+ cmd = exec.Command("git", "checkout", "FETCH_HEAD")
+ cmd.Dir = dir
+ combinedOutput, err = cmd.CombinedOutput()
+ if err != nil {
+ // Return err.Error() instead of err as we want buildah to override error code with more predictable
+ // value.
+ return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git checkout`: %s", err.Error())
+ }
+ return combinedOutput, gitSubdir, nil
+}
+
+func downloadToDirectory(url, dir string) error {
+ logrus.Debugf("extracting %q to %q", url, dir)
+ resp, err := http.Get(url)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusBadRequest {
+ return fmt.Errorf("invalid response status %d", resp.StatusCode)
+ }
+ if resp.ContentLength == 0 {
+ return fmt.Errorf("no contents in %q", url)
+ }
+ if err := chrootarchive.Untar(resp.Body, dir, nil); err != nil {
+ resp1, err := http.Get(url)
+ if err != nil {
+ return err
+ }
+ defer resp1.Body.Close()
+ body, err := io.ReadAll(resp1.Body)
+ if err != nil {
+ return err
+ }
+ dockerfile := filepath.Join(dir, "Dockerfile")
+ // Assume this is a Dockerfile
+ if err := ioutils.AtomicWriteFile(dockerfile, body, 0o600); err != nil {
+ return fmt.Errorf("failed to write %q to %q: %w", url, dockerfile, err)
+ }
+ }
+ return nil
+}
+
+func stdinToDirectory(dir string) error {
+ logrus.Debugf("extracting stdin to %q", dir)
+ r := bufio.NewReader(os.Stdin)
+ b, err := io.ReadAll(r)
+ if err != nil {
+ return fmt.Errorf("failed to read from stdin: %w", err)
+ }
+ reader := bytes.NewReader(b)
+ if err := chrootarchive.Untar(reader, dir, nil); err != nil {
+ dockerfile := filepath.Join(dir, "Dockerfile")
+ // Assume this is a Dockerfile
+ if err := ioutils.AtomicWriteFile(dockerfile, b, 0o600); err != nil {
+ return fmt.Errorf("failed to write bytes to %q: %w", dockerfile, err)
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/containers/buildah/define/types_unix.go b/vendor/github.com/containers/buildah/define/types_unix.go
new file mode 100644
index 00000000..20b1a7b2
--- /dev/null
+++ b/vendor/github.com/containers/buildah/define/types_unix.go
@@ -0,0 +1,18 @@
+//go:build darwin || linux
+
+package define
+
+import (
+ "github.com/opencontainers/runc/libcontainer/devices"
+)
+
+// BuildahDevice is a wrapper around devices.Device
+// with additional support for renaming a device
+// using bind-mount in rootless environments.
+type BuildahDevice struct {
+ devices.Device
+ Source string
+ Destination string
+}
+
+type ContainerDevices = []BuildahDevice
diff --git a/vendor/github.com/containers/buildah/define/types_unsupported.go b/vendor/github.com/containers/buildah/define/types_unsupported.go
new file mode 100644
index 00000000..7db27204
--- /dev/null
+++ b/vendor/github.com/containers/buildah/define/types_unsupported.go
@@ -0,0 +1,6 @@
+//go:build !linux && !darwin
+
+package define
+
+// ContainerDevices is currently not implemented.
+type ContainerDevices = []struct{}
diff --git a/vendor/github.com/containers/buildah/delete.go b/vendor/github.com/containers/buildah/delete.go
new file mode 100644
index 00000000..7adb7c5e
--- /dev/null
+++ b/vendor/github.com/containers/buildah/delete.go
@@ -0,0 +1,15 @@
+package buildah
+
+import "fmt"
+
+// Delete removes the working container. The buildah.Builder object should not
+// be used after this method is called.
+func (b *Builder) Delete() error {
+ if err := b.store.DeleteContainer(b.ContainerID); err != nil {
+ return fmt.Errorf("deleting build container %q: %w", b.ContainerID, err)
+ }
+ b.MountPoint = ""
+ b.Container = ""
+ b.ContainerID = ""
+ return nil
+}
diff --git a/vendor/github.com/containers/buildah/developmentplan.md b/vendor/github.com/containers/buildah/developmentplan.md
new file mode 100644
index 00000000..7d82c2e6
--- /dev/null
+++ b/vendor/github.com/containers/buildah/developmentplan.md
@@ -0,0 +1,13 @@
+
+
+# Development Plan
+
+## Development goals for Buildah
+
+ * Integration into Kubernetes and potentially other tools. The biggest requirement for this is to be able run Buildah within a standard linux container without SYS_ADMIN privileges. This would allow Buildah to run non-privileged containers inside of Kubernetes, so you could distribute your container workloads.
+
+ * Integration with User Namespace, Podman has this already and the goal is to get `buildah build` and `buildah run` to be able to run its containers in a usernamespace to give the builder better security isolation from the host.
+
+ * Buildah `buildah build` command's goal is to have feature parity with other OCI image and container build systems.
+
+ * Addressing issues from the community as reported in the [Issues](https://github.com/containers/buildah/issues) page.
diff --git a/vendor/github.com/containers/buildah/digester.go b/vendor/github.com/containers/buildah/digester.go
new file mode 100644
index 00000000..65e11569
--- /dev/null
+++ b/vendor/github.com/containers/buildah/digester.go
@@ -0,0 +1,286 @@
+package buildah
+
+import (
+ "archive/tar"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "sync"
+ "time"
+
+ digest "github.com/opencontainers/go-digest"
+)
+
+type digester interface {
+ io.WriteCloser
+ ContentType() string
+ Digest() digest.Digest
+}
+
+// A simple digester just digests its content as-is.
+type simpleDigester struct {
+ digester digest.Digester
+ hasher hash.Hash
+ contentType string
+}
+
+func newSimpleDigester(contentType string) digester {
+ finalDigester := digest.Canonical.Digester()
+ return &simpleDigester{
+ digester: finalDigester,
+ hasher: finalDigester.Hash(),
+ contentType: contentType,
+ }
+}
+
+func (s *simpleDigester) ContentType() string {
+ return s.contentType
+}
+
+func (s *simpleDigester) Write(p []byte) (int, error) {
+ return s.hasher.Write(p)
+}
+
+func (s *simpleDigester) Close() error {
+ return nil
+}
+
+func (s *simpleDigester) Digest() digest.Digest {
+ return s.digester.Digest()
+}
+
+// A tarFilterer passes a tarball through to an io.WriteCloser, potentially
+// modifying headers as it goes.
+type tarFilterer struct {
+ wg sync.WaitGroup
+ pipeWriter *io.PipeWriter
+ closedLock sync.Mutex
+ closed bool
+ err error
+}
+
+func (t *tarFilterer) Write(p []byte) (int, error) {
+ n, err := t.pipeWriter.Write(p)
+ if err != nil {
+ t.closedLock.Lock()
+ closed := t.closed
+ t.closedLock.Unlock()
+ err = fmt.Errorf("writing to tar filter pipe (closed=%v,err=%v): %w", closed, t.err, err)
+ }
+ return n, err
+}
+
+func (t *tarFilterer) Close() error {
+ t.closedLock.Lock()
+ if t.closed {
+ t.closedLock.Unlock()
+ return errors.New("tar filter is already closed")
+ }
+ t.closed = true
+ t.closedLock.Unlock()
+ err := t.pipeWriter.Close()
+ t.wg.Wait()
+ if err != nil {
+ return fmt.Errorf("closing filter pipe: %w", err)
+ }
+ return t.err
+}
+
+// newTarFilterer passes one or more tar archives through to an io.WriteCloser
+// as a single archive, potentially calling filter to modify headers and
+// contents as it goes.
+//
+// Note: if "filter" indicates that a given item should be skipped, there is no
+// guarantee that there will not be a subsequent item of type TypeLink, which
+// is a hard link, which points to the skipped item as the link target.
+func newTarFilterer(writeCloser io.WriteCloser, filter func(hdr *tar.Header) (skip, replaceContents bool, replacementContents io.Reader)) io.WriteCloser {
+ pipeReader, pipeWriter := io.Pipe()
+ tarWriter := tar.NewWriter(writeCloser)
+ filterer := &tarFilterer{
+ pipeWriter: pipeWriter,
+ }
+ filterer.wg.Add(1)
+ go func() {
+ filterer.closedLock.Lock()
+ closed := filterer.closed
+ filterer.closedLock.Unlock()
+ for !closed {
+ tarReader := tar.NewReader(pipeReader)
+ hdr, err := tarReader.Next()
+ for err == nil {
+ var skip, replaceContents bool
+ var replacementContents io.Reader
+ if filter != nil {
+ skip, replaceContents, replacementContents = filter(hdr)
+ }
+ if !skip {
+ if err = tarWriter.WriteHeader(hdr); err != nil {
+ err = fmt.Errorf("writing tar header for %q: %w", hdr.Name, err)
+ break
+ }
+ if hdr.Size != 0 {
+ var n int64
+ var copyErr error
+ if replaceContents {
+ n, copyErr = io.CopyN(tarWriter, replacementContents, hdr.Size)
+ } else {
+ n, copyErr = io.Copy(tarWriter, tarReader)
+ }
+ if copyErr != nil {
+ err = fmt.Errorf("copying content for %q: %w", hdr.Name, copyErr)
+ break
+ }
+ if n != hdr.Size {
+ err = fmt.Errorf("filtering content for %q: expected %d bytes, got %d bytes", hdr.Name, hdr.Size, n)
+ break
+ }
+ }
+ if err = tarWriter.Flush(); err != nil {
+ err = fmt.Errorf("flushing tar item padding for %q: %w", hdr.Name, err)
+ break
+ }
+ }
+ hdr, err = tarReader.Next()
+ }
+ if !errors.Is(err, io.EOF) {
+ filterer.err = fmt.Errorf("reading tar archive: %w", err)
+ break
+ }
+ filterer.closedLock.Lock()
+ closed = filterer.closed
+ filterer.closedLock.Unlock()
+ }
+ err1 := tarWriter.Close()
+ err := writeCloser.Close()
+ if err == nil {
+ err = err1
+ }
+ if err != nil {
+ pipeReader.CloseWithError(err)
+ } else {
+ pipeReader.Close()
+ }
+ filterer.wg.Done()
+ }()
+ return filterer
+}
+
+// A tar digester digests an archive, modifying the headers it digests by
+// calling a specified function to potentially modify the header that it's
+// about to write.
+type tarDigester struct {
+ isOpen bool
+ nested digester
+ tarFilterer io.WriteCloser
+}
+
+func modifyTarHeaderForDigesting(hdr *tar.Header) (skip, replaceContents bool, replacementContents io.Reader) {
+ zeroTime := time.Time{}
+ hdr.ModTime = zeroTime
+ hdr.AccessTime = zeroTime
+ hdr.ChangeTime = zeroTime
+ return false, false, nil
+}
+
+func newTarDigester(contentType string) digester {
+ nested := newSimpleDigester(contentType)
+ digester := &tarDigester{
+ isOpen: true,
+ nested: nested,
+ tarFilterer: newTarFilterer(nested, modifyTarHeaderForDigesting),
+ }
+ return digester
+}
+
+func (t *tarDigester) ContentType() string {
+ return t.nested.ContentType()
+}
+
+func (t *tarDigester) Digest() digest.Digest {
+ return t.nested.Digest()
+}
+
+func (t *tarDigester) Write(p []byte) (int, error) {
+ return t.tarFilterer.Write(p)
+}
+
+func (t *tarDigester) Close() error {
+ if t.isOpen {
+ t.isOpen = false
+ return t.tarFilterer.Close()
+ }
+ return nil
+}
+
+// CompositeDigester can compute a digest over multiple items.
+type CompositeDigester struct {
+ digesters []digester
+ closer io.Closer
+}
+
+// closeOpenDigester closes an open sub-digester, if we have one.
+func (c *CompositeDigester) closeOpenDigester() {
+ if c.closer != nil {
+ c.closer.Close()
+ c.closer = nil
+ }
+}
+
+// Restart clears all state, so that the composite digester can start over.
+func (c *CompositeDigester) Restart() {
+ c.closeOpenDigester()
+ c.digesters = nil
+}
+
+// Start starts recording the digest for a new item ("", "file", or "dir").
+// The caller should call Hash() immediately after to retrieve the new
+// io.WriteCloser.
+func (c *CompositeDigester) Start(contentType string) {
+ c.closeOpenDigester()
+ switch contentType {
+ case "":
+ c.digesters = append(c.digesters, newSimpleDigester(""))
+ case "file", "dir":
+ digester := newTarDigester(contentType)
+ c.closer = digester
+ c.digesters = append(c.digesters, digester)
+ default:
+ panic(fmt.Sprintf(`unrecognized content type: expected "", "file", or "dir", got %q`, contentType))
+ }
+}
+
+// Hash returns the hasher for the current item.
+func (c *CompositeDigester) Hash() io.WriteCloser {
+ num := len(c.digesters)
+ if num == 0 {
+ return nil
+ }
+ return c.digesters[num-1]
+}
+
+// Digest returns the content type and a composite digest over everything
+// that's been digested.
+func (c *CompositeDigester) Digest() (string, digest.Digest) {
+ c.closeOpenDigester()
+ num := len(c.digesters)
+ switch num {
+ case 0:
+ return "", ""
+ case 1:
+ return c.digesters[0].ContentType(), c.digesters[0].Digest()
+ default:
+ content := ""
+ for i, digester := range c.digesters {
+ if i > 0 {
+ content += ","
+ }
+ contentType := digester.ContentType()
+ if contentType != "" {
+ contentType += ":"
+ }
+ content += contentType + digester.Digest().Encoded()
+ }
+ return "multi", digest.Canonical.FromString(content)
+ }
+}
diff --git a/vendor/github.com/containers/buildah/docker/AUTHORS b/vendor/github.com/containers/buildah/docker/AUTHORS
new file mode 100644
index 00000000..b2cd9ecb
--- /dev/null
+++ b/vendor/github.com/containers/buildah/docker/AUTHORS
@@ -0,0 +1,1788 @@
+# This file lists all individuals having contributed content to the repository.
+# For how it is generated, see `hack/generate-authors.sh`.
+
+Aanand Prasad
+Aaron Davidson
+Aaron Feng
+Aaron Huslage
+Aaron Lehmann
+Aaron Welch
+Aaron.L.Xu
+Abel Muiño
+Abhijeet Kasurde
+Abhinav Ajgaonkar
+Abhishek Chanda
+Abin Shahab
+Adam Avilla
+Adam Eijdenberg
+Adam Kunk
+Adam Miller
+Adam Mills
+Adam Singer
+Adam Walz
+Addam Hardy
+Aditi Rajagopal
+Aditya
+Adolfo Ochagavía
+Adria Casas
+Adrian Moisey
+Adrian Mouat
+Adrian Oprea
+Adrien Folie
+Adrien Gallouët
+Ahmed Kamal
+Ahmet Alp Balkan
+Aidan Feldman
+Aidan Hobson Sayers
+AJ Bowen
+Ajey Charantimath
+ajneu
+Akihiro Suda
+Akira Koyasu
+Akshay Karle
+Al Tobey
+alambike
+Alan Scherger
+Alan Thompson
+Albert Callarisa
+Albert Zhang
+Aleksa Sarai
+Aleksandrs Fadins
+Alena Prokharchyk
+Alessandro Boch
+Alessio Biancalana
+Alex Chan
+Alex Chen
+Alex Coventry
+Alex Crawford
+Alex Ellis
+Alex Gaynor
+Alex Olshansky
+Alex Samorukov
+Alex Warhawk
+Alexander Artemenko
+Alexander Boyd
+Alexander Larsson
+Alexander Morozov
+Alexander Shopov
+Alexandre Beslic
+Alexandre González
+Alexandru Sfirlogea
+Alexey Guskov
+Alexey Kotlyarov
+Alexey Shamrin
+Alexis THOMAS
+Alfred Landrum
+Ali Dehghani
+Alicia Lauerman
+Alihan Demir
+Allen Madsen
+Allen Sun
+almoehi
+Alvaro Saurin
+Alvin Richards
+amangoel
+Amen Belayneh
+Amir Goldstein
+Amit Bakshi
+Amit Krishnan
+Amit Shukla
+Amy Lindburg
+Anand Patil
+AnandkumarPatel
+Anatoly Borodin
+Anchal Agrawal
+Anders Janmyr
+Andre Dublin <81dublin@gmail.com>
+Andre Granovsky
+Andrea Luzzardi
+Andrea Turli
+Andreas Köhler
+Andreas Savvides
+Andreas Tiefenthaler
+Andrei Gherzan
+Andrew C. Bodine
+Andrew Clay Shafer
+Andrew Duckworth
+Andrew France
+Andrew Gerrand
+Andrew Guenther
+Andrew Hsu
+Andrew Kuklewicz
+Andrew Macgregor
+Andrew Macpherson
+Andrew Martin
+Andrew McDonnell
+Andrew Munsell
+Andrew Po
+Andrew Weiss
+Andrew Williams
+Andrews Medina
+Andrey Petrov
+Andrey Stolbovsky
+André Martins
+andy
+Andy Chambers
+andy diller
+Andy Goldstein
+Andy Kipp
+Andy Rothfusz
+Andy Smith
+Andy Wilson
+Anes Hasicic
+Anil Belur
+Anil Madhavapeddy
+Ankush Agarwal
+Anonmily
+Anran Qiao
+Anthon van der Neut
+Anthony Baire
+Anthony Bishopric
+Anthony Dahanne
+Anthony Sottile
+Anton Löfgren
+Anton Nikitin
+Anton Polonskiy
+Anton Tiurin
+Antonio Murdaca
+Antonis Kalipetis
+Antony Messerli
+Anuj Bahuguna
+Anusha Ragunathan
+apocas
+Arash Deshmeh
+ArikaChen
+Arnaud Lefebvre
+Arnaud Porterie
+Arthur Barr
+Arthur Gautier
+Artur Meyster
+Arun Gupta
+Asbjørn Enge
+averagehuman
+Avi Das
+Avi Miller
+Avi Vaid
+ayoshitake
+Azat Khuyiyakhmetov
+Bardia Keyoumarsi
+Barnaby Gray
+Barry Allard
+Bartłomiej Piotrowski
+Bastiaan Bakker
+bdevloed
+Ben Bonnefoy
+Ben Firshman
+Ben Golub
+Ben Hall
+Ben Sargent
+Ben Severson
+Ben Toews
+Ben Wiklund
+Benjamin Atkin
+Benoit Chesneau
+Bernerd Schaefer
+Bert Goethals
+Bharath Thiruveedula
+Bhiraj Butala
+Bhumika Bayani
+Bilal Amarni
+Bill W
+bin liu
+Bingshen Wang
+Blake Geno
+Boaz Shuster
+bobby abbott
+Boshi Lian
+boucher
+Bouke Haarsma
+Boyd Hemphill
+boynux
+Bradley Cicenas
+Bradley Wright
+Brandon Liu
+Brandon Philips
+Brandon Rhodes
+Brendan Dixon