From 3a16b27bb054ea7f1ea3405a2d010e73b67f1e29 Mon Sep 17 00:00:00 2001 From: Lucian Petrut Date: Wed, 10 Jul 2024 13:02:25 +0000 Subject: [PATCH 1/4] Add Harbor *photon Rock images The Harbor project has some Photon-OS based images: * docker.io/goharbor/nginx-photon:v2.10.2 * docker.io/goharbor/registry-photon:v2.10.2 * docker.io/goharbor/trivy-adapter-photon:v2.10.2 * docker.io/goharbor/redis-photon:v2.10.2 * docker.io/goharbor/harbor-portal:v2.10.2 We're defining Rock drop-in replacements, which obviously will not be Photon based. Note that the reference images have an install_cert.sh script that is a no-op on non-Photon images. We're keeping it for now, mostly for consistency. --- v2.10.2/nginx-photon/rockcraft.yaml | 53 ++ v2.10.2/portal/rockcraft.yaml | 91 ++ v2.10.2/redis-photon/rockcraft.yaml | 64 ++ v2.10.2/registry-photon/redis.patch | 883 ++++++++++++++++++++ v2.10.2/registry-photon/rockcraft.yaml | 84 ++ v2.10.2/trivy-adapter-photon/rockcraft.yaml | 94 +++ 6 files changed, 1269 insertions(+) create mode 100644 v2.10.2/nginx-photon/rockcraft.yaml create mode 100644 v2.10.2/portal/rockcraft.yaml create mode 100644 v2.10.2/redis-photon/rockcraft.yaml create mode 100644 v2.10.2/registry-photon/redis.patch create mode 100644 v2.10.2/registry-photon/rockcraft.yaml create mode 100644 v2.10.2/trivy-adapter-photon/rockcraft.yaml diff --git a/v2.10.2/nginx-photon/rockcraft.yaml b/v2.10.2/nginx-photon/rockcraft.yaml new file mode 100644 index 0000000..3e64a61 --- /dev/null +++ b/v2.10.2/nginx-photon/rockcraft.yaml @@ -0,0 +1,53 @@ +name: nginx +summary: Rock replacement for the Harbor Nginx image. +description: > + This rock is a drop in replacement for the + docker.io/goharbor/nginx-photon:v2.10.2 image. +# Based on the following: +# https://github.com/goharbor/harbor/tree/v2.10.2/make/photon/nginx +version: v2.10.2 +license: Apache-2.0 + +base: ubuntu@22.04 +build-base: ubuntu@22.04 +platforms: + amd64: + arm64: + +services: + nginx: + command: nginx -g daemon off + override: replace + startup: enabled + user: nginx + group: nginx + +parts: + nginx-user: + plugin: nil + overlay-script: | + groupadd -R $CRAFT_OVERLAY -g 10000 nginx + useradd -R $CRAFT_OVERLAY -u 10000 -g 10000 \ + -d /home/nginx -s /bin/bash -m nginx + + nginx: + after: [nginx-user] + plugin: nil + stage-packages: [nginx] + override-build: | + chown -R 10000:10000 $CRAFT_PART_INSTALL/etc/nginx + + mkdir -p $CRAFT_PART_INSTALL/var/log/nginx + chown -R 10000:10000 $CRAFT_PART_INSTALL/var/log/nginx + + # The reference image creates these symlinks, however a volume is expected + # to be mounted at the given location. + # + # Also, the github image builder job fails when having these links: + # https://paste.ubuntu.com/p/zsDHyR2NY4/plain/ + # + # ln -sf /dev/stdout $CRAFT_PART_INSTALL/var/log/nginx/access.log + # ln -sf /dev/stderr $CRAFT_PART_INSTALL/var/log/nginx/error.log + + # TODO: the upstream image defines a healthcheck, stop signal and a volume, + # should/can we do the same? diff --git a/v2.10.2/portal/rockcraft.yaml b/v2.10.2/portal/rockcraft.yaml new file mode 100644 index 0000000..31bbefb --- /dev/null +++ b/v2.10.2/portal/rockcraft.yaml @@ -0,0 +1,91 @@ +name: portal +summary: Rock replacement for the Harbor Portal image. +description: > + This rock is a drop in replacement for the + docker.io/goharbor/harbor-portal:v2.10.2 image. +# Based on the following: +# https://github.com/goharbor/harbor/tree/v2.10.2/make/photon/portal +version: v2.10.2 +license: Apache-2.0 + +base: ubuntu@22.04 +build-base: ubuntu@22.04 +platforms: + amd64: + arm64: + +services: + nginx: + command: nginx -g daemon off + override: replace + startup: enabled + user: nginx + group: nginx + +parts: + nginx-user: + plugin: nil + overlay-script: | + groupadd -R $CRAFT_OVERLAY -g 10000 nginx + useradd -R $CRAFT_OVERLAY -u 10000 -g 10000 \ + -d /home/nginx -s /bin/bash -m nginx + + nginx: + after: [nginx-user] + plugin: nil + stage-packages: [nginx] + override-build: | + chown -R 10000:10000 $CRAFT_PART_INSTALL/etc/nginx + + mkdir -p $CRAFT_PART_INSTALL/var/log/nginx + chown -R 10000:10000 $CRAFT_PART_INSTALL/var/log/nginx + + # The reference image creates these symlinks, however a volume is expected + # to be mounted at the given location. + # + # Also, the github image builder job fails when having these links: + # https://paste.ubuntu.com/p/zsDHyR2NY4/plain/ + # + # ln -sf /dev/stdout $CRAFT_PART_INSTALL/var/log/nginx/access.log + # ln -sf /dev/stderr $CRAFT_PART_INSTALL/var/log/nginx/error.log + + # TODO: the upstream image defines a healthcheck, stop signal and a volume, + # should/can we do the same? + + portal: + after: [nginx] + plugin: nil + source-type: git + source: https://github.com/goharbor/harbor + source-tag: v2.10.2 + source-depth: 1 + build-snaps: + - node/18/stable + override-build: | + cd $CRAFT_PART_BUILD/src/portal + cp $CRAFT_PART_SRC/api/v2.0/swagger.yaml . + + export NPM_CONFIG_REGISTRY=https://registry.npmjs.org + + export PATH="$PATH:$CRAFT_PART_BUILD/src/portal/node_modules/.bin" + npm install ng-swagger-gen + + npm install --unsafe-perm + npm run generate-build-timestamp + node --max_old_space_size=2048 \ + 'node_modules/@angular/cli/bin/ng' \ + build --configuration production + npm install js-yaml@4.1.0 + node -e "const yaml = require('js-yaml'); const fs = require('fs'); const swagger = yaml.load(fs.readFileSync('swagger.yaml', 'utf8')); fs.writeFileSync('swagger.json', JSON.stringify(swagger));" + cp $CRAFT_PART_SRC/LICENSE dist/ + + cd app-swagger-ui + npm install --unsafe-perm + npm run build + + mkdir -p $CRAFT_PART_INSTALL/usr/share/nginx/html + cp -r $CRAFT_PART_BUILD/src/portal/dist/* $CRAFT_PART_INSTALL/usr/share/nginx/html/ + cp $CRAFT_PART_BUILD/src/portal/swagger.json $CRAFT_PART_INSTALL/usr/share/nginx/html/ + cp -r $CRAFT_PART_BUILD/src/portal/app-swagger-ui/dist/* $CRAFT_PART_INSTALL/usr/share/nginx/html/ + + chown -R 10000:10000 $CRAFT_PART_INSTALL/usr/share/nginx/html diff --git a/v2.10.2/redis-photon/rockcraft.yaml b/v2.10.2/redis-photon/rockcraft.yaml new file mode 100644 index 0000000..56fa402 --- /dev/null +++ b/v2.10.2/redis-photon/rockcraft.yaml @@ -0,0 +1,64 @@ +name: redis +summary: Rock replacement for the Harbor Redis image. +description: > + This rock is a drop in replacement for the + docker.io/goharbor/redis-photon:v2.10.2 image. +# Based on the following: +# https://github.com/goharbor/harbor/tree/v2.10.2/make/photon/redis +version: v2.10.2 +license: Apache-2.0 + +base: ubuntu@22.04 +build-base: ubuntu@22.04 +platforms: + amd64: + arm64: + +package-repositories: + - type: apt + components: [main] + suites: [jammy] + key-id: 54318FA4052D1E61A6B6F7BB5F4349D6BF53AA0C + url: https://packages.redis.io/deb + priority: always + +services: + redis: + command: redis-server /etc/redis.conf + override: replace + startup: enabled + user: redis + group: redis + # working-dir: /var/lib/redis + +parts: + redis-user: + plugin: nil + overlay-script: | + groupadd -R $CRAFT_OVERLAY -g 999 redis + useradd -R $CRAFT_OVERLAY -u 999 -g 999 -c "Redis Database Server" \ + -d /var/lib/redis -s /sbin/nologin -m redis + + image-prep: + after: [redis-user] + plugin: nil + source-type: git + source: https://github.com/goharbor/harbor + source-tag: v2.10.2 + source-depth: 1 + override-build: | + mkdir -p $CRAFT_PART_INSTALL/usr/bin + mkdir -p $CRAFT_PART_INSTALL/etc + cd $CRAFT_PART_SRC + cp ./make/photon/redis/docker-healthcheck $CRAFT_PART_INSTALL/usr/bin + cp ./make/photon/redis/redis.conf $CRAFT_PART_INSTALL/etc/redis.conf + + chown 999:999 $CRAFT_PART_INSTALL/etc/redis.conf + chmod +x $CRAFT_PART_INSTALL/usr/bin/docker-healthcheck + + redis: + after: [image-prep] + plugin: nil + stage-packages: [redis] + # TODO: the upstream image defines a healthcheck and a volume, + # should/can we do the same? diff --git a/v2.10.2/registry-photon/redis.patch b/v2.10.2/registry-photon/redis.patch new file mode 100644 index 0000000..ab4649c --- /dev/null +++ b/v2.10.2/registry-photon/redis.patch @@ -0,0 +1,883 @@ +diff --git a/configuration/configuration.go b/configuration/configuration.go +index 7076df85d4..3e74330321 100644 +--- a/configuration/configuration.go ++++ b/configuration/configuration.go +@@ -168,6 +168,9 @@ type Configuration struct { + // Addr specifies the the redis instance available to the application. + Addr string `yaml:"addr,omitempty"` + ++ // SentinelMasterSet specifies the the redis sentinel master set name. ++ SentinelMasterSet string `yaml:"sentinelMasterSet,omitempty"` ++ + // Password string to use when making a connection. + Password string `yaml:"password,omitempty"` + +diff --git a/registry/handlers/app.go b/registry/handlers/app.go +index bf56cea22a..4a7cee9a2e 100644 +--- a/registry/handlers/app.go ++++ b/registry/handlers/app.go +@@ -3,6 +3,7 @@ package handlers + import ( + "context" + "crypto/rand" ++ "errors" + "expvar" + "fmt" + "math" +@@ -16,6 +17,7 @@ import ( + "strings" + "time" + ++ "github.com/FZambia/sentinel" + "github.com/distribution/reference" + "github.com/docker/distribution" + "github.com/docker/distribution/configuration" +@@ -499,6 +501,45 @@ func (app *App) configureRedis(configuration *configuration.Configuration) { + return + } + ++ var getRedisAddr func() (string, error) ++ var testOnBorrow func(c redis.Conn, t time.Time) error ++ if configuration.Redis.SentinelMasterSet != "" { ++ sntnl := &sentinel.Sentinel{ ++ Addrs: strings.Split(configuration.Redis.Addr, ","), ++ MasterName: configuration.Redis.SentinelMasterSet, ++ Dial: func(addr string) (redis.Conn, error) { ++ c, err := redis.DialTimeout("tcp", addr, ++ configuration.Redis.DialTimeout, ++ configuration.Redis.ReadTimeout, ++ configuration.Redis.WriteTimeout) ++ if err != nil { ++ return nil, err ++ } ++ return c, nil ++ }, ++ } ++ getRedisAddr = func() (string, error) { ++ return sntnl.MasterAddr() ++ } ++ testOnBorrow = func(c redis.Conn, t time.Time) error { ++ if !sentinel.TestRole(c, "master") { ++ return errors.New("role check failed") ++ } ++ return nil ++ } ++ ++ } else { ++ getRedisAddr = func() (string, error) { ++ return configuration.Redis.Addr, nil ++ } ++ testOnBorrow = func(c redis.Conn, t time.Time) error { ++ // TODO(stevvooe): We can probably do something more interesting ++ // here with the health package. ++ _, err := c.Do("PING") ++ return err ++ } ++ } ++ + pool := &redis.Pool{ + Dial: func() (redis.Conn, error) { + // TODO(stevvooe): Yet another use case for contextual timing. +@@ -514,8 +555,11 @@ func (app *App) configureRedis(configuration *configuration.Configuration) { + } + } + +- conn, err := redis.DialTimeout("tcp", +- configuration.Redis.Addr, ++ redisAddr, err := getRedisAddr() ++ if err != nil { ++ return nil, err ++ } ++ conn, err := redis.DialTimeout("tcp", redisAddr, + configuration.Redis.DialTimeout, + configuration.Redis.ReadTimeout, + configuration.Redis.WriteTimeout) +@@ -547,16 +591,11 @@ func (app *App) configureRedis(configuration *configuration.Configuration) { + done(nil) + return conn, nil + }, +- MaxIdle: configuration.Redis.Pool.MaxIdle, +- MaxActive: configuration.Redis.Pool.MaxActive, +- IdleTimeout: configuration.Redis.Pool.IdleTimeout, +- TestOnBorrow: func(c redis.Conn, t time.Time) error { +- // TODO(stevvooe): We can probably do something more interesting +- // here with the health package. +- _, err := c.Do("PING") +- return err +- }, +- Wait: false, // if a connection is not available, proceed without cache. ++ MaxIdle: configuration.Redis.Pool.MaxIdle, ++ MaxActive: configuration.Redis.Pool.MaxActive, ++ IdleTimeout: configuration.Redis.Pool.IdleTimeout, ++ TestOnBorrow: testOnBorrow, ++ Wait: false, // if a connection is not available, proceed without cache. + } + + app.redis = pool +diff --git a/registry/handlers/app_test.go b/registry/handlers/app_test.go +index 60a57e6c15..8a644d83d8 100644 +--- a/registry/handlers/app_test.go ++++ b/registry/handlers/app_test.go +@@ -140,7 +140,29 @@ func TestAppDispatcher(t *testing.T) { + // TestNewApp covers the creation of an application via NewApp with a + // configuration. + func TestNewApp(t *testing.T) { +- ctx := context.Background() ++ ++ config := configuration.Configuration{ ++ Storage: configuration.Storage{ ++ "testdriver": nil, ++ "maintenance": configuration.Parameters{"uploadpurging": map[interface{}]interface{}{ ++ "enabled": false, ++ }}, ++ }, ++ Auth: configuration.Auth{ ++ // For now, we simply test that new auth results in a viable ++ // application. ++ "silly": { ++ "realm": "realm-test", ++ "service": "service-test", ++ }, ++ }, ++ } ++ runAppWithConfig(t, config) ++} ++ ++// TestNewApp covers the creation of an application via NewApp with a ++// configuration(with redis). ++func TestNewAppWithRedis(t *testing.T) { + config := configuration.Configuration{ + Storage: configuration.Storage{ + "testdriver": nil, +@@ -157,7 +179,38 @@ func TestNewApp(t *testing.T) { + }, + }, + } ++ config.Redis.Addr = "127.0.0.1:6379" ++ config.Redis.DB = 0 ++ runAppWithConfig(t, config) ++} + ++// TestNewApp covers the creation of an application via NewApp with a ++// configuration(with redis sentinel cluster). ++func TestNewAppWithRedisSentinelCluster(t *testing.T) { ++ config := configuration.Configuration{ ++ Storage: configuration.Storage{ ++ "testdriver": nil, ++ "maintenance": configuration.Parameters{"uploadpurging": map[interface{}]interface{}{ ++ "enabled": false, ++ }}, ++ }, ++ Auth: configuration.Auth{ ++ // For now, we simply test that new auth results in a viable ++ // application. ++ "silly": { ++ "realm": "realm-test", ++ "service": "service-test", ++ }, ++ }, ++ } ++ config.Redis.Addr = "192.168.0.11:26379,192.168.0.12:26379" ++ config.Redis.DB = 0 ++ config.Redis.SentinelMasterSet = "mymaster" ++ runAppWithConfig(t, config) ++} ++ ++func runAppWithConfig(t *testing.T, config configuration.Configuration) { ++ ctx := context.Background() + // Mostly, with this test, given a sane configuration, we are simply + // ensuring that NewApp doesn't panic. We might want to tweak this + // behavior. +diff --git a/vendor.conf b/vendor.conf +index 33fe616b76..a8d8f58bc6 100644 +--- a/vendor.conf ++++ b/vendor.conf +@@ -51,3 +51,4 @@ gopkg.in/yaml.v2 v2.2.1 + rsc.io/letsencrypt e770c10b0f1a64775ae91d240407ce00d1a5bdeb https://github.com/dmcgowan/letsencrypt.git + github.com/opencontainers/go-digest ea51bea511f75cfa3ef6098cc253c5c3609b037a # v1.0.0 + github.com/opencontainers/image-spec 67d2d5658fe0476ab9bf414cec164077ebff3920 # v1.0.2 ++github.com/FZambia/sentinel 5585739eb4b6478aa30161866ccf9ce0ef5847c7 https://github.com/jeremyxu2010/sentinel.git +diff --git a/vendor/github.com/FZambia/sentinel/LICENSE b/vendor/github.com/FZambia/sentinel/LICENSE +new file mode 100644 +index 0000000000..8dada3edaf +--- /dev/null ++++ b/vendor/github.com/FZambia/sentinel/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/FZambia/sentinel/README.md b/vendor/github.com/FZambia/sentinel/README.md +new file mode 100644 +index 0000000000..f544c54ef6 +--- /dev/null ++++ b/vendor/github.com/FZambia/sentinel/README.md +@@ -0,0 +1,39 @@ ++go-sentinel ++=========== ++ ++Redis Sentinel support for [redigo](https://github.com/gomodule/redigo) library. ++ ++Documentation ++------------- ++ ++- [API Reference](http://godoc.org/github.com/FZambia/sentinel) ++ ++Alternative solution ++-------------------- ++ ++You can alternatively configure Haproxy between your application and Redis to proxy requests to Redis master instance if you only need HA: ++ ++``` ++listen redis ++ server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 ++ server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup ++ bind *:6379 ++ mode tcp ++ option tcpka ++ option tcplog ++ option tcp-check ++ tcp-check send PING\r\n ++ tcp-check expect string +PONG ++ tcp-check send info\ replication\r\n ++ tcp-check expect string role:master ++ tcp-check send QUIT\r\n ++ tcp-check expect string +OK ++ balance roundrobin ++``` ++ ++This way you don't need to use this library. ++ ++License ++------- ++ ++Library is available under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). +diff --git a/vendor/github.com/FZambia/sentinel/sentinel.go b/vendor/github.com/FZambia/sentinel/sentinel.go +new file mode 100644 +index 0000000000..79209e9f0d +--- /dev/null ++++ b/vendor/github.com/FZambia/sentinel/sentinel.go +@@ -0,0 +1,426 @@ ++package sentinel ++ ++import ( ++ "errors" ++ "fmt" ++ "net" ++ "strings" ++ "sync" ++ "time" ++ ++ "github.com/garyburd/redigo/redis" ++) ++ ++// Sentinel provides a way to add high availability (HA) to Redis Pool using ++// preconfigured addresses of Sentinel servers and name of master which Sentinels ++// monitor. It works with Redis >= 2.8.12 (mostly because of ROLE command that ++// was introduced in that version, it's possible though to support old versions ++// using INFO command). ++// ++// Example of the simplest usage to contact master "mymaster": ++// ++// func newSentinelPool() *redis.Pool { ++// sntnl := &sentinel.Sentinel{ ++// Addrs: []string{":26379", ":26380", ":26381"}, ++// MasterName: "mymaster", ++// Dial: func(addr string) (redis.Conn, error) { ++// timeout := 500 * time.Millisecond ++// c, err := redis.DialTimeout("tcp", addr, timeout, timeout, timeout) ++// if err != nil { ++// return nil, err ++// } ++// return c, nil ++// }, ++// } ++// return &redis.Pool{ ++// MaxIdle: 3, ++// MaxActive: 64, ++// Wait: true, ++// IdleTimeout: 240 * time.Second, ++// Dial: func() (redis.Conn, error) { ++// masterAddr, err := sntnl.MasterAddr() ++// if err != nil { ++// return nil, err ++// } ++// c, err := redis.Dial("tcp", masterAddr) ++// if err != nil { ++// return nil, err ++// } ++// return c, nil ++// }, ++// TestOnBorrow: func(c redis.Conn, t time.Time) error { ++// if !sentinel.TestRole(c, "master") { ++// return errors.New("Role check failed") ++// } else { ++// return nil ++// } ++// }, ++// } ++// } ++type Sentinel struct { ++ // Addrs is a slice with known Sentinel addresses. ++ Addrs []string ++ ++ // MasterName is a name of Redis master Sentinel servers monitor. ++ MasterName string ++ ++ // Dial is a user supplied function to connect to Sentinel on given address. This ++ // address will be chosen from Addrs slice. ++ // Note that as per the redis-sentinel client guidelines, a timeout is mandatory ++ // while connecting to Sentinels, and should not be set to 0. ++ Dial func(addr string) (redis.Conn, error) ++ ++ // Pool is a user supplied function returning custom connection pool to Sentinel. ++ // This can be useful to tune options if you are not satisfied with what default ++ // Sentinel pool offers. See defaultPool() method for default pool implementation. ++ // In most cases you only need to provide Dial function and let this be nil. ++ Pool func(addr string) *redis.Pool ++ ++ mu sync.RWMutex ++ pools map[string]*redis.Pool ++ addr string ++} ++ ++// NoSentinelsAvailable is returned when all sentinels in the list are exhausted ++// (or none configured), and contains the last error returned by Dial (which ++// may be nil) ++type NoSentinelsAvailable struct { ++ lastError error ++} ++ ++func (ns NoSentinelsAvailable) Error() string { ++ if ns.lastError != nil { ++ return fmt.Sprintf("redigo: no sentinels available; last error: %s", ns.lastError.Error()) ++ } ++ return fmt.Sprintf("redigo: no sentinels available") ++} ++ ++// putToTop puts Sentinel address to the top of address list - this means ++// that all next requests will use Sentinel on this address first. ++// ++// From Sentinel guidelines: ++// ++// The first Sentinel replying to the client request should be put at the ++// start of the list, so that at the next reconnection, we'll try first ++// the Sentinel that was reachable in the previous connection attempt, ++// minimizing latency. ++// ++// Lock must be held by caller. ++func (s *Sentinel) putToTop(addr string) { ++ addrs := s.Addrs ++ if addrs[0] == addr { ++ // Already on top. ++ return ++ } ++ newAddrs := []string{addr} ++ for _, a := range addrs { ++ if a == addr { ++ continue ++ } ++ newAddrs = append(newAddrs, a) ++ } ++ s.Addrs = newAddrs ++} ++ ++// putToBottom puts Sentinel address to the bottom of address list. ++// We call this method internally when see that some Sentinel failed to answer ++// on application request so next time we start with another one. ++// ++// Lock must be held by caller. ++func (s *Sentinel) putToBottom(addr string) { ++ addrs := s.Addrs ++ if addrs[len(addrs)-1] == addr { ++ // Already on bottom. ++ return ++ } ++ newAddrs := []string{} ++ for _, a := range addrs { ++ if a == addr { ++ continue ++ } ++ newAddrs = append(newAddrs, a) ++ } ++ newAddrs = append(newAddrs, addr) ++ s.Addrs = newAddrs ++} ++ ++// defaultPool returns a connection pool to one Sentinel. This allows ++// us to call concurrent requests to Sentinel using connection Do method. ++func (s *Sentinel) defaultPool(addr string) *redis.Pool { ++ return &redis.Pool{ ++ MaxIdle: 3, ++ MaxActive: 10, ++ Wait: true, ++ IdleTimeout: 240 * time.Second, ++ Dial: func() (redis.Conn, error) { ++ return s.Dial(addr) ++ }, ++ TestOnBorrow: func(c redis.Conn, t time.Time) error { ++ _, err := c.Do("PING") ++ return err ++ }, ++ } ++} ++ ++func (s *Sentinel) get(addr string) redis.Conn { ++ pool := s.poolForAddr(addr) ++ return pool.Get() ++} ++ ++func (s *Sentinel) poolForAddr(addr string) *redis.Pool { ++ s.mu.Lock() ++ if s.pools == nil { ++ s.pools = make(map[string]*redis.Pool) ++ } ++ pool, ok := s.pools[addr] ++ if ok { ++ s.mu.Unlock() ++ return pool ++ } ++ s.mu.Unlock() ++ newPool := s.newPool(addr) ++ s.mu.Lock() ++ p, ok := s.pools[addr] ++ if ok { ++ s.mu.Unlock() ++ return p ++ } ++ s.pools[addr] = newPool ++ s.mu.Unlock() ++ return newPool ++} ++ ++func (s *Sentinel) newPool(addr string) *redis.Pool { ++ if s.Pool != nil { ++ return s.Pool(addr) ++ } ++ return s.defaultPool(addr) ++} ++ ++// close connection pool to Sentinel. ++// Lock must be hold by caller. ++func (s *Sentinel) close() { ++ if s.pools != nil { ++ for _, pool := range s.pools { ++ pool.Close() ++ } ++ } ++ s.pools = nil ++} ++ ++func (s *Sentinel) doUntilSuccess(f func(redis.Conn) (interface{}, error)) (interface{}, error) { ++ s.mu.RLock() ++ addrs := s.Addrs ++ s.mu.RUnlock() ++ ++ var lastErr error ++ ++ for _, addr := range addrs { ++ conn := s.get(addr) ++ reply, err := f(conn) ++ conn.Close() ++ if err != nil { ++ lastErr = err ++ s.mu.Lock() ++ pool, ok := s.pools[addr] ++ if ok { ++ pool.Close() ++ delete(s.pools, addr) ++ } ++ s.putToBottom(addr) ++ s.mu.Unlock() ++ continue ++ } ++ s.putToTop(addr) ++ return reply, nil ++ } ++ ++ return nil, NoSentinelsAvailable{lastError: lastErr} ++} ++ ++// MasterAddr returns an address of current Redis master instance. ++func (s *Sentinel) MasterAddr() (string, error) { ++ res, err := s.doUntilSuccess(func(c redis.Conn) (interface{}, error) { ++ return queryForMaster(c, s.MasterName) ++ }) ++ if err != nil { ++ return "", err ++ } ++ return res.(string), nil ++} ++ ++// SlaveAddrs returns a slice with known slave addresses of current master instance. ++func (s *Sentinel) SlaveAddrs() ([]string, error) { ++ res, err := s.doUntilSuccess(func(c redis.Conn) (interface{}, error) { ++ return queryForSlaveAddrs(c, s.MasterName) ++ }) ++ if err != nil { ++ return nil, err ++ } ++ return res.([]string), nil ++} ++ ++// Slave represents a Redis slave instance which is known by Sentinel. ++type Slave struct { ++ ip string ++ port string ++ flags string ++} ++ ++// Addr returns an address of slave. ++func (s *Slave) Addr() string { ++ return net.JoinHostPort(s.ip, s.port) ++} ++ ++// Available returns if slave is in working state at moment based on information in slave flags. ++func (s *Slave) Available() bool { ++ return !strings.Contains(s.flags, "disconnected") && !strings.Contains(s.flags, "s_down") ++} ++ ++// Slaves returns a slice with known slaves of master instance. ++func (s *Sentinel) Slaves() ([]*Slave, error) { ++ res, err := s.doUntilSuccess(func(c redis.Conn) (interface{}, error) { ++ return queryForSlaves(c, s.MasterName) ++ }) ++ if err != nil { ++ return nil, err ++ } ++ return res.([]*Slave), nil ++} ++ ++// SentinelAddrs returns a slice of known Sentinel addresses Sentinel server aware of. ++func (s *Sentinel) SentinelAddrs() ([]string, error) { ++ res, err := s.doUntilSuccess(func(c redis.Conn) (interface{}, error) { ++ return queryForSentinels(c, s.MasterName) ++ }) ++ if err != nil { ++ return nil, err ++ } ++ return res.([]string), nil ++} ++ ++// Discover allows to update list of known Sentinel addresses. From docs: ++// ++// A client may update its internal list of Sentinel nodes following this procedure: ++// 1) Obtain a list of other Sentinels for this master using the command SENTINEL sentinels . ++// 2) Add every ip:port pair not already existing in our list at the end of the list. ++func (s *Sentinel) Discover() error { ++ addrs, err := s.SentinelAddrs() ++ if err != nil { ++ return err ++ } ++ s.mu.Lock() ++ for _, addr := range addrs { ++ if !stringInSlice(addr, s.Addrs) { ++ s.Addrs = append(s.Addrs, addr) ++ } ++ } ++ s.mu.Unlock() ++ return nil ++} ++ ++// Close closes current connection to Sentinel. ++func (s *Sentinel) Close() error { ++ s.mu.Lock() ++ s.close() ++ s.mu.Unlock() ++ return nil ++} ++ ++// TestRole wraps GetRole in a test to verify if the role matches an expected ++// role string. If there was any error in querying the supplied connection, ++// the function returns false. Works with Redis >= 2.8.12. ++// It's not goroutine safe, but if you call this method on pooled connections ++// then you are OK. ++func TestRole(c redis.Conn, expectedRole string) bool { ++ role, err := getRole(c) ++ if err != nil || role != expectedRole { ++ return false ++ } ++ return true ++} ++ ++// getRole is a convenience function supplied to query an instance (master or ++// slave) for its role. It attempts to use the ROLE command introduced in ++// redis 2.8.12. ++func getRole(c redis.Conn) (string, error) { ++ res, err := c.Do("ROLE") ++ if err != nil { ++ return "", err ++ } ++ rres, ok := res.([]interface{}) ++ if ok { ++ return redis.String(rres[0], nil) ++ } ++ return "", errors.New("redigo: can not transform ROLE reply to string") ++} ++ ++func queryForMaster(conn redis.Conn, masterName string) (string, error) { ++ res, err := redis.Strings(conn.Do("SENTINEL", "get-master-addr-by-name", masterName)) ++ if err != nil { ++ return "", err ++ } ++ if len(res) < 2 { ++ return "", errors.New("redigo: malformed get-master-addr-by-name reply") ++ } ++ masterAddr := net.JoinHostPort(res[0], res[1]) ++ return masterAddr, nil ++} ++ ++func queryForSlaveAddrs(conn redis.Conn, masterName string) ([]string, error) { ++ slaves, err := queryForSlaves(conn, masterName) ++ if err != nil { ++ return nil, err ++ } ++ slaveAddrs := make([]string, 0) ++ for _, slave := range slaves { ++ slaveAddrs = append(slaveAddrs, slave.Addr()) ++ } ++ return slaveAddrs, nil ++} ++ ++func queryForSlaves(conn redis.Conn, masterName string) ([]*Slave, error) { ++ res, err := redis.Values(conn.Do("SENTINEL", "slaves", masterName)) ++ if err != nil { ++ return nil, err ++ } ++ slaves := make([]*Slave, 0) ++ for _, a := range res { ++ sm, err := redis.StringMap(a, err) ++ if err != nil { ++ return slaves, err ++ } ++ slave := &Slave{ ++ ip: sm["ip"], ++ port: sm["port"], ++ flags: sm["flags"], ++ } ++ slaves = append(slaves, slave) ++ } ++ return slaves, nil ++} ++ ++func queryForSentinels(conn redis.Conn, masterName string) ([]string, error) { ++ res, err := redis.Values(conn.Do("SENTINEL", "sentinels", masterName)) ++ if err != nil { ++ return nil, err ++ } ++ sentinels := make([]string, 0) ++ for _, a := range res { ++ sm, err := redis.StringMap(a, err) ++ if err != nil { ++ return sentinels, err ++ } ++ sentinels = append(sentinels, fmt.Sprintf("%s:%s", sm["ip"], sm["port"])) ++ } ++ return sentinels, nil ++} ++ ++func stringInSlice(str string, slice []string) bool { ++ for _, s := range slice { ++ if s == str { ++ return true ++ } ++ } ++ return false ++} diff --git a/v2.10.2/registry-photon/rockcraft.yaml b/v2.10.2/registry-photon/rockcraft.yaml new file mode 100644 index 0000000..a01c3c7 --- /dev/null +++ b/v2.10.2/registry-photon/rockcraft.yaml @@ -0,0 +1,84 @@ +name: registry +summary: Rock replacement for the Harbor registry image. +description: > + This rock is a drop in replacement for the + docker.io/goharbor/registry-photon:v2.10.2 image. +# Based on the following: +# https://github.com/goharbor/harbor/tree/v2.10.2/make/photon/registry +version: v2.10.2 +license: Apache-2.0 + +base: ubuntu@22.04 +build-base: ubuntu@22.04 +platforms: + amd64: + arm64: + +services: + registry: + command: /home/harbor/entrypoint.sh + override: replace + startup: enabled + user: harbor + group: harbor + working-dir: /home/harbor + +parts: + create-user: + plugin: nil + overlay-script: | + groupadd -R $CRAFT_OVERLAY -r -g 10000 harbor + useradd -R $CRAFT_OVERLAY \ + --no-log-init -r -m -g 10000 -u 10000 harbor + + image-prep: + after: [create-user] + plugin: nil + source-type: git + source: https://github.com/goharbor/harbor + source-tag: v2.10.2 + source-depth: 1 + override-build: | + OUTDIR="$CRAFT_PART_INSTALL/home/harbor" + mkdir -p "$OUTDIR" + cd $CRAFT_PART_SRC + cp ./make/photon/common/install_cert.sh "$OUTDIR/" + cp ./make/photon/registry/entrypoint.sh "$OUTDIR/" + mkdir -p "$CRAFT_PART_INSTALL/etc/pki/tls/certs" + chown -R 10000:10000 "$CRAFT_PART_INSTALL/etc/pki/tls/certs" + chown -R 10000:10000 "$OUTDIR" + chmod u+x "$OUTDIR/entrypoint.sh" + chmod u+x "$OUTDIR/install_cert.sh" + + registry: + after: [image-prep] + build-snaps: + - go/1.21/stable + plugin: go + source-type: git + source: https://github.com/distribution/distribution.git + source-tag: v2.8.3 + source-depth: 1 + override-build: | + git apply --ignore-whitespace $CRAFT_PROJECT_DIR/redis.patch + + # 2.8.3 doesn't have a go.mod definition. + mkdir -p /go/src/github.com/docker + + ln -sf $(pwd) /go/src/github.com/docker/distribution + + pushd /go/src/github.com/docker/distribution + + export GOPATH=/go + export BUILDTAGS=include_oss include_gcs + export GO111MODULE=auto + export CGO_ENABLED=0 + make clean binaries + + mkdir -p $CRAFT_PART_INSTALL/usr/bin + + cp bin/registry $CRAFT_PART_INSTALL/usr/bin/registry_DO_NOT_USE_GC + chown 10000:10000 $CRAFT_PART_INSTALL/usr/bin/registry_DO_NOT_USE_GC + + # TODO: the upstream image defines a healthcheck and a volume, + # should/can we do the same? diff --git a/v2.10.2/trivy-adapter-photon/rockcraft.yaml b/v2.10.2/trivy-adapter-photon/rockcraft.yaml new file mode 100644 index 0000000..d7db7a2 --- /dev/null +++ b/v2.10.2/trivy-adapter-photon/rockcraft.yaml @@ -0,0 +1,94 @@ +name: trivy-adapter +summary: Rock replacement for the Harbor Trivy adapter image. +description: > + This rock is a drop in replacement for the + docker.io/goharbor/trivy-adapter-photon:v2.10.2 image. +# Based on the following: +# https://github.com/goharbor/harbor/tree/v2.10.2/make/photon/trivy-adapter +version: v2.10.2 +license: Apache-2.0 + +base: ubuntu@22.04 +build-base: ubuntu@22.04 +platforms: + amd64: + arm64: + +services: + scanner: + command: /home/scanner/entrypoint.sh + override: replace + startup: enabled + user: scanner + group: scanner + +parts: + create-user: + plugin: nil + overlay-script: | + groupadd -R $CRAFT_OVERLAY -r -g 10000 scanner + useradd -R $CRAFT_OVERLAY \ + --no-log-init -r -m -g 10000 -u 10000 scanner + + image-prep: + after: [create-user] + plugin: nil + source-type: git + source: https://github.com/goharbor/harbor + source-tag: v2.10.2 + source-depth: 1 + override-build: | + OUTDIR="$CRAFT_PART_INSTALL/home/scanner" + mkdir -p "$OUTDIR" + cd $CRAFT_PART_SRC + cp ./make/photon/common/install_cert.sh "$OUTDIR/" + cp ./make/photon/trivy-adapter/entrypoint.sh "$OUTDIR/" + mkdir -p "$CRAFT_PART_INSTALL/etc/pki/tls/certs" + chown -R 10000:10000 "$CRAFT_PART_INSTALL/etc/pki/tls/certs" + chown -R 10000:10000 "$OUTDIR" + chmod u+x "$OUTDIR/entrypoint.sh" + chmod u+x "$OUTDIR/install_cert.sh" + + trivy: + after: [image-prep] + build-snaps: + - go/1.21/stable + plugin: go + source-type: git + source: https://github.com/aquasecurity/trivy + source-tag: v0.50.1 + source-depth: 1 + override-build: | + export GOOS=linux + export GO111MODULE=on + export CGO_ENABLED=0 + go build -o trivy cmd/trivy/main.go + + mkdir -p $CRAFT_PART_INSTALL/usr/local/bin + cp trivy $CRAFT_PART_INSTALL/usr/local/bin/trivy + chown 10000:10000 $CRAFT_PART_INSTALL/usr/local/bin/trivy + + # TODO: the upstream image defines a healthcheck, + # should/can we do the same? + + trivy-adapter: + after: [image-prep] + build-snaps: + - go/1.21/stable + plugin: go + source-type: git + source: https://github.com/aquasecurity/harbor-scanner-trivy + source-tag: v0.30.23 + source-depth: 1 + override-build: | + export GOOS=linux + export GO111MODULE=on + export CGO_ENABLED=0 + go build -o scanner-trivy cmd/scanner-trivy/main.go + + mkdir -p $CRAFT_PART_INSTALL/home/scanner/bin + cp scanner-trivy $CRAFT_PART_INSTALL/home/scanner/bin/scanner-trivy + chown 10000:10000 $CRAFT_PART_INSTALL/home/scanner/bin/scanner-trivy + + # TODO: the upstream image defines a healthcheck, + # should/can we do the same? From f74434ef762cac9937e57ec022995420f96644b6 Mon Sep 17 00:00:00 2001 From: Lucian Petrut Date: Tue, 16 Jul 2024 07:32:17 +0000 Subject: [PATCH 2/4] Fix portal image Rockcraft does not allow two parts to stage the same files with different content. In our case, the nginx package includes a sample at /usr/share/nginx/html that has to be removed. --- v2.10.2/portal/rockcraft.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/v2.10.2/portal/rockcraft.yaml b/v2.10.2/portal/rockcraft.yaml index 31bbefb..87cc99b 100644 --- a/v2.10.2/portal/rockcraft.yaml +++ b/v2.10.2/portal/rockcraft.yaml @@ -49,6 +49,10 @@ parts: # ln -sf /dev/stdout $CRAFT_PART_INSTALL/var/log/nginx/access.log # ln -sf /dev/stderr $CRAFT_PART_INSTALL/var/log/nginx/error.log + # Drop the sample html dir, we'll add the portal files instead and we + # have to avoid staging the same files with different content. + rm -rf $CRAFT_PART_INSTALL/usr/share/nginx/html/ + # TODO: the upstream image defines a healthcheck, stop signal and a volume, # should/can we do the same? From 01cb25556ea234745e8feb9ee6780a673d21accd Mon Sep 17 00:00:00 2001 From: Lucian Petrut Date: Wed, 17 Jul 2024 13:16:40 +0000 Subject: [PATCH 3/4] portal: drop unnecessary build steps We'll skip adding the node_modules dir to $PATH. At the same time, we'll no longer install ng-swagger-gen explicitly, expecting it to be retrieved as part of the build process. --- v2.10.2/portal/rockcraft.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/v2.10.2/portal/rockcraft.yaml b/v2.10.2/portal/rockcraft.yaml index 87cc99b..e8f5107 100644 --- a/v2.10.2/portal/rockcraft.yaml +++ b/v2.10.2/portal/rockcraft.yaml @@ -71,9 +71,6 @@ parts: export NPM_CONFIG_REGISTRY=https://registry.npmjs.org - export PATH="$PATH:$CRAFT_PART_BUILD/src/portal/node_modules/.bin" - npm install ng-swagger-gen - npm install --unsafe-perm npm run generate-build-timestamp node --max_old_space_size=2048 \ From 2f5b037a734f017a731e471e9f1b949b04daed2b Mon Sep 17 00:00:00 2001 From: Lucian Petrut Date: Wed, 17 Jul 2024 14:34:37 +0000 Subject: [PATCH 4/4] portal: apply cypress build workaround On Ubuntu 22.04 with snap installed nodejs, one of the portal dependencies fails to install: https://github.com/cypress-io/cypress-documentation/issues/5868 We'll apply the recommended workaround. --- v2.10.2/portal/rockcraft.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/v2.10.2/portal/rockcraft.yaml b/v2.10.2/portal/rockcraft.yaml index e8f5107..b84efb4 100644 --- a/v2.10.2/portal/rockcraft.yaml +++ b/v2.10.2/portal/rockcraft.yaml @@ -71,6 +71,9 @@ parts: export NPM_CONFIG_REGISTRY=https://registry.npmjs.org + # https://github.com/cypress-io/cypress-documentation/issues/5868 + npm install cypress --save-dev --foreground-scripts + npm install --unsafe-perm npm run generate-build-timestamp node --max_old_space_size=2048 \