Refactor and enhance push command and various bug fixes
- Added progress bar for image upload using github.com/cheggaaa/pb - Refactored HTTP request handling to support streaming - Enhanced image name and size validation - Refactored PostPush to streamline image validation and storage - Update UploadImagePOSTHandler to remove image from storage after import - Minor HTML and CSS adjustments for better UI consistency - Standardized error messages in Docker package - Fix ExportImageFromEngine not returning metadata - Rewrite GetImageInfo, GetImageSize, and GetImageTag methods
- commit
- 863a4b7591a091efa5a1ecd57f95222065bbffa5
- author
- bnema · · 2023-10-31 18:37 UTC
- parent
- 534fa77
- signature
- unsigned
17 files · +262 -171
M .gitignore +2 -1
| @@ -10,4 +10,5 @@ bun.lockb | |||
| 10 | 10 | main | |
| 11 | 11 | gordon | |
| 12 | 12 | dist/ | |
| 13 | - | data/⏎̸ | |
| 13 | + | data/ | |
| 14 | + | setup.sh⏎̸ | |
| @@ -10,4 +10,5 @@ bun.lockb | |||||
| 10 | main | 10 | main | ||
| 11 | gordon | 11 | gordon | ||
| 12 | dist/ | 12 | dist/ | ||
| 13 | - | data/⏎̸ | 13 | + | data/ |
| 14 | + | setup.sh⏎̸ | |||
M .goreleaser.yaml +2 -0
| @@ -12,6 +12,8 @@ builds: | |||
| 12 | 12 | - arm64 | |
| 13 | 13 | env: | |
| 14 | 14 | - CGO_ENABLED=0 | |
| 15 | + | ldflags: | |
| 16 | + | - -s -w -X main.build={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} | |
| 15 | 17 | ||
| 16 | 18 | dockers: | |
| 17 | 19 | - image_templates: | |
| @@ -12,6 +12,8 @@ builds: | |||||
| 12 | - arm64 | 12 | - arm64 | ||
| 13 | env: | 13 | env: | ||
| 14 | - CGO_ENABLED=0 | 14 | - CGO_ENABLED=0 | ||
| 15 | + | ldflags: | |||
| 16 | + | - -s -w -X main.build={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} | |||
| 15 | 17 | ||||
| 16 | dockers: | 18 | dockers: | ||
| 17 | - image_templates: | 19 | - image_templates: | ||
M go.mod +3 -0
| @@ -4,6 +4,7 @@ go 1.21.3 | |||
| 4 | 4 | ||
| 5 | 5 | require ( | |
| 6 | 6 | github.com/PuerkitoBio/goquery v1.8.1 | |
| 7 | + | github.com/cheggaaa/pb v1.0.29 | |
| 7 | 8 | github.com/docker/docker v24.0.6+incompatible | |
| 8 | 9 | github.com/docker/go-connections v0.4.0 | |
| 9 | 10 | github.com/gorilla/sessions v1.2.1 | |
| @@ -21,8 +22,10 @@ require ( | |||
| 21 | 22 | github.com/inconshreveable/mousetrap v1.1.0 // indirect | |
| 22 | 23 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect | |
| 23 | 24 | github.com/kr/text v0.2.0 // indirect | |
| 25 | + | github.com/mattn/go-runewidth v0.0.15 // indirect | |
| 24 | 26 | github.com/mattn/go-sqlite3 v1.14.17 // indirect | |
| 25 | 27 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect | |
| 28 | + | github.com/rivo/uniseg v0.4.4 // indirect | |
| 26 | 29 | github.com/spf13/pflag v1.0.5 // indirect | |
| 27 | 30 | github.com/stretchr/testify v1.8.2 // indirect | |
| 28 | 31 | lukechampine.com/uint128 v1.3.0 // indirect | |
| @@ -4,6 +4,7 @@ go 1.21.3 | |||||
| 4 | 4 | ||||
| 5 | require ( | 5 | require ( | ||
| 6 | github.com/PuerkitoBio/goquery v1.8.1 | 6 | github.com/PuerkitoBio/goquery v1.8.1 | ||
| 7 | + | github.com/cheggaaa/pb v1.0.29 | |||
| 7 | github.com/docker/docker v24.0.6+incompatible | 8 | github.com/docker/docker v24.0.6+incompatible | ||
| 8 | github.com/docker/go-connections v0.4.0 | 9 | github.com/docker/go-connections v0.4.0 | ||
| 9 | github.com/gorilla/sessions v1.2.1 | 10 | github.com/gorilla/sessions v1.2.1 | ||
| @@ -21,8 +22,10 @@ require ( | |||||
| 21 | github.com/inconshreveable/mousetrap v1.1.0 // indirect | 22 | github.com/inconshreveable/mousetrap v1.1.0 // indirect | ||
| 22 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect | 23 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect | ||
| 23 | github.com/kr/text v0.2.0 // indirect | 24 | github.com/kr/text v0.2.0 // indirect | ||
| 25 | + | github.com/mattn/go-runewidth v0.0.15 // indirect | |||
| 24 | github.com/mattn/go-sqlite3 v1.14.17 // indirect | 26 | github.com/mattn/go-sqlite3 v1.14.17 // indirect | ||
| 25 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect | 27 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect | ||
| 28 | + | github.com/rivo/uniseg v0.4.4 // indirect | |||
| 26 | github.com/spf13/pflag v1.0.5 // indirect | 29 | github.com/spf13/pflag v1.0.5 // indirect | ||
| 27 | github.com/stretchr/testify v1.8.2 // indirect | 30 | github.com/stretchr/testify v1.8.2 // indirect | ||
| 28 | lukechampine.com/uint128 v1.3.0 // indirect | 31 | lukechampine.com/uint128 v1.3.0 // indirect | ||
M go.sum +15 -0
| @@ -8,6 +8,8 @@ github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x0 | |||
| 8 | 8 | github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= | |
| 9 | 9 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= | |
| 10 | 10 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= | |
| 11 | + | github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo= | |
| 12 | + | github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30= | |
| 11 | 13 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | |
| 12 | 14 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | |
| 13 | 15 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |
| @@ -23,6 +25,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 | |||
| 23 | 25 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= | |
| 24 | 26 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= | |
| 25 | 27 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= | |
| 28 | + | github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= | |
| 29 | + | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= | |
| 26 | 30 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= | |
| 27 | 31 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= | |
| 28 | 32 | github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= | |
| @@ -59,13 +63,19 @@ github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUU | |||
| 59 | 63 | github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= | |
| 60 | 64 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= | |
| 61 | 65 | github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= | |
| 66 | + | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= | |
| 62 | 67 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= | |
| 63 | 68 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | |
| 64 | 69 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= | |
| 70 | + | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | |
| 71 | + | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= | |
| 65 | 72 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= | |
| 66 | 73 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | |
| 67 | 74 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | |
| 68 | 75 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | |
| 76 | + | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= | |
| 77 | + | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= | |
| 78 | + | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= | |
| 69 | 79 | github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= | |
| 70 | 80 | github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= | |
| 71 | 81 | github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= | |
| @@ -84,6 +94,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb | |||
| 84 | 94 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | |
| 85 | 95 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= | |
| 86 | 96 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= | |
| 97 | + | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= | |
| 98 | + | github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= | |
| 99 | + | github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= | |
| 87 | 100 | github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= | |
| 88 | 101 | github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= | |
| 89 | 102 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | |
| @@ -135,7 +148,9 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ | |||
| 135 | 148 | golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= | |
| 136 | 149 | golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= | |
| 137 | 150 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
| 151 | + | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
| 138 | 152 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 153 | + | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 139 | 154 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 140 | 155 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 141 | 156 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| @@ -8,6 +8,8 @@ github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x0 | |||||
| 8 | github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= | 8 | github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= | ||
| 9 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= | 9 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= | ||
| 10 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= | 10 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= | ||
| 11 | + | github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo= | |||
| 12 | + | github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30= | |||
| 11 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | 13 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||
| 12 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | 14 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | ||
| 13 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 15 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
| @@ -23,6 +25,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 | |||||
| 23 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= | 25 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= | ||
| 24 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= | 26 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= | ||
| 25 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= | 27 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= | ||
| 28 | + | github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= | |||
| 29 | + | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= | |||
| 26 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= | 30 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= | ||
| 27 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= | 31 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= | ||
| 28 | github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= | 32 | github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= | ||
| @@ -59,13 +63,19 @@ github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUU | |||||
| 59 | github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= | 63 | github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= | ||
| 60 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= | 64 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= | ||
| 61 | github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= | 65 | github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= | ||
| 66 | + | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= | |||
| 62 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= | 67 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= | ||
| 63 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | 68 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | ||
| 64 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= | 69 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= | ||
| 70 | + | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | |||
| 71 | + | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= | |||
| 65 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= | 72 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= | ||
| 66 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | 73 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | ||
| 67 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | 74 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | ||
| 68 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | 75 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||
| 76 | + | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= | |||
| 77 | + | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= | |||
| 78 | + | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= | |||
| 69 | github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= | 79 | github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= | ||
| 70 | github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= | 80 | github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= | ||
| 71 | github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= | 81 | github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= | ||
| @@ -84,6 +94,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb | |||||
| 84 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | 94 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
| 85 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= | 95 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= | ||
| 86 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= | 96 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= | ||
| 97 | + | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= | |||
| 98 | + | github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= | |||
| 99 | + | github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= | |||
| 87 | github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= | 100 | github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= | ||
| 88 | github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= | 101 | github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= | ||
| 89 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | 102 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||
| @@ -135,7 +148,9 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ | |||||
| 135 | golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= | 148 | golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= | ||
| 136 | golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= | 149 | golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= | ||
| 137 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | 150 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
| 151 | + | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |||
| 138 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 152 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
| 153 | + | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
| 139 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 154 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
| 140 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 155 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
| 141 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 156 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
M internal/cli/cmd/push.go +42 -24
| @@ -3,34 +3,20 @@ package cmd | |||
| 3 | 3 | import ( | |
| 4 | 4 | "fmt" | |
| 5 | 5 | "os" | |
| 6 | + | "regexp" | |
| 6 | 7 | ||
| 7 | 8 | "github.com/bnema/gordon/internal/cli" | |
| 8 | 9 | "github.com/bnema/gordon/internal/cli/handler" | |
| 9 | 10 | "github.com/bnema/gordon/internal/common" | |
| 10 | 11 | "github.com/bnema/gordon/pkg/docker" | |
| 12 | + | "github.com/cheggaaa/pb" | |
| 11 | 13 | "github.com/spf13/cobra" | |
| 12 | 14 | ) | |
| 13 | 15 | ||
| 14 | - | // the push command is used to push a container image to gordon's endpoint | |
| 15 | - | ||
| 16 | - | // Pseudo code of the steps for the push command | |
| 17 | - | ||
| 18 | - | // 1. Extract the container image as .tar and store it in a temporary directory | |
| 19 | - | ||
| 20 | - | // 2. Prepare a payload with the .tar as a byte array, the image name and tag (if any) | |
| 21 | - | ||
| 22 | - | // 3. append the payload to the request body with the type "push" and the token | |
| 23 | - | ||
| 24 | - | // 4. Send the request to the backend | |
| 25 | - | ||
| 26 | - | // 5. If the response is 200, print the success message | |
| 27 | - | ||
| 28 | 16 | func NewPushCommand(a *cli.App) *cobra.Command { | |
| 29 | 17 | var port string | |
| 30 | 18 | var targetDomain string | |
| 31 | 19 | ||
| 32 | - | // handler.FieldCheck(a) | |
| 33 | - | ||
| 34 | 20 | pushCmd := &cobra.Command{ | |
| 35 | 21 | Use: "push [image:tag]", | |
| 36 | 22 | Short: "Push an image to the server, if no tag is specified, latest is used", | |
| @@ -44,32 +30,64 @@ func NewPushCommand(a *cli.App) *cobra.Command { | |||
| 44 | 30 | Run: func(cmd *cobra.Command, args []string) { | |
| 45 | 31 | imageName := args[0] | |
| 46 | 32 | fmt.Println("Exporting image:", imageName) | |
| 47 | - | // Export the image using Docker client | |
| 48 | - | data, err := docker.ExportImageFromEngine(imageName) | |
| 33 | + | ||
| 34 | + | // Check if the image name is valid or if a tag is specified | |
| 35 | + | match, _ := regexp.MatchString(`^([a-zA-Z0-9\-_.]+\/)?[a-zA-Z0-9\-_.]+(:[a-zA-Z0-9\-_.]+)?$`, imageName) | |
| 36 | + | if !match { | |
| 37 | + | fmt.Println("Invalid image name or no tag specified") | |
| 38 | + | return | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | imageID, err := docker.GetImageID(imageName) | |
| 42 | + | if err != nil { | |
| 43 | + | fmt.Println("Error getting image ID:", err) | |
| 44 | + | return | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | totalSize, err := docker.GetImageSize(imageID) | |
| 48 | + | if err != nil { | |
| 49 | + | fmt.Println("Error estimating image size:", err) | |
| 50 | + | return | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | // Create a new progress bar | |
| 54 | + | bar := pb.New64(totalSize).SetUnits(pb.U_BYTES) | |
| 55 | + | reader, err := docker.ExportImageFromEngine(imageID) | |
| 49 | 56 | if err != nil { | |
| 50 | 57 | fmt.Println("Error exporting image:", err) | |
| 51 | 58 | return | |
| 52 | 59 | } | |
| 53 | - | fmt.Println("Image exported successfully") | |
| 60 | + | // Wrap the original reader with progress bar reader | |
| 61 | + | progressReader := bar.NewProxyReader(reader) | |
| 62 | + | ||
| 54 | 63 | // Create a RequestPayload and populate it | |
| 55 | - | reqPayload := common.RequestPayload{ // <- TODO : Load RequestPayLoad from cli.App | |
| 64 | + | reqPayload := common.RequestPayload{ | |
| 56 | 65 | Type: "push", | |
| 57 | - | Payload: common.PushPayload{ // <- Same | |
| 66 | + | Payload: common.PushPayload{ | |
| 58 | 67 | Ports: port, | |
| 59 | 68 | TargetDomain: targetDomain, | |
| 60 | 69 | ImageName: imageName, | |
| 61 | - | Data: data, | |
| 70 | + | Data: progressReader, | |
| 62 | 71 | }, | |
| 63 | 72 | } | |
| 64 | - | fmt.Println("Sending image to backend") | |
| 73 | + | ||
| 74 | + | // Start the progress bar | |
| 75 | + | bar.Start() | |
| 65 | 76 | // Send the request to the backend | |
| 66 | 77 | resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push") | |
| 67 | 78 | if err != nil { | |
| 68 | 79 | fmt.Println("Error sending HTTP request:", err) | |
| 69 | 80 | return | |
| 70 | 81 | } | |
| 82 | + | // Stop the progress bar | |
| 83 | + | bar.Finish() | |
| 84 | + | ||
| 85 | + | if resp.StatusCode != 200 { | |
| 86 | + | fmt.Println("Unexpected status code:", resp.StatusCode) | |
| 87 | + | return | |
| 88 | + | } | |
| 71 | 89 | ||
| 72 | - | fmt.Print(string(resp.Body)) | |
| 90 | + | fmt.Println(resp.Body) | |
| 73 | 91 | }, | |
| 74 | 92 | } | |
| 75 | 93 | ||
| @@ -3,34 +3,20 @@ package cmd | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | "os" | 5 | "os" | ||
| 6 | + | "regexp" | |||
| 6 | 7 | ||||
| 7 | "github.com/bnema/gordon/internal/cli" | 8 | "github.com/bnema/gordon/internal/cli" | ||
| 8 | "github.com/bnema/gordon/internal/cli/handler" | 9 | "github.com/bnema/gordon/internal/cli/handler" | ||
| 9 | "github.com/bnema/gordon/internal/common" | 10 | "github.com/bnema/gordon/internal/common" | ||
| 10 | "github.com/bnema/gordon/pkg/docker" | 11 | "github.com/bnema/gordon/pkg/docker" | ||
| 12 | + | "github.com/cheggaaa/pb" | |||
| 11 | "github.com/spf13/cobra" | 13 | "github.com/spf13/cobra" | ||
| 12 | ) | 14 | ) | ||
| 13 | 15 | ||||
| 14 | - | // the push command is used to push a container image to gordon's endpoint | |||
| 15 | - | ||||
| 16 | - | // Pseudo code of the steps for the push command | |||
| 17 | - | ||||
| 18 | - | // 1. Extract the container image as .tar and store it in a temporary directory | |||
| 19 | - | ||||
| 20 | - | // 2. Prepare a payload with the .tar as a byte array, the image name and tag (if any) | |||
| 21 | - | ||||
| 22 | - | // 3. append the payload to the request body with the type "push" and the token | |||
| 23 | - | ||||
| 24 | - | // 4. Send the request to the backend | |||
| 25 | - | ||||
| 26 | - | // 5. If the response is 200, print the success message | |||
| 27 | - | ||||
| 28 | func NewPushCommand(a *cli.App) *cobra.Command { | 16 | func NewPushCommand(a *cli.App) *cobra.Command { | ||
| 29 | var port string | 17 | var port string | ||
| 30 | var targetDomain string | 18 | var targetDomain string | ||
| 31 | 19 | ||||
| 32 | - | // handler.FieldCheck(a) | |||
| 33 | - | ||||
| 34 | pushCmd := &cobra.Command{ | 20 | pushCmd := &cobra.Command{ | ||
| 35 | Use: "push [image:tag]", | 21 | Use: "push [image:tag]", | ||
| 36 | Short: "Push an image to the server, if no tag is specified, latest is used", | 22 | Short: "Push an image to the server, if no tag is specified, latest is used", | ||
| @@ -44,32 +30,64 @@ func NewPushCommand(a *cli.App) *cobra.Command { | |||||
| 44 | Run: func(cmd *cobra.Command, args []string) { | 30 | Run: func(cmd *cobra.Command, args []string) { | ||
| 45 | imageName := args[0] | 31 | imageName := args[0] | ||
| 46 | fmt.Println("Exporting image:", imageName) | 32 | fmt.Println("Exporting image:", imageName) | ||
| 47 | - | // Export the image using Docker client | 33 | + | |
| 48 | - | data, err := docker.ExportImageFromEngine(imageName) | 34 | + | // Check if the image name is valid or if a tag is specified |
| 35 | + | match, _ := regexp.MatchString(`^([a-zA-Z0-9\-_.]+\/)?[a-zA-Z0-9\-_.]+(:[a-zA-Z0-9\-_.]+)?$`, imageName) | |||
| 36 | + | if !match { | |||
| 37 | + | fmt.Println("Invalid image name or no tag specified") | |||
| 38 | + | return | |||
| 39 | + | } | |||
| 40 | + | ||||
| 41 | + | imageID, err := docker.GetImageID(imageName) | |||
| 42 | + | if err != nil { | |||
| 43 | + | fmt.Println("Error getting image ID:", err) | |||
| 44 | + | return | |||
| 45 | + | } | |||
| 46 | + | ||||
| 47 | + | totalSize, err := docker.GetImageSize(imageID) | |||
| 48 | + | if err != nil { | |||
| 49 | + | fmt.Println("Error estimating image size:", err) | |||
| 50 | + | return | |||
| 51 | + | } | |||
| 52 | + | ||||
| 53 | + | // Create a new progress bar | |||
| 54 | + | bar := pb.New64(totalSize).SetUnits(pb.U_BYTES) | |||
| 55 | + | reader, err := docker.ExportImageFromEngine(imageID) | |||
| 49 | if err != nil { | 56 | if err != nil { | ||
| 50 | fmt.Println("Error exporting image:", err) | 57 | fmt.Println("Error exporting image:", err) | ||
| 51 | return | 58 | return | ||
| 52 | } | 59 | } | ||
| 53 | - | fmt.Println("Image exported successfully") | 60 | + | // Wrap the original reader with progress bar reader |
| 61 | + | progressReader := bar.NewProxyReader(reader) | |||
| 62 | + | ||||
| 54 | // Create a RequestPayload and populate it | 63 | // Create a RequestPayload and populate it | ||
| 55 | - | reqPayload := common.RequestPayload{ // <- TODO : Load RequestPayLoad from cli.App | 64 | + | reqPayload := common.RequestPayload{ |
| 56 | Type: "push", | 65 | Type: "push", | ||
| 57 | - | Payload: common.PushPayload{ // <- Same | 66 | + | Payload: common.PushPayload{ |
| 58 | Ports: port, | 67 | Ports: port, | ||
| 59 | TargetDomain: targetDomain, | 68 | TargetDomain: targetDomain, | ||
| 60 | ImageName: imageName, | 69 | ImageName: imageName, | ||
| 61 | - | Data: data, | 70 | + | Data: progressReader, |
| 62 | }, | 71 | }, | ||
| 63 | } | 72 | } | ||
| 64 | - | fmt.Println("Sending image to backend") | 73 | + | |
| 74 | + | // Start the progress bar | |||
| 75 | + | bar.Start() | |||
| 65 | // Send the request to the backend | 76 | // Send the request to the backend | ||
| 66 | resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push") | 77 | resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push") | ||
| 67 | if err != nil { | 78 | if err != nil { | ||
| 68 | fmt.Println("Error sending HTTP request:", err) | 79 | fmt.Println("Error sending HTTP request:", err) | ||
| 69 | return | 80 | return | ||
| 70 | } | 81 | } | ||
| 82 | + | // Stop the progress bar | |||
| 83 | + | bar.Finish() | |||
| 84 | + | ||||
| 85 | + | if resp.StatusCode != 200 { | |||
| 86 | + | fmt.Println("Unexpected status code:", resp.StatusCode) | |||
| 87 | + | return | |||
| 88 | + | } | |||
| 71 | 89 | ||||
| 72 | - | fmt.Print(string(resp.Body)) | 90 | + | fmt.Println(resp.Body) |
| 73 | }, | 91 | }, | ||
| 74 | } | 92 | } | ||
| 75 | 93 | ||||
M internal/cli/handler/http.go +46 -13
| @@ -23,8 +23,7 @@ func CreateNewRequest(method, url string, body []byte) (*http.Request, error) { | |||
| 23 | 23 | } | |
| 24 | 24 | ||
| 25 | 25 | // SetRequestHeaders sets the headers for the request | |
| 26 | - | func SetRequestHeaders(req *http.Request, token string) { | |
| 27 | - | req.Header.Set("Content-Type", "application/json") | |
| 26 | + | func setAuthRequestHeaders(req *http.Request, token string) { | |
| 28 | 27 | req.Header.Set("Authorization", "Bearer "+token) | |
| 29 | 28 | } | |
| 30 | 29 | ||
| @@ -35,7 +34,8 @@ func SendRequest(req *http.Request) (*http.Response, error) { | |||
| 35 | 34 | } | |
| 36 | 35 | ||
| 37 | 36 | type Response struct { | |
| 38 | - | Body []byte | |
| 37 | + | Body []byte | |
| 38 | + | StatusCode int | |
| 39 | 39 | } | |
| 40 | 40 | ||
| 41 | 41 | // SendHTTPRequest sends the HTTP request | |
| @@ -43,29 +43,62 @@ func SendHTTPRequest(a *cli.App, rp *common.RequestPayload, method string, endpo | |||
| 43 | 43 | apiUrl := a.Config.Http.BackendURL + "/api" | |
| 44 | 44 | token := a.Config.General.Token | |
| 45 | 45 | ||
| 46 | - | fmt.Printf("Final url:", apiUrl+endpoint) | |
| 46 | + | var req *http.Request | |
| 47 | + | var err error | |
| 48 | + | ||
| 49 | + | // Handle push type payload | |
| 50 | + | if rp.Type == "push" { | |
| 51 | + | pushPayload, ok := rp.Payload.(common.PushPayload) | |
| 52 | + | if !ok { | |
| 53 | + | return nil, fmt.Errorf("invalid payload type for push") | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | req, err = http.NewRequest(method, apiUrl+endpoint, pushPayload.Data) | |
| 57 | + | if err != nil { | |
| 58 | + | return nil, fmt.Errorf("failed to create new streaming request: %w", err) | |
| 59 | + | } | |
| 47 | 60 | ||
| 48 | - | // Prepare the entire RequestPayload, not just the inner Payload | |
| 61 | + | setAuthRequestHeaders(req, token) | |
| 62 | + | req.Header.Set("Content-Type", "application/octet-stream") | |
| 63 | + | req.Header.Set("X-Ports", pushPayload.Ports) | |
| 64 | + | req.Header.Set("X-Target-Domain", pushPayload.TargetDomain) | |
| 65 | + | req.Header.Set("X-Image-Name", pushPayload.ImageName) | |
| 66 | + | ||
| 67 | + | client := &http.Client{} | |
| 68 | + | resp, err := client.Do(req) | |
| 69 | + | if err != nil { | |
| 70 | + | return nil, fmt.Errorf("failed to send request: %w", err) | |
| 71 | + | } | |
| 72 | + | defer resp.Body.Close() | |
| 73 | + | ||
| 74 | + | body, err := io.ReadAll(resp.Body) | |
| 75 | + | if err != nil { | |
| 76 | + | return nil, fmt.Errorf("failed to read response body: %w", err) | |
| 77 | + | } | |
| 78 | + | return &Response{Body: body}, nil | |
| 79 | + | } | |
| 80 | + | ||
| 81 | + | // Handle other types of payloads | |
| 49 | 82 | jsonPayload, err := json.Marshal(rp) | |
| 50 | 83 | if err != nil { | |
| 51 | 84 | return nil, fmt.Errorf("failed to marshal payload: %w", err) | |
| 52 | 85 | } | |
| 53 | - | // Create a new request | |
| 54 | - | req, err := CreateNewRequest(method, apiUrl+endpoint, jsonPayload) | |
| 86 | + | ||
| 87 | + | req, err = http.NewRequest(method, apiUrl+endpoint, bytes.NewBuffer(jsonPayload)) | |
| 55 | 88 | if err != nil { | |
| 56 | - | return nil, fmt.Errorf("failed to create new request: %w", err) | |
| 89 | + | return nil, fmt.Errorf("failed to create new JSON request: %w", err) | |
| 57 | 90 | } | |
| 58 | - | // Set the request headers | |
| 59 | - | SetRequestHeaders(req, token) | |
| 60 | 91 | ||
| 61 | - | // Send the request | |
| 62 | - | resp, err := SendRequest(req) | |
| 92 | + | setAuthRequestHeaders(req, token) | |
| 93 | + | req.Header.Set("Content-Type", "application/json") | |
| 94 | + | ||
| 95 | + | client := &http.Client{} | |
| 96 | + | resp, err := client.Do(req) | |
| 63 | 97 | if err != nil { | |
| 64 | 98 | return nil, fmt.Errorf("failed to send request: %w", err) | |
| 65 | 99 | } | |
| 66 | 100 | defer resp.Body.Close() | |
| 67 | 101 | ||
| 68 | - | // Read the response body | |
| 69 | 102 | body, err := io.ReadAll(resp.Body) | |
| 70 | 103 | if err != nil { | |
| 71 | 104 | return nil, fmt.Errorf("failed to read response body: %w", err) | |
| @@ -23,8 +23,7 @@ func CreateNewRequest(method, url string, body []byte) (*http.Request, error) { | |||||
| 23 | } | 23 | } | ||
| 24 | 24 | ||||
| 25 | // SetRequestHeaders sets the headers for the request | 25 | // SetRequestHeaders sets the headers for the request | ||
| 26 | - | func SetRequestHeaders(req *http.Request, token string) { | 26 | + | func setAuthRequestHeaders(req *http.Request, token string) { |
| 27 | - | req.Header.Set("Content-Type", "application/json") | |||
| 28 | req.Header.Set("Authorization", "Bearer "+token) | 27 | req.Header.Set("Authorization", "Bearer "+token) | ||
| 29 | } | 28 | } | ||
| 30 | 29 | ||||
| @@ -35,7 +34,8 @@ func SendRequest(req *http.Request) (*http.Response, error) { | |||||
| 35 | } | 34 | } | ||
| 36 | 35 | ||||
| 37 | type Response struct { | 36 | type Response struct { | ||
| 38 | - | Body []byte | 37 | + | Body []byte |
| 38 | + | StatusCode int | |||
| 39 | } | 39 | } | ||
| 40 | 40 | ||||
| 41 | // SendHTTPRequest sends the HTTP request | 41 | // SendHTTPRequest sends the HTTP request | ||
| @@ -43,29 +43,62 @@ func SendHTTPRequest(a *cli.App, rp *common.RequestPayload, method string, endpo | |||||
| 43 | apiUrl := a.Config.Http.BackendURL + "/api" | 43 | apiUrl := a.Config.Http.BackendURL + "/api" | ||
| 44 | token := a.Config.General.Token | 44 | token := a.Config.General.Token | ||
| 45 | 45 | ||||
| 46 | - | fmt.Printf("Final url:", apiUrl+endpoint) | 46 | + | var req *http.Request |
| 47 | + | var err error | |||
| 48 | + | ||||
| 49 | + | // Handle push type payload | |||
| 50 | + | if rp.Type == "push" { | |||
| 51 | + | pushPayload, ok := rp.Payload.(common.PushPayload) | |||
| 52 | + | if !ok { | |||
| 53 | + | return nil, fmt.Errorf("invalid payload type for push") | |||
| 54 | + | } | |||
| 55 | + | ||||
| 56 | + | req, err = http.NewRequest(method, apiUrl+endpoint, pushPayload.Data) | |||
| 57 | + | if err != nil { | |||
| 58 | + | return nil, fmt.Errorf("failed to create new streaming request: %w", err) | |||
| 59 | + | } | |||
| 47 | 60 | ||||
| 48 | - | // Prepare the entire RequestPayload, not just the inner Payload | 61 | + | setAuthRequestHeaders(req, token) |
| 62 | + | req.Header.Set("Content-Type", "application/octet-stream") | |||
| 63 | + | req.Header.Set("X-Ports", pushPayload.Ports) | |||
| 64 | + | req.Header.Set("X-Target-Domain", pushPayload.TargetDomain) | |||
| 65 | + | req.Header.Set("X-Image-Name", pushPayload.ImageName) | |||
| 66 | + | ||||
| 67 | + | client := &http.Client{} | |||
| 68 | + | resp, err := client.Do(req) | |||
| 69 | + | if err != nil { | |||
| 70 | + | return nil, fmt.Errorf("failed to send request: %w", err) | |||
| 71 | + | } | |||
| 72 | + | defer resp.Body.Close() | |||
| 73 | + | ||||
| 74 | + | body, err := io.ReadAll(resp.Body) | |||
| 75 | + | if err != nil { | |||
| 76 | + | return nil, fmt.Errorf("failed to read response body: %w", err) | |||
| 77 | + | } | |||
| 78 | + | return &Response{Body: body}, nil | |||
| 79 | + | } | |||
| 80 | + | ||||
| 81 | + | // Handle other types of payloads | |||
| 49 | jsonPayload, err := json.Marshal(rp) | 82 | jsonPayload, err := json.Marshal(rp) | ||
| 50 | if err != nil { | 83 | if err != nil { | ||
| 51 | return nil, fmt.Errorf("failed to marshal payload: %w", err) | 84 | return nil, fmt.Errorf("failed to marshal payload: %w", err) | ||
| 52 | } | 85 | } | ||
| 53 | - | // Create a new request | 86 | + | |
| 54 | - | req, err := CreateNewRequest(method, apiUrl+endpoint, jsonPayload) | 87 | + | req, err = http.NewRequest(method, apiUrl+endpoint, bytes.NewBuffer(jsonPayload)) |
| 55 | if err != nil { | 88 | if err != nil { | ||
| 56 | - | return nil, fmt.Errorf("failed to create new request: %w", err) | 89 | + | return nil, fmt.Errorf("failed to create new JSON request: %w", err) |
| 57 | } | 90 | } | ||
| 58 | - | // Set the request headers | |||
| 59 | - | SetRequestHeaders(req, token) | |||
| 60 | 91 | ||||
| 61 | - | // Send the request | 92 | + | setAuthRequestHeaders(req, token) |
| 62 | - | resp, err := SendRequest(req) | 93 | + | req.Header.Set("Content-Type", "application/json") |
| 94 | + | ||||
| 95 | + | client := &http.Client{} | |||
| 96 | + | resp, err := client.Do(req) | |||
| 63 | if err != nil { | 97 | if err != nil { | ||
| 64 | return nil, fmt.Errorf("failed to send request: %w", err) | 98 | return nil, fmt.Errorf("failed to send request: %w", err) | ||
| 65 | } | 99 | } | ||
| 66 | defer resp.Body.Close() | 100 | defer resp.Body.Close() | ||
| 67 | 101 | ||||
| 68 | - | // Read the response body | |||
| 69 | body, err := io.ReadAll(resp.Body) | 102 | body, err := io.ReadAll(resp.Body) | ||
| 70 | if err != nil { | 103 | if err != nil { | ||
| 71 | return nil, fmt.Errorf("failed to read response body: %w", err) | 104 | return nil, fmt.Errorf("failed to read response body: %w", err) | ||
M internal/common/payload.go +3 -1
| @@ -3,6 +3,8 @@ package common | |||
| 3 | 3 | import ( | |
| 4 | 4 | "encoding/json" | |
| 5 | 5 | "fmt" | |
| 6 | + | ||
| 7 | + | "io" | |
| 6 | 8 | ) | |
| 7 | 9 | ||
| 8 | 10 | type RequestPayload struct { | |
| @@ -71,5 +73,5 @@ type PushPayload struct { | |||
| 71 | 73 | Ports string `json:"ports"` | |
| 72 | 74 | TargetDomain string `json:"targetdomain"` | |
| 73 | 75 | ImageName string `json:"imagename"` | |
| 74 | - | Data []byte `json:"data"` | |
| 76 | + | Data io.ReadCloser | |
| 75 | 77 | } | |
| @@ -3,6 +3,8 @@ package common | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "encoding/json" | 4 | "encoding/json" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | + | ||||
| 7 | + | "io" | |||
| 6 | ) | 8 | ) | ||
| 7 | 9 | ||||
| 8 | type RequestPayload struct { | 10 | type RequestPayload struct { | ||
| @@ -71,5 +73,5 @@ type PushPayload struct { | |||||
| 71 | Ports string `json:"ports"` | 73 | Ports string `json:"ports"` | ||
| 72 | TargetDomain string `json:"targetdomain"` | 74 | TargetDomain string `json:"targetdomain"` | ||
| 73 | ImageName string `json:"imagename"` | 75 | ImageName string `json:"imagename"` | ||
| 74 | - | Data []byte `json:"data"` | 76 | + | Data io.ReadCloser |
| 75 | } | 77 | } | ||
M internal/httpserve/handler/clientendpoins.go +46 -41
| @@ -5,6 +5,7 @@ import ( | |||
| 5 | 5 | "fmt" | |
| 6 | 6 | "io" | |
| 7 | 7 | "net/http" | |
| 8 | + | "regexp" | |
| 8 | 9 | ||
| 9 | 10 | "github.com/bnema/gordon/internal/common" | |
| 10 | 11 | "github.com/bnema/gordon/internal/server" | |
| @@ -60,64 +61,68 @@ func GetInfos(c echo.Context, a *server.App) error { | |||
| 60 | 61 | } | |
| 61 | 62 | ||
| 62 | 63 | func PostPush(c echo.Context, a *server.App) error { | |
| 63 | - | body, _ := io.ReadAll(c.Request().Body) | |
| 64 | - | c.Request().Body = io.NopCloser(bytes.NewBuffer(body)) // Reset the body | |
| 65 | 64 | ||
| 66 | - | payload := new(common.RequestPayload) | |
| 67 | - | if err := c.Bind(payload); err != nil { | |
| 68 | - | fmt.Println("Bind Error:", err) | |
| 69 | - | return c.JSON(http.StatusBadRequest, err.Error()) | |
| 65 | + | // Initialize pushPayload object | |
| 66 | + | payload := &common.PushPayload{ | |
| 67 | + | Ports: c.Request().Header.Get("X-Ports"), | |
| 68 | + | ImageName: c.Request().Header.Get("X-Image-Name"), | |
| 69 | + | TargetDomain: c.Request().Header.Get("X-Target-Domain"), | |
| 70 | 70 | } | |
| 71 | 71 | ||
| 72 | - | if payload.Type != "push" { | |
| 73 | - | return c.JSON(http.StatusBadRequest, "Invalid payload type") | |
| 72 | + | if payload.ImageName == "" { | |
| 73 | + | return c.JSON(http.StatusBadRequest, "Invalid image name") | |
| 74 | 74 | } | |
| 75 | 75 | ||
| 76 | - | pushPayload, ok := payload.Payload.(common.PushPayload) | |
| 77 | - | if !ok { | |
| 78 | - | return c.JSON(http.StatusBadRequest, "Invalid payload type") | |
| 79 | - | } | |
| 76 | + | imageReader := c.Request().Body | |
| 77 | + | defer imageReader.Close() | |
| 80 | 78 | ||
| 81 | - | // Push payload contains Ports as a string, Image as string (image:tag), and the .tar image as a byte array | |
| 82 | - | // Validate the ports | |
| 83 | - | ports := pushPayload.Ports | |
| 84 | - | if ports == "" { | |
| 85 | - | return c.JSON(http.StatusBadRequest, "Ports cannot be empty") | |
| 86 | - | } | |
| 87 | - | ||
| 88 | - | // Validate the target domain | |
| 89 | - | domain := pushPayload.TargetDomain | |
| 90 | - | if domain == "" { | |
| 91 | - | return c.JSON(http.StatusBadRequest, "Image cannot be empty") | |
| 92 | - | } | |
| 79 | + | // Rename the image to a valid name so that it can be saved (remove user/, :tag and add .tar) | |
| 80 | + | imageFileName := payload.ImageName | |
| 81 | + | imageFileName = regexp.MustCompile(`^([a-zA-Z0-9\-_.]+\/)?`).ReplaceAllString(imageFileName, "") | |
| 82 | + | imageFileName = regexp.MustCompile(`(:[a-zA-Z0-9\-_.]+)?$`).ReplaceAllString(imageFileName, "") | |
| 83 | + | imageFileName = imageFileName + ".tar" | |
| 93 | 84 | ||
| 94 | - | // Validate the image tar | |
| 95 | - | imageTar := pushPayload.Data | |
| 96 | - | if imageTar == nil { | |
| 97 | - | return c.JSON(http.StatusBadRequest, "Image tar cannot be empty") | |
| 85 | + | // Save the image tar in the storage | |
| 86 | + | imageFilePath, err := store.SaveImageToStorage(&a.Config, imageFileName, imageReader) | |
| 87 | + | if err != nil { | |
| 88 | + | return err | |
| 98 | 89 | } | |
| 99 | 90 | ||
| 100 | - | imageName := pushPayload.ImageName | |
| 101 | - | if imageName == "" { | |
| 102 | - | return c.JSON(http.StatusBadRequest, "Image name cannot be empty") | |
| 91 | + | // Import the tar in docker | |
| 92 | + | err = docker.ImportImageToEngine(imageFilePath) | |
| 93 | + | if err != nil { | |
| 94 | + | return err | |
| 103 | 95 | } | |
| 104 | 96 | ||
| 105 | - | // 1 - Save the image tar in the storage | |
| 106 | - | imagePath, err := store.SaveImageToStorage(&a.Config, imageName, bytes.NewBuffer(imageTar)) | |
| 97 | + | // Remove the image from the storage | |
| 98 | + | err = store.RemoveFromStorage() | |
| 107 | 99 | if err != nil { | |
| 108 | - | return c.JSON(http.StatusBadRequest, err.Error()) | |
| 100 | + | return err | |
| 109 | 101 | } | |
| 110 | - | // 2 - Import the tar in docker | |
| 111 | - | imageID, err := docker.ImportImageToEngine(imagePath) | |
| 102 | + | ||
| 103 | + | // append localhost to the image name since it's a manually imported image and not from a registry | |
| 104 | + | payload.ImageName = "localhost/" + payload.ImageName | |
| 105 | + | ||
| 106 | + | // Get the image ID | |
| 107 | + | imageID, err := docker.GetImageID(payload.ImageName) | |
| 112 | 108 | if err != nil { | |
| 113 | - | return c.JSON(http.StatusBadRequest, err.Error()) | |
| 109 | + | return err | |
| 114 | 110 | } | |
| 115 | 111 | ||
| 116 | - | fmt.Println("Import successful, ImageID:", imageID) | |
| 112 | + | // Print the imageID short version | |
| 113 | + | fmt.Println("Image ID:", imageID[:12]) | |
| 117 | 114 | ||
| 118 | - | // 3 - Create the container with traefik labels | |
| 115 | + | // // Create the container with traefik labels | |
| 116 | + | // err = addTraefikLabels(payload) | |
| 117 | + | // if err != nil { | |
| 118 | + | // return err | |
| 119 | + | // } | |
| 119 | 120 | ||
| 120 | - | // 4 - Start the container | |
| 121 | + | // // Start the container | |
| 122 | + | // err = docker.StartContainer(imageID) | |
| 123 | + | // if err != nil { | |
| 124 | + | // return err | |
| 125 | + | // } | |
| 121 | 126 | ||
| 122 | - | return c.JSON(http.StatusOK, "OK") | |
| 127 | + | return c.JSON(http.StatusOK, "Image pushed successfully") | |
| 123 | 128 | } | |
| @@ -5,6 +5,7 @@ import ( | |||||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | "io" | 6 | "io" | ||
| 7 | "net/http" | 7 | "net/http" | ||
| 8 | + | "regexp" | |||
| 8 | 9 | ||||
| 9 | "github.com/bnema/gordon/internal/common" | 10 | "github.com/bnema/gordon/internal/common" | ||
| 10 | "github.com/bnema/gordon/internal/server" | 11 | "github.com/bnema/gordon/internal/server" | ||
| @@ -60,64 +61,68 @@ func GetInfos(c echo.Context, a *server.App) error { | |||||
| 60 | } | 61 | } | ||
| 61 | 62 | ||||
| 62 | func PostPush(c echo.Context, a *server.App) error { | 63 | func PostPush(c echo.Context, a *server.App) error { | ||
| 63 | - | body, _ := io.ReadAll(c.Request().Body) | |||
| 64 | - | c.Request().Body = io.NopCloser(bytes.NewBuffer(body)) // Reset the body | |||
| 65 | 64 | ||||
| 66 | - | payload := new(common.RequestPayload) | 65 | + | // Initialize pushPayload object |
| 67 | - | if err := c.Bind(payload); err != nil { | 66 | + | payload := &common.PushPayload{ |
| 68 | - | fmt.Println("Bind Error:", err) | 67 | + | Ports: c.Request().Header.Get("X-Ports"), |
| 69 | - | return c.JSON(http.StatusBadRequest, err.Error()) | 68 | + | ImageName: c.Request().Header.Get("X-Image-Name"), |
| 69 | + | TargetDomain: c.Request().Header.Get("X-Target-Domain"), | |||
| 70 | } | 70 | } | ||
| 71 | 71 | ||||
| 72 | - | if payload.Type != "push" { | 72 | + | if payload.ImageName == "" { |
| 73 | - | return c.JSON(http.StatusBadRequest, "Invalid payload type") | 73 | + | return c.JSON(http.StatusBadRequest, "Invalid image name") |
| 74 | } | 74 | } | ||
| 75 | 75 | ||||
| 76 | - | pushPayload, ok := payload.Payload.(common.PushPayload) | 76 | + | imageReader := c.Request().Body |
| 77 | - | if !ok { | 77 | + | defer imageReader.Close() |
| 78 | - | return c.JSON(http.StatusBadRequest, "Invalid payload type") | |||
| 79 | - | } | |||
| 80 | 78 | ||||
| 81 | - | // Push payload contains Ports as a string, Image as string (image:tag), and the .tar image as a byte array | 79 | + | // Rename the image to a valid name so that it can be saved (remove user/, :tag and add .tar) |
| 82 | - | // Validate the ports | 80 | + | imageFileName := payload.ImageName |
| 83 | - | ports := pushPayload.Ports | 81 | + | imageFileName = regexp.MustCompile(`^([a-zA-Z0-9\-_.]+\/)?`).ReplaceAllString(imageFileName, "") |
| 84 | - | if ports == "" { | 82 | + | imageFileName = regexp.MustCompile(`(:[a-zA-Z0-9\-_.]+)?$`).ReplaceAllString(imageFileName, "") |
| 85 | - | return c.JSON(http.StatusBadRequest, "Ports cannot be empty") | 83 | + | imageFileName = imageFileName + ".tar" |
| 86 | - | } | |||
| 87 | - | ||||
| 88 | - | // Validate the target domain | |||
| 89 | - | domain := pushPayload.TargetDomain | |||
| 90 | - | if domain == "" { | |||
| 91 | - | return c.JSON(http.StatusBadRequest, "Image cannot be empty") | |||
| 92 | - | } | |||
| 93 | 84 | ||||
| 94 | - | // Validate the image tar | 85 | + | // Save the image tar in the storage |
| 95 | - | imageTar := pushPayload.Data | 86 | + | imageFilePath, err := store.SaveImageToStorage(&a.Config, imageFileName, imageReader) |
| 96 | - | if imageTar == nil { | 87 | + | if err != nil { |
| 97 | - | return c.JSON(http.StatusBadRequest, "Image tar cannot be empty") | 88 | + | return err |
| 98 | } | 89 | } | ||
| 99 | 90 | ||||
| 100 | - | imageName := pushPayload.ImageName | 91 | + | // Import the tar in docker |
| 101 | - | if imageName == "" { | 92 | + | err = docker.ImportImageToEngine(imageFilePath) |
| 102 | - | return c.JSON(http.StatusBadRequest, "Image name cannot be empty") | 93 | + | if err != nil { |
| 94 | + | return err | |||
| 103 | } | 95 | } | ||
| 104 | 96 | ||||
| 105 | - | // 1 - Save the image tar in the storage | 97 | + | // Remove the image from the storage |
| 106 | - | imagePath, err := store.SaveImageToStorage(&a.Config, imageName, bytes.NewBuffer(imageTar)) | 98 | + | err = store.RemoveFromStorage() |
| 107 | if err != nil { | 99 | if err != nil { | ||
| 108 | - | return c.JSON(http.StatusBadRequest, err.Error()) | 100 | + | return err |
| 109 | } | 101 | } | ||
| 110 | - | // 2 - Import the tar in docker | 102 | + | |
| 111 | - | imageID, err := docker.ImportImageToEngine(imagePath) | 103 | + | // append localhost to the image name since it's a manually imported image and not from a registry |
| 104 | + | payload.ImageName = "localhost/" + payload.ImageName | |||
| 105 | + | ||||
| 106 | + | // Get the image ID | |||
| 107 | + | imageID, err := docker.GetImageID(payload.ImageName) | |||
| 112 | if err != nil { | 108 | if err != nil { | ||
| 113 | - | return c.JSON(http.StatusBadRequest, err.Error()) | 109 | + | return err |
| 114 | } | 110 | } | ||
| 115 | 111 | ||||
| 116 | - | fmt.Println("Import successful, ImageID:", imageID) | 112 | + | // Print the imageID short version |
| 113 | + | fmt.Println("Image ID:", imageID[:12]) | |||
| 117 | 114 | ||||
| 118 | - | // 3 - Create the container with traefik labels | 115 | + | // // Create the container with traefik labels |
| 116 | + | // err = addTraefikLabels(payload) | |||
| 117 | + | // if err != nil { | |||
| 118 | + | // return err | |||
| 119 | + | // } | |||
| 119 | 120 | ||||
| 120 | - | // 4 - Start the container | 121 | + | // // Start the container |
| 122 | + | // err = docker.StartContainer(imageID) | |||
| 123 | + | // if err != nil { | |||
| 124 | + | // return err | |||
| 125 | + | // } | |||
| 121 | 126 | ||||
| 122 | - | return c.JSON(http.StatusOK, "OK") | 127 | + | return c.JSON(http.StatusOK, "Image pushed successfully") |
| 123 | } | 128 | } | ||
M internal/httpserve/handler/uploadcontainerimage.go +6 -2
| @@ -64,12 +64,16 @@ func UploadImagePOSTHandler(c echo.Context, a *server.App) error { | |||
| 64 | 64 | } | |
| 65 | 65 | ||
| 66 | 66 | // Import the image into Docker | |
| 67 | - | imageId, err := docker.ImportImageToEngine(saveInPath) | |
| 67 | + | err = docker.ImportImageToEngine(saveInPath) | |
| 68 | 68 | if err != nil { | |
| 69 | 69 | return fmt.Errorf("failed to import image to Docker: %v", err) | |
| 70 | 70 | } | |
| 71 | 71 | ||
| 72 | - | fmt.Println("Image ID:", imageId) | |
| 72 | + | // Remove the image from the storage directory | |
| 73 | + | err = store.RemoveFromStorage() | |
| 74 | + | if err != nil { | |
| 75 | + | return sendError(c, err) | |
| 76 | + | } | |
| 73 | 77 | ||
| 74 | 78 | return c.HTML(http.StatusOK, ActionSuccess(a)) | |
| 75 | 79 | } | |
| @@ -64,12 +64,16 @@ func UploadImagePOSTHandler(c echo.Context, a *server.App) error { | |||||
| 64 | } | 64 | } | ||
| 65 | 65 | ||||
| 66 | // Import the image into Docker | 66 | // Import the image into Docker | ||
| 67 | - | imageId, err := docker.ImportImageToEngine(saveInPath) | 67 | + | err = docker.ImportImageToEngine(saveInPath) |
| 68 | if err != nil { | 68 | if err != nil { | ||
| 69 | return fmt.Errorf("failed to import image to Docker: %v", err) | 69 | return fmt.Errorf("failed to import image to Docker: %v", err) | ||
| 70 | } | 70 | } | ||
| 71 | 71 | ||||
| 72 | - | fmt.Println("Image ID:", imageId) | 72 | + | // Remove the image from the storage directory |
| 73 | + | err = store.RemoveFromStorage() | |||
| 74 | + | if err != nil { | |||
| 75 | + | return sendError(c, err) | |||
| 76 | + | } | |||
| 73 | 77 | ||||
| 74 | return c.HTML(http.StatusOK, ActionSuccess(a)) | 78 | return c.HTML(http.StatusOK, ActionSuccess(a)) | ||
| 75 | } | 79 | } | ||
M internal/httpserve/middleware/secure.go +1 -3
| @@ -44,11 +44,9 @@ func InitSessionMiddleware(a *server.App) echo.MiddlewareFunc { | |||
| 44 | 44 | log.Printf("Error in session middleware: %v", err) | |
| 45 | 45 | return err | |
| 46 | 46 | } | |
| 47 | - | sess, err := session.Get("session", c) | |
| 47 | + | _, err = session.Get("session", c) | |
| 48 | 48 | if err != nil { | |
| 49 | 49 | log.Printf("Could not retrieve session: %v", err) | |
| 50 | - | } else { | |
| 51 | - | log.Printf("Session values: %+v", sess.Values) | |
| 52 | 50 | } | |
| 53 | 51 | return nil | |
| 54 | 52 | } | |
| @@ -44,11 +44,9 @@ func InitSessionMiddleware(a *server.App) echo.MiddlewareFunc { | |||||
| 44 | log.Printf("Error in session middleware: %v", err) | 44 | log.Printf("Error in session middleware: %v", err) | ||
| 45 | return err | 45 | return err | ||
| 46 | } | 46 | } | ||
| 47 | - | sess, err := session.Get("session", c) | 47 | + | _, err = session.Get("session", c) |
| 48 | if err != nil { | 48 | if err != nil { | ||
| 49 | log.Printf("Could not retrieve session: %v", err) | 49 | log.Printf("Could not retrieve session: %v", err) | ||
| 50 | - | } else { | |||
| 51 | - | log.Printf("Session values: %+v", sess.Values) | |||
| 52 | } | 50 | } | ||
| 53 | return nil | 51 | return nil | ||
| 54 | } | 52 | } | ||
M internal/templating/cmdparams/_fromcli.go +21 -10
| @@ -1,23 +1,34 @@ | |||
| 1 | 1 | package cmdparams | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | + | "io" | |
| 5 | + | ||
| 4 | 6 | "github.com/bnema/gordon/internal/common" | |
| 5 | 7 | "github.com/bnema/gordon/pkg/docker" | |
| 6 | 8 | ) | |
| 7 | 9 | ||
| 10 | + | type PushPayload struct { | |
| 11 | + | Ports string `json:"ports"` | |
| 12 | + | TargetDomain string `json:"targetdomain"` | |
| 13 | + | ImageName string `json:"imagename"` | |
| 14 | + | Data io.ReadCloser | |
| 15 | + | } | |
| 16 | + | ||
| 8 | 17 | // FromYAMLStructToCmdParams converts a YAMLContainerParams struct to a ContainerCommandParams struct | |
| 9 | - | func FromPayloadStructToCmdParams(PushPayload *common.PushPayload) (docker.ContainerCommandParams, error) { | |
| 18 | + | func FromPayloadStructToCmdParams(ppl *common.PushPayload) (docker.ContainerCommandParams, error) { | |
| 10 | 19 | ||
| 11 | 20 | params := docker.ContainerCommandParams{ | |
| 12 | - | ContainerName: yamlParams.Name, | |
| 13 | - | ImageName: yamlParams.Image, | |
| 14 | - | ImageID: imageID, | |
| 15 | - | Volumes: yamlParams.Volumes, | |
| 16 | - | Environment: yamlParams.Environment, | |
| 17 | - | Labels: labels, | |
| 18 | - | Network: network, | |
| 19 | - | Restart: yamlParams.Restart, | |
| 20 | - | PortMappings: portMappings, | |
| 21 | + | IsSSL: ppl.IsSSL, | |
| 22 | + | ContainerName: ppl.ContainerName, | |
| 23 | + | ServiceName: ppl.ServiceName, | |
| 24 | + | Domain: ppl.Domain, | |
| 25 | + | ImageName: ppl.ImageName, | |
| 26 | + | ImageID: ppl.ImageID, | |
| 27 | + | Restart: ppl.Restart, | |
| 28 | + | Volumes: ppl.Volumes, | |
| 29 | + | Environment: ppl.Environment, | |
| 30 | + | Network: ppl.Network, | |
| 31 | + | PortMappings: ppl.PortMappings, | |
| 21 | 32 | } | |
| 22 | 33 | ||
| 23 | 34 | return params, nil | |
| @@ -1,23 +1,34 @@ | |||||
| 1 | package cmdparams | 1 | package cmdparams | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | + | "io" | |||
| 5 | + | ||||
| 4 | "github.com/bnema/gordon/internal/common" | 6 | "github.com/bnema/gordon/internal/common" | ||
| 5 | "github.com/bnema/gordon/pkg/docker" | 7 | "github.com/bnema/gordon/pkg/docker" | ||
| 6 | ) | 8 | ) | ||
| 7 | 9 | ||||
| 10 | + | type PushPayload struct { | |||
| 11 | + | Ports string `json:"ports"` | |||
| 12 | + | TargetDomain string `json:"targetdomain"` | |||
| 13 | + | ImageName string `json:"imagename"` | |||
| 14 | + | Data io.ReadCloser | |||
| 15 | + | } | |||
| 16 | + | ||||
| 8 | // FromYAMLStructToCmdParams converts a YAMLContainerParams struct to a ContainerCommandParams struct | 17 | // FromYAMLStructToCmdParams converts a YAMLContainerParams struct to a ContainerCommandParams struct | ||
| 9 | - | func FromPayloadStructToCmdParams(PushPayload *common.PushPayload) (docker.ContainerCommandParams, error) { | 18 | + | func FromPayloadStructToCmdParams(ppl *common.PushPayload) (docker.ContainerCommandParams, error) { |
| 10 | 19 | ||||
| 11 | params := docker.ContainerCommandParams{ | 20 | params := docker.ContainerCommandParams{ | ||
| 12 | - | ContainerName: yamlParams.Name, | 21 | + | IsSSL: ppl.IsSSL, |
| 13 | - | ImageName: yamlParams.Image, | 22 | + | ContainerName: ppl.ContainerName, |
| 14 | - | ImageID: imageID, | 23 | + | ServiceName: ppl.ServiceName, |
| 15 | - | Volumes: yamlParams.Volumes, | 24 | + | Domain: ppl.Domain, |
| 16 | - | Environment: yamlParams.Environment, | 25 | + | ImageName: ppl.ImageName, |
| 17 | - | Labels: labels, | 26 | + | ImageID: ppl.ImageID, |
| 18 | - | Network: network, | 27 | + | Restart: ppl.Restart, |
| 19 | - | Restart: yamlParams.Restart, | 28 | + | Volumes: ppl.Volumes, |
| 20 | - | PortMappings: portMappings, | 29 | + | Environment: ppl.Environment, |
| 30 | + | Network: ppl.Network, | |||
| 31 | + | PortMappings: ppl.PortMappings, | |||
| 21 | } | 32 | } | ||
| 22 | 33 | ||||
| 23 | return params, nil | 34 | return params, nil | ||
M internal/templating/models/html/fragments/header.gohtml +0 -1
| @@ -10,5 +10,4 @@ | |||
| 10 | 10 | <script src="/libs/htmx-sse.js"></script> | |
| 11 | 11 | <script defer src="/libs/hyperscript.min.js"></script> | |
| 12 | 12 | <script defer src="/assets/js/custom.js"></script> | |
| 13 | - | <!-- script wait for the page to be loaded and insert <link rel="stylesheet" href="/libs/iconoir.css" /> --> | |
| 14 | 13 | </head> | |
| @@ -10,5 +10,4 @@ | |||||
| 10 | <script src="/libs/htmx-sse.js"></script> | 10 | <script src="/libs/htmx-sse.js"></script> | ||
| 11 | <script defer src="/libs/hyperscript.min.js"></script> | 11 | <script defer src="/libs/hyperscript.min.js"></script> | ||
| 12 | <script defer src="/assets/js/custom.js"></script> | 12 | <script defer src="/assets/js/custom.js"></script> | ||
| 13 | - | <!-- script wait for the page to be loaded and insert <link rel="stylesheet" href="/libs/iconoir.css" /> --> | |||
| 14 | </head> | 13 | </head> | ||
M internal/templating/models/html/fragments/menu.gohtml +8 -9
| @@ -1,11 +1,9 @@ | |||
| 1 | 1 | <div | |
| 2 | 2 | id="menubar" | |
| 3 | - | class="w-full h-12 md:h-16 fixed top-0 left-0 bg-gordonmascot-lblue flex flex-row justify-evenly items-center px-4 IBMPlexSans" | |
| 4 | - | > | |
| 3 | + | class="w-full h-12 md:h-16 fixed top-0 left-0 bg-gordonmascot-lblue flex flex-row justify-evenly items-center px-4 IBMPlexSans"> | |
| 5 | 4 | <div class="w-full flex justify-start"> | |
| 6 | 5 | <h1 | |
| 7 | - | class="gordon-title text-3xl md:text-4xl text-gordonmascot-hblue tracking-tighter" | |
| 8 | - | > | |
| 6 | + | class="gordon-title text-3xl md:text-4xl text-gordonmascot-hblue tracking-tighter"> | |
| 9 | 7 | Gordon | |
| 10 | 8 | </h1> | |
| 11 | 9 | <span | |
| @@ -15,11 +13,10 @@ | |||
| 15 | 13 | </div> | |
| 16 | 14 | <div class="w-full"> | |
| 17 | 15 | <ul | |
| 18 | - | class="flex flex-row space-y-6 font-semibold text-gray-100 text-lg md:text-xl justify-center" | |
| 19 | - | > | |
| 16 | + | class="flex flex-row space-y-6 font-semibold text-gray-100 text-lg md:text-xl justify-center"> | |
| 20 | 17 | <li class=""> | |
| 21 | 18 | <span class="JetBrainsMonoBoldNF pr-1"> </span> | |
| 22 | - | <a href="/admin/manager">Manager</a> | |
| 19 | + | <a href="manager">Manager</a> | |
| 23 | 20 | </li> | |
| 24 | 21 | </ul> | |
| 25 | 22 | </div> | |
| @@ -28,12 +25,14 @@ | |||
| 28 | 25 | href="/admin/settings" | |
| 29 | 26 | title="User settings" | |
| 30 | 27 | class="iconf text-gordonmascot-mblue text-lg md:text-xl" | |
| 31 | - | ></a> | |
| 28 | + | ></a | |
| 29 | + | > | |
| 32 | 30 | <a | |
| 33 | 31 | href="/admin/logout" | |
| 34 | 32 | title="Logout" | |
| 35 | 33 | class="iconf text-gordonmascot-mblue text-lg md:text-xl" | |
| 36 | - | ></a> | |
| 34 | + | ></a | |
| 35 | + | > | |
| 37 | 36 | </div> | |
| 38 | 37 | </div> | |
| 39 | 38 | ||
| @@ -1,11 +1,9 @@ | |||||
| 1 | <div | 1 | <div | ||
| 2 | id="menubar" | 2 | id="menubar" | ||
| 3 | - | class="w-full h-12 md:h-16 fixed top-0 left-0 bg-gordonmascot-lblue flex flex-row justify-evenly items-center px-4 IBMPlexSans" | 3 | + | class="w-full h-12 md:h-16 fixed top-0 left-0 bg-gordonmascot-lblue flex flex-row justify-evenly items-center px-4 IBMPlexSans"> |
| 4 | - | > | |||
| 5 | <div class="w-full flex justify-start"> | 4 | <div class="w-full flex justify-start"> | ||
| 6 | <h1 | 5 | <h1 | ||
| 7 | - | class="gordon-title text-3xl md:text-4xl text-gordonmascot-hblue tracking-tighter" | 6 | + | class="gordon-title text-3xl md:text-4xl text-gordonmascot-hblue tracking-tighter"> |
| 8 | - | > | |||
| 9 | Gordon | 7 | Gordon | ||
| 10 | </h1> | 8 | </h1> | ||
| 11 | <span | 9 | <span | ||
| @@ -15,11 +13,10 @@ | |||||
| 15 | </div> | 13 | </div> | ||
| 16 | <div class="w-full"> | 14 | <div class="w-full"> | ||
| 17 | <ul | 15 | <ul | ||
| 18 | - | class="flex flex-row space-y-6 font-semibold text-gray-100 text-lg md:text-xl justify-center" | 16 | + | class="flex flex-row space-y-6 font-semibold text-gray-100 text-lg md:text-xl justify-center"> |
| 19 | - | > | |||
| 20 | <li class=""> | 17 | <li class=""> | ||
| 21 | <span class="JetBrainsMonoBoldNF pr-1"> </span> | 18 | <span class="JetBrainsMonoBoldNF pr-1"> </span> | ||
| 22 | - | <a href="/admin/manager">Manager</a> | 19 | + | <a href="manager">Manager</a> |
| 23 | </li> | 20 | </li> | ||
| 24 | </ul> | 21 | </ul> | ||
| 25 | </div> | 22 | </div> | ||
| @@ -28,12 +25,14 @@ | |||||
| 28 | href="/admin/settings" | 25 | href="/admin/settings" | ||
| 29 | title="User settings" | 26 | title="User settings" | ||
| 30 | class="iconf text-gordonmascot-mblue text-lg md:text-xl" | 27 | class="iconf text-gordonmascot-mblue text-lg md:text-xl" | ||
| 31 | - | ></a> | 28 | + | ></a |
| 29 | + | > | |||
| 32 | <a | 30 | <a | ||
| 33 | href="/admin/logout" | 31 | href="/admin/logout" | ||
| 34 | title="Logout" | 32 | title="Logout" | ||
| 35 | class="iconf text-gordonmascot-mblue text-lg md:text-xl" | 33 | class="iconf text-gordonmascot-mblue text-lg md:text-xl" | ||
| 36 | - | ></a> | 34 | + | ></a |
| 35 | + | > | |||
| 37 | </div> | 36 | </div> | ||
| 38 | </div> | 37 | </div> | ||
| 39 | 38 | ||||
M internal/templating/render/localization.go +0 -2
| @@ -17,8 +17,6 @@ func GetLocalization(lang string, a *server.App) (map[string]interface{}, error) | |||
| 17 | 17 | return nil, fmt.Errorf("language %s is not supported", lang) | |
| 18 | 18 | } | |
| 19 | 19 | ||
| 20 | - | fmt.Println("Language:", lang) | |
| 21 | - | ||
| 22 | 20 | // 2. Get the localizations from the strings.yml file | |
| 23 | 21 | localizations, err := getLocalizations(a) | |
| 24 | 22 | if err != nil { | |
| @@ -17,8 +17,6 @@ func GetLocalization(lang string, a *server.App) (map[string]interface{}, error) | |||||
| 17 | return nil, fmt.Errorf("language %s is not supported", lang) | 17 | return nil, fmt.Errorf("language %s is not supported", lang) | ||
| 18 | } | 18 | } | ||
| 19 | 19 | ||||
| 20 | - | fmt.Println("Language:", lang) | |||
| 21 | - | ||||
| 22 | // 2. Get the localizations from the strings.yml file | 20 | // 2. Get the localizations from the strings.yml file | ||
| 23 | localizations, err := getLocalizations(a) | 21 | localizations, err := getLocalizations(a) | ||
| 24 | if err != nil { | 22 | if err != nil { | ||
M pkg/docker/containers.go +1 -1
| @@ -99,7 +99,7 @@ func ListRunningContainers() ([]types.Container, error) { | |||
| 99 | 99 | } | |
| 100 | 100 | // Check if the list is empty | |
| 101 | 101 | if len(containers) == 0 { | |
| 102 | - | return nil, fmt.Errorf("No containers found") | |
| 102 | + | return nil, fmt.Errorf("no containers found") | |
| 103 | 103 | } | |
| 104 | 104 | return containers, nil | |
| 105 | 105 | } | |
| @@ -99,7 +99,7 @@ func ListRunningContainers() ([]types.Container, error) { | |||||
| 99 | } | 99 | } | ||
| 100 | // Check if the list is empty | 100 | // Check if the list is empty | ||
| 101 | if len(containers) == 0 { | 101 | if len(containers) == 0 { | ||
| 102 | - | return nil, fmt.Errorf("No containers found") | 102 | + | return nil, fmt.Errorf("no containers found") |
| 103 | } | 103 | } | ||
| 104 | return containers, nil | 104 | return containers, nil | ||
| 105 | } | 105 | } | ||
M pkg/docker/images.go +55 -43
| @@ -1,8 +1,8 @@ | |||
| 1 | 1 | package docker | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "bytes" | |
| 5 | 4 | "context" | |
| 5 | + | "fmt" | |
| 6 | 6 | "io" | |
| 7 | 7 | "os" | |
| 8 | 8 | ||
| @@ -33,13 +33,10 @@ func DeleteContainerImage(imageID string) error { | |||
| 33 | 33 | } | |
| 34 | 34 | ||
| 35 | 35 | func GetImageID(imageName string) (string, error) { | |
| 36 | - | // Check if the Docker client has been initialized | |
| 37 | - | CheckIfInitialized() | |
| 38 | 36 | ||
| 39 | - | // List all images | |
| 40 | 37 | images, err := dockerCli.ImageList(context.Background(), types.ImageListOptions{}) | |
| 41 | 38 | if err != nil { | |
| 42 | - | return "", err | |
| 39 | + | return "", fmt.Errorf("failed to list images: %w", err) | |
| 43 | 40 | } | |
| 44 | 41 | ||
| 45 | 42 | // Search for the image we just loaded | |
| @@ -53,80 +50,95 @@ func GetImageID(imageName string) (string, error) { | |||
| 53 | 50 | } | |
| 54 | 51 | ||
| 55 | 52 | if imageID == "" { | |
| 56 | - | return "", err | |
| 53 | + | return "", fmt.Errorf("image not found") | |
| 57 | 54 | } | |
| 58 | 55 | ||
| 59 | 56 | return imageID, nil | |
| 60 | 57 | } | |
| 61 | 58 | ||
| 62 | 59 | // ImportImageToEngine imports an image to the Docker engine | |
| 63 | - | func ImportImageToEngine(imagePath string) (string, error) { | |
| 60 | + | func ImportImageToEngine(imageFilePath string) error { | |
| 64 | 61 | // Open the image file | |
| 65 | - | imageFile, err := os.Open(imagePath) | |
| 62 | + | imageFile, err := os.Open(imageFilePath) | |
| 66 | 63 | if err != nil { | |
| 67 | - | return "", err | |
| 64 | + | return fmt.Errorf("failed to open image file: %w", err) | |
| 68 | 65 | } | |
| 66 | + | defer imageFile.Close() | |
| 69 | 67 | ||
| 70 | - | _, err = dockerCli.ImageLoad(context.Background(), imageFile, false) | |
| 68 | + | // Import the image using the Docker client | |
| 69 | + | importedImage, err := dockerCli.ImageLoad(context.Background(), imageFile, false) | |
| 71 | 70 | if err != nil { | |
| 72 | - | return "", err | |
| 71 | + | return fmt.Errorf("failed to import image: %w", err) | |
| 73 | 72 | } | |
| 74 | - | // Close the image file | |
| 75 | - | imageFile.Close() | |
| 73 | + | defer importedImage.Body.Close() | |
| 76 | 74 | ||
| 77 | - | // List all images | |
| 78 | - | images, err := dockerCli.ImageList(context.Background(), types.ImageListOptions{}) | |
| 75 | + | return nil | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | // ExportImageFromEngine exports an image from the Docker engine and returns it as an io.Reader | |
| 79 | + | func ExportImageFromEngine(imageID string) (io.ReadCloser, error) { | |
| 80 | + | // Check if the Docker client has been initialized | |
| 81 | + | CheckIfInitialized() | |
| 82 | + | ||
| 83 | + | // Get the image information using the Docker client | |
| 84 | + | imageInfo, err := GetImageInfo(imageID) | |
| 79 | 85 | if err != nil { | |
| 80 | - | return "", err | |
| 86 | + | return nil, err | |
| 81 | 87 | } | |
| 82 | 88 | ||
| 83 | - | // Search for the image we just loaded | |
| 84 | - | var imageID string | |
| 85 | - | for _, image := range images { | |
| 86 | - | for _, tag := range image.RepoTags { | |
| 87 | - | if tag == "<none>:<none>" { | |
| 88 | - | imageID = image.ID | |
| 89 | - | } | |
| 90 | - | } | |
| 89 | + | if len(imageInfo.RepoTags) == 0 { | |
| 90 | + | return nil, fmt.Errorf("image has no tag") | |
| 91 | 91 | } | |
| 92 | 92 | ||
| 93 | - | if imageID == "" { | |
| 94 | - | return "", err | |
| 93 | + | // Export the image using the Docker client | |
| 94 | + | imageReader, err := dockerCli.ImageSave(context.Background(), []string{imageInfo.RepoTags[0]}) | |
| 95 | + | if err != nil { | |
| 96 | + | return nil, fmt.Errorf("failed to export image: %w", err) | |
| 95 | 97 | } | |
| 96 | 98 | ||
| 97 | - | return imageID, nil | |
| 99 | + | return imageReader, nil | |
| 98 | 100 | } | |
| 99 | 101 | ||
| 100 | - | // ExportImageFromEngine exports an image from the Docker engine and returns it as a byte array | |
| 101 | - | func ExportImageFromEngine(imageName string) ([]byte, error) { | |
| 102 | - | // we check if the image exists | |
| 103 | - | _, err := GetImageID(imageName) | |
| 102 | + | // From an ID, get the all the information about the image | |
| 103 | + | func GetImageInfo(imageID string) (*types.ImageInspect, error) { | |
| 104 | + | // Get the image information using the Docker client | |
| 105 | + | imageInfo, _, err := dockerCli.ImageInspectWithRaw(context.Background(), imageID) | |
| 104 | 106 | if err != nil { | |
| 105 | 107 | return nil, err | |
| 106 | 108 | } | |
| 107 | 109 | ||
| 108 | - | image, err := dockerCli.ImageSave(context.Background(), []string{imageName}) | |
| 110 | + | return &imageInfo, nil | |
| 111 | + | } | |
| 112 | + | ||
| 113 | + | // GetImageSize returns the size of an image | |
| 114 | + | func GetImageSize(imageID string) (int64, error) { | |
| 115 | + | // Get the image information using the Docker client | |
| 116 | + | imageInfo, err := GetImageInfo(imageID) | |
| 109 | 117 | if err != nil { | |
| 110 | - | return nil, err | |
| 118 | + | return 0, err | |
| 111 | 119 | } | |
| 112 | - | defer image.Close() | |
| 113 | 120 | ||
| 114 | - | var buf bytes.Buffer | |
| 115 | - | _, err = io.Copy(&buf, image) | |
| 121 | + | return imageInfo.Size, nil | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | // GetImageTag returns the tag of an image | |
| 125 | + | func GetImageTag(imageID string) (string, error) { | |
| 126 | + | // Get the image information using the Docker client | |
| 127 | + | imageInfo, err := GetImageInfo(imageID) | |
| 116 | 128 | if err != nil { | |
| 117 | - | return nil, err | |
| 129 | + | return "", err | |
| 118 | 130 | } | |
| 119 | 131 | ||
| 120 | - | return buf.Bytes(), nil | |
| 132 | + | return imageInfo.RepoTags[0], nil | |
| 121 | 133 | } | |
| 122 | 134 | ||
| 123 | - | // From an ID, get the all the information about the image | |
| 124 | - | func GetImageInfo(imageID string) (*types.ImageInspect, error) { | |
| 135 | + | // GetImageName returns the name of an image | |
| 136 | + | func GetImageName(imageID string) (string, error) { | |
| 125 | 137 | // Get the image information using the Docker client | |
| 126 | - | imageInfo, _, err := dockerCli.ImageInspectWithRaw(context.Background(), imageID) | |
| 138 | + | imageInfo, err := GetImageInfo(imageID) | |
| 127 | 139 | if err != nil { | |
| 128 | - | return nil, err | |
| 140 | + | return "", err | |
| 129 | 141 | } | |
| 130 | 142 | ||
| 131 | - | return &imageInfo, nil | |
| 143 | + | return imageInfo.RepoDigests[0], nil | |
| 132 | 144 | } | |
| @@ -1,8 +1,8 @@ | |||||
| 1 | package docker | 1 | package docker | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "bytes" | |||
| 5 | "context" | 4 | "context" | ||
| 5 | + | "fmt" | |||
| 6 | "io" | 6 | "io" | ||
| 7 | "os" | 7 | "os" | ||
| 8 | 8 | ||||
| @@ -33,13 +33,10 @@ func DeleteContainerImage(imageID string) error { | |||||
| 33 | } | 33 | } | ||
| 34 | 34 | ||||
| 35 | func GetImageID(imageName string) (string, error) { | 35 | func GetImageID(imageName string) (string, error) { | ||
| 36 | - | // Check if the Docker client has been initialized | |||
| 37 | - | CheckIfInitialized() | |||
| 38 | 36 | ||||
| 39 | - | // List all images | |||
| 40 | images, err := dockerCli.ImageList(context.Background(), types.ImageListOptions{}) | 37 | images, err := dockerCli.ImageList(context.Background(), types.ImageListOptions{}) | ||
| 41 | if err != nil { | 38 | if err != nil { | ||
| 42 | - | return "", err | 39 | + | return "", fmt.Errorf("failed to list images: %w", err) |
| 43 | } | 40 | } | ||
| 44 | 41 | ||||
| 45 | // Search for the image we just loaded | 42 | // Search for the image we just loaded | ||
| @@ -53,80 +50,95 @@ func GetImageID(imageName string) (string, error) { | |||||
| 53 | } | 50 | } | ||
| 54 | 51 | ||||
| 55 | if imageID == "" { | 52 | if imageID == "" { | ||
| 56 | - | return "", err | 53 | + | return "", fmt.Errorf("image not found") |
| 57 | } | 54 | } | ||
| 58 | 55 | ||||
| 59 | return imageID, nil | 56 | return imageID, nil | ||
| 60 | } | 57 | } | ||
| 61 | 58 | ||||
| 62 | // ImportImageToEngine imports an image to the Docker engine | 59 | // ImportImageToEngine imports an image to the Docker engine | ||
| 63 | - | func ImportImageToEngine(imagePath string) (string, error) { | 60 | + | func ImportImageToEngine(imageFilePath string) error { |
| 64 | // Open the image file | 61 | // Open the image file | ||
| 65 | - | imageFile, err := os.Open(imagePath) | 62 | + | imageFile, err := os.Open(imageFilePath) |
| 66 | if err != nil { | 63 | if err != nil { | ||
| 67 | - | return "", err | 64 | + | return fmt.Errorf("failed to open image file: %w", err) |
| 68 | } | 65 | } | ||
| 66 | + | defer imageFile.Close() | |||
| 69 | 67 | ||||
| 70 | - | _, err = dockerCli.ImageLoad(context.Background(), imageFile, false) | 68 | + | // Import the image using the Docker client |
| 69 | + | importedImage, err := dockerCli.ImageLoad(context.Background(), imageFile, false) | |||
| 71 | if err != nil { | 70 | if err != nil { | ||
| 72 | - | return "", err | 71 | + | return fmt.Errorf("failed to import image: %w", err) |
| 73 | } | 72 | } | ||
| 74 | - | // Close the image file | 73 | + | defer importedImage.Body.Close() |
| 75 | - | imageFile.Close() | |||
| 76 | 74 | ||||
| 77 | - | // List all images | 75 | + | return nil |
| 78 | - | images, err := dockerCli.ImageList(context.Background(), types.ImageListOptions{}) | 76 | + | } |
| 77 | + | ||||
| 78 | + | // ExportImageFromEngine exports an image from the Docker engine and returns it as an io.Reader | |||
| 79 | + | func ExportImageFromEngine(imageID string) (io.ReadCloser, error) { | |||
| 80 | + | // Check if the Docker client has been initialized | |||
| 81 | + | CheckIfInitialized() | |||
| 82 | + | ||||
| 83 | + | // Get the image information using the Docker client | |||
| 84 | + | imageInfo, err := GetImageInfo(imageID) | |||
| 79 | if err != nil { | 85 | if err != nil { | ||
| 80 | - | return "", err | 86 | + | return nil, err |
| 81 | } | 87 | } | ||
| 82 | 88 | ||||
| 83 | - | // Search for the image we just loaded | 89 | + | if len(imageInfo.RepoTags) == 0 { |
| 84 | - | var imageID string | 90 | + | return nil, fmt.Errorf("image has no tag") |
| 85 | - | for _, image := range images { | |||
| 86 | - | for _, tag := range image.RepoTags { | |||
| 87 | - | if tag == "<none>:<none>" { | |||
| 88 | - | imageID = image.ID | |||
| 89 | - | } | |||
| 90 | - | } | |||
| 91 | } | 91 | } | ||
| 92 | 92 | ||||
| 93 | - | if imageID == "" { | 93 | + | // Export the image using the Docker client |
| 94 | - | return "", err | 94 | + | imageReader, err := dockerCli.ImageSave(context.Background(), []string{imageInfo.RepoTags[0]}) |
| 95 | + | if err != nil { | |||
| 96 | + | return nil, fmt.Errorf("failed to export image: %w", err) | |||
| 95 | } | 97 | } | ||
| 96 | 98 | ||||
| 97 | - | return imageID, nil | 99 | + | return imageReader, nil |
| 98 | } | 100 | } | ||
| 99 | 101 | ||||
| 100 | - | // ExportImageFromEngine exports an image from the Docker engine and returns it as a byte array | 102 | + | // From an ID, get the all the information about the image |
| 101 | - | func ExportImageFromEngine(imageName string) ([]byte, error) { | 103 | + | func GetImageInfo(imageID string) (*types.ImageInspect, error) { |
| 102 | - | // we check if the image exists | 104 | + | // Get the image information using the Docker client |
| 103 | - | _, err := GetImageID(imageName) | 105 | + | imageInfo, _, err := dockerCli.ImageInspectWithRaw(context.Background(), imageID) |
| 104 | if err != nil { | 106 | if err != nil { | ||
| 105 | return nil, err | 107 | return nil, err | ||
| 106 | } | 108 | } | ||
| 107 | 109 | ||||
| 108 | - | image, err := dockerCli.ImageSave(context.Background(), []string{imageName}) | 110 | + | return &imageInfo, nil |
| 111 | + | } | |||
| 112 | + | ||||
| 113 | + | // GetImageSize returns the size of an image | |||
| 114 | + | func GetImageSize(imageID string) (int64, error) { | |||
| 115 | + | // Get the image information using the Docker client | |||
| 116 | + | imageInfo, err := GetImageInfo(imageID) | |||
| 109 | if err != nil { | 117 | if err != nil { | ||
| 110 | - | return nil, err | 118 | + | return 0, err |
| 111 | } | 119 | } | ||
| 112 | - | defer image.Close() | |||
| 113 | 120 | ||||
| 114 | - | var buf bytes.Buffer | 121 | + | return imageInfo.Size, nil |
| 115 | - | _, err = io.Copy(&buf, image) | 122 | + | } |
| 123 | + | ||||
| 124 | + | // GetImageTag returns the tag of an image | |||
| 125 | + | func GetImageTag(imageID string) (string, error) { | |||
| 126 | + | // Get the image information using the Docker client | |||
| 127 | + | imageInfo, err := GetImageInfo(imageID) | |||
| 116 | if err != nil { | 128 | if err != nil { | ||
| 117 | - | return nil, err | 129 | + | return "", err |
| 118 | } | 130 | } | ||
| 119 | 131 | ||||
| 120 | - | return buf.Bytes(), nil | 132 | + | return imageInfo.RepoTags[0], nil |
| 121 | } | 133 | } | ||
| 122 | 134 | ||||
| 123 | - | // From an ID, get the all the information about the image | 135 | + | // GetImageName returns the name of an image |
| 124 | - | func GetImageInfo(imageID string) (*types.ImageInspect, error) { | 136 | + | func GetImageName(imageID string) (string, error) { |
| 125 | // Get the image information using the Docker client | 137 | // Get the image information using the Docker client | ||
| 126 | - | imageInfo, _, err := dockerCli.ImageInspectWithRaw(context.Background(), imageID) | 138 | + | imageInfo, err := GetImageInfo(imageID) |
| 127 | if err != nil { | 139 | if err != nil { | ||
| 128 | - | return nil, err | 140 | + | return "", err |
| 129 | } | 141 | } | ||
| 130 | 142 | ||||
| 131 | - | return &imageInfo, nil | 143 | + | return imageInfo.RepoDigests[0], nil |
| 132 | } | 144 | } | ||
M pkg/store/storeimages.go +11 -20
| @@ -7,20 +7,9 @@ import ( | |||
| 7 | 7 | "path/filepath" | |
| 8 | 8 | ||
| 9 | 9 | "github.com/bnema/gordon/internal/common" | |
| 10 | - | "github.com/docker/docker/api/types" | |
| 11 | 10 | ) | |
| 12 | 11 | ||
| 13 | - | type StorageConfig struct { | |
| 14 | - | StorageDir string | |
| 15 | - | ImageID string | |
| 16 | - | Images []types.ImageSummary | |
| 17 | - | } | |
| 18 | - | ||
| 19 | - | func NewStorageConfig(config *common.Config) *StorageConfig { | |
| 20 | - | return &StorageConfig{ | |
| 21 | - | StorageDir: config.General.StorageDir, | |
| 22 | - | } | |
| 23 | - | } | |
| 12 | + | var imagePath string | |
| 24 | 13 | ||
| 25 | 14 | func SaveImageToStorage(config *common.Config, filename string, buf io.Reader) (string, error) { | |
| 26 | 15 | // Check if the folder exist if not create it | |
| @@ -32,10 +21,10 @@ func SaveImageToStorage(config *common.Config, filename string, buf io.Reader) ( | |||
| 32 | 21 | } | |
| 33 | 22 | ||
| 34 | 23 | // Define the path where the image will be saved | |
| 35 | - | saveInPath := filepath.Join(config.General.StorageDir, filename) | |
| 24 | + | imagePath = filepath.Join(config.General.StorageDir, filename) | |
| 36 | 25 | ||
| 37 | 26 | // Create or open a file for appending. | |
| 38 | - | outFile, err := os.Create(saveInPath) | |
| 27 | + | outFile, err := os.Create(imagePath) | |
| 39 | 28 | if err != nil { | |
| 40 | 29 | return "", fmt.Errorf("failed to open file: %v", err) | |
| 41 | 30 | } | |
| @@ -47,20 +36,22 @@ func SaveImageToStorage(config *common.Config, filename string, buf io.Reader) ( | |||
| 47 | 36 | } | |
| 48 | 37 | ||
| 49 | 38 | // Check if file exists | |
| 50 | - | if _, err := os.Stat(saveInPath); os.IsNotExist(err) { | |
| 39 | + | if _, err := os.Stat(imagePath); os.IsNotExist(err) { | |
| 51 | 40 | return "", fmt.Errorf("file does not exist: %v", err) | |
| 52 | 41 | } | |
| 53 | 42 | ||
| 54 | - | return saveInPath, nil | |
| 43 | + | return imagePath, nil | |
| 55 | 44 | } | |
| 56 | 45 | ||
| 57 | - | func (sc *StorageConfig) DeleteImageFromStorage(imageId string) error { | |
| 58 | - | // Delete the image from the storage directory | |
| 59 | - | imagePath := filepath.Join(sc.StorageDir, imageId+".tar") | |
| 46 | + | func RemoveFromStorage() error { | |
| 47 | + | // Delete the file | |
| 60 | 48 | err := os.Remove(imagePath) | |
| 61 | 49 | if err != nil { | |
| 62 | - | return fmt.Errorf("failed to delete image from storage directory: %v", err) | |
| 50 | + | return fmt.Errorf("failed to remove file: %v", err) | |
| 63 | 51 | } | |
| 64 | 52 | ||
| 53 | + | // clean up the path | |
| 54 | + | imagePath = "" | |
| 55 | + | ||
| 65 | 56 | return nil | |
| 66 | 57 | } | |
| @@ -7,20 +7,9 @@ import ( | |||||
| 7 | "path/filepath" | 7 | "path/filepath" | ||
| 8 | 8 | ||||
| 9 | "github.com/bnema/gordon/internal/common" | 9 | "github.com/bnema/gordon/internal/common" | ||
| 10 | - | "github.com/docker/docker/api/types" | |||
| 11 | ) | 10 | ) | ||
| 12 | 11 | ||||
| 13 | - | type StorageConfig struct { | 12 | + | var imagePath string |
| 14 | - | StorageDir string | |||
| 15 | - | ImageID string | |||
| 16 | - | Images []types.ImageSummary | |||
| 17 | - | } | |||
| 18 | - | ||||
| 19 | - | func NewStorageConfig(config *common.Config) *StorageConfig { | |||
| 20 | - | return &StorageConfig{ | |||
| 21 | - | StorageDir: config.General.StorageDir, | |||
| 22 | - | } | |||
| 23 | - | } | |||
| 24 | 13 | ||||
| 25 | func SaveImageToStorage(config *common.Config, filename string, buf io.Reader) (string, error) { | 14 | func SaveImageToStorage(config *common.Config, filename string, buf io.Reader) (string, error) { | ||
| 26 | // Check if the folder exist if not create it | 15 | // Check if the folder exist if not create it | ||
| @@ -32,10 +21,10 @@ func SaveImageToStorage(config *common.Config, filename string, buf io.Reader) ( | |||||
| 32 | } | 21 | } | ||
| 33 | 22 | ||||
| 34 | // Define the path where the image will be saved | 23 | // Define the path where the image will be saved | ||
| 35 | - | saveInPath := filepath.Join(config.General.StorageDir, filename) | 24 | + | imagePath = filepath.Join(config.General.StorageDir, filename) |
| 36 | 25 | ||||
| 37 | // Create or open a file for appending. | 26 | // Create or open a file for appending. | ||
| 38 | - | outFile, err := os.Create(saveInPath) | 27 | + | outFile, err := os.Create(imagePath) |
| 39 | if err != nil { | 28 | if err != nil { | ||
| 40 | return "", fmt.Errorf("failed to open file: %v", err) | 29 | return "", fmt.Errorf("failed to open file: %v", err) | ||
| 41 | } | 30 | } | ||
| @@ -47,20 +36,22 @@ func SaveImageToStorage(config *common.Config, filename string, buf io.Reader) ( | |||||
| 47 | } | 36 | } | ||
| 48 | 37 | ||||
| 49 | // Check if file exists | 38 | // Check if file exists | ||
| 50 | - | if _, err := os.Stat(saveInPath); os.IsNotExist(err) { | 39 | + | if _, err := os.Stat(imagePath); os.IsNotExist(err) { |
| 51 | return "", fmt.Errorf("file does not exist: %v", err) | 40 | return "", fmt.Errorf("file does not exist: %v", err) | ||
| 52 | } | 41 | } | ||
| 53 | 42 | ||||
| 54 | - | return saveInPath, nil | 43 | + | return imagePath, nil |
| 55 | } | 44 | } | ||
| 56 | 45 | ||||
| 57 | - | func (sc *StorageConfig) DeleteImageFromStorage(imageId string) error { | 46 | + | func RemoveFromStorage() error { |
| 58 | - | // Delete the image from the storage directory | 47 | + | // Delete the file |
| 59 | - | imagePath := filepath.Join(sc.StorageDir, imageId+".tar") | |||
| 60 | err := os.Remove(imagePath) | 48 | err := os.Remove(imagePath) | ||
| 61 | if err != nil { | 49 | if err != nil { | ||
| 62 | - | return fmt.Errorf("failed to delete image from storage directory: %v", err) | 50 | + | return fmt.Errorf("failed to remove file: %v", err) |
| 63 | } | 51 | } | ||
| 64 | 52 | ||||
| 53 | + | // clean up the path | |||
| 54 | + | imagePath = "" | |||
| 55 | + | ||||
| 65 | return nil | 56 | return nil | ||
| 66 | } | 57 | } | ||