Skip to content
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ require (
github.com/rodaine/table v1.1.1 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/rs/zerolog v1.34.0 // indirect
github.com/rubenv/sql-migrate v1.6.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/ryanuber/columnize v2.1.2+incompatible // indirect
Expand Down
7 changes: 3 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -415,10 +415,6 @@ github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80N
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/deckhouse/deckhouse/pkg/log v0.1.0 h1:2aPfyiHHSIJlX4x7ysyPOaIb7CLmyY+hUf9uDb8TYd8=
github.com/deckhouse/deckhouse/pkg/log v0.1.0/go.mod h1:pbAxTSDcPmwyl3wwKDcEB3qdxHnRxqTV+J0K+sha8bw=
github.com/deckhouse/deckhouse/pkg/registry v0.0.0-20251120122028-65011cba39f4 h1:puYW42+BF8fYuoq/dMDd+oxNprMuuSACWqDss6IQulE=
github.com/deckhouse/deckhouse/pkg/registry v0.0.0-20251120122028-65011cba39f4/go.mod h1:+oNXMQMOaVpDq00i+PX9NXptzIybUDRmxAO7iRWM32s=
github.com/deckhouse/deckhouse/pkg/registry v0.0.0-20260119191635-04ce9157d702 h1:HdfASfTGK2124itxEKqFNqEIEdjJ2XfD0DA+8ONBTok=
github.com/deckhouse/deckhouse/pkg/registry v0.0.0-20260119191635-04ce9157d702/go.mod h1:OdmJduRktTXVMNLAULkzoPbzLbtaU/jBuwSoAUbnxRM=
github.com/deckhouse/deckhouse/pkg/registry v0.0.0-20260120103154-2be5575578db h1:xq4DMxGgDk0IaqUzIqwkKOiY9dtQlpVnEupfE/TBU6c=
github.com/deckhouse/deckhouse/pkg/registry v0.0.0-20260120103154-2be5575578db/go.mod h1:OdmJduRktTXVMNLAULkzoPbzLbtaU/jBuwSoAUbnxRM=
github.com/deckhouse/virtualization/api v1.0.0 h1:q4TvC74tpjk25k0byXJCYP4HjvRexBSeI0cC8QeCMTQ=
Expand Down Expand Up @@ -1290,6 +1286,7 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
Expand Down Expand Up @@ -1643,6 +1640,8 @@ github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/rubenv/sql-migrate v1.6.1 h1:bo6/sjsan9HaXAsNxYP/jCEDUGibHp8JmOBw7NTGRos=
github.com/rubenv/sql-migrate v1.6.1/go.mod h1:tPzespupJS0jacLfhbwto/UjSX+8h2FdWB7ar+QlHa0=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
Expand Down
68 changes: 68 additions & 0 deletions internal/tools/imagedigest/cmd/add/add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Copyright 2025 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package add

import (
"fmt"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"

"github.com/deckhouse/deckhouse-cli/internal/tools/imagedigest"
)

func NewCommand() *cobra.Command {
addCmd := &cobra.Command{
Use: "add <image>",
Short: "Calculating and adding the image digest to the image metadata according to the GOST standard Streebog (GOST R 34.11-2012)",
Long: `Calculating and adding the image digest to the image metadata according to the GOST standard Streebog (GOST R 34.11-2012)`,
Args: func(cmd *cobra.Command, args []string) error {
if err := cobra.MinimumNArgs(1)(cmd, args); err != nil {
return err
}
return nil
},
RunE: runAdd,
}

return addCmd
}

func runAdd(cmd *cobra.Command, args []string) error {
imageName := args[0]

insecure, err := cmd.Flags().GetBool("insecure")
if err != nil {
return fmt.Errorf("failed to get insecure flag: %w", err)
}

var opts []crane.Option
if insecure {
opts = append(opts, crane.Insecure)
}

digest, err := imagedigest.PullAnnotatePush(imageName, opts...)
if err != nil {
log.Fatal().Err(err).Msg("AddGostImageDigest")
}

log.Info().Msgf("GOST Image Digest: %s", digest)
log.Info().Msg("Added successfully")

return nil
}
68 changes: 68 additions & 0 deletions internal/tools/imagedigest/cmd/calculate/calculate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Copyright 2025 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package calculate

import (
"encoding/hex"
"fmt"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"

"github.com/deckhouse/deckhouse-cli/internal/tools/imagedigest"
)

func NewCommand() *cobra.Command {
calculateCmd := &cobra.Command{
Use: "calculate <image>",
Short: "Calculating the image digest according to the GOST standard Streebog (GOST R 34.11-2012)",
Long: `Calculating the image digest according to the GOST standard Streebog (GOST R 34.11-2012)`,
Args: func(cmd *cobra.Command, args []string) error {
if err := cobra.MinimumNArgs(1)(cmd, args); err != nil {
return err
}
return nil
},
RunE: runCalculate,
}

return calculateCmd
}

func runCalculate(cmd *cobra.Command, args []string) error {
imageName := args[0]

insecure, err := cmd.Flags().GetBool("insecure")
if err != nil {
return fmt.Errorf("failed to get insecure flag: %w", err)
}

var opts []crane.Option
if insecure {
opts = append(opts, crane.Insecure)
}

gostImageDigest, err := imagedigest.PullAndCalculate(imageName, opts...)
if err != nil {
log.Fatal().Err(err).Msg("CalculateGostImageDigest")
}

log.Info().Msgf("GOST Image Digest: %s", hex.EncodeToString(gostImageDigest))

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright 2025 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package calculatefromfile

import (
"encoding/hex"
"fmt"
"os"

"github.com/spf13/cobra"

"github.com/deckhouse/deckhouse-cli/internal/tools/imagedigest"
)

func NewCommand() *cobra.Command {
calculateFromFileCmd := &cobra.Command{
Use: "calculate-from-file <file>",
Short: "Calculating the file digest according to the GOST standard Streebog (GOST R 34.11-2012). For stdin use '-'",
Long: `Calculating the file digest according to the GOST standard Streebog (GOST R 34.11-2012)`,
Args: func(cmd *cobra.Command, args []string) error {
if err := cobra.MinimumNArgs(1)(cmd, args); err != nil {
return err
}
return nil
},
RunE: runCalculateFromFile,
}

return calculateFromFileCmd
}

func runCalculateFromFile(cmd *cobra.Command, args []string) error {
filename := args[0]

reader := os.Stdin
if filename != "-" {
file, err := os.Open(filename)
if err != nil {
return fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()
reader = file
}

sum, err := imagedigest.CalculateGostHashFromReader(reader)
if err != nil {
return fmt.Errorf("failed to calculate GOST digest: %w", err)
}

fmt.Println(hex.EncodeToString(sum))

return nil
}
63 changes: 63 additions & 0 deletions internal/tools/imagedigest/cmd/imagedigest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
Copyright 2025 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
"os"

"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"

"github.com/deckhouse/deckhouse-cli/internal/tools/imagedigest/cmd/add"
"github.com/deckhouse/deckhouse-cli/internal/tools/imagedigest/cmd/calculate"
"github.com/deckhouse/deckhouse-cli/internal/tools/imagedigest/cmd/calculatefromfile"
"github.com/deckhouse/deckhouse-cli/internal/tools/imagedigest/cmd/validate"
)

func NewCommand() *cobra.Command {
imagedigestCmd := &cobra.Command{
Use: "imagedigest",
Short: "",
Long: "",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
ojson, _ := cmd.Flags().GetBool("json")
debug, _ := cmd.Flags().GetBool("debug")

zerolog.SetGlobalLevel(zerolog.InfoLevel)
if !ojson {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout})
}
if debug {
zerolog.SetGlobalLevel(zerolog.DebugLevel)
}
},
}

imagedigestCmd.PersistentFlags().BoolP("insecure", "i", false, "Allow insecure connections to registries (skip TLS verification)")
imagedigestCmd.PersistentFlags().BoolP("json", "", false, "Use JSON formatter for output logs")
imagedigestCmd.PersistentFlags().BoolP("debug", "d", false, "Enable debug logging")

imagedigestCmd.AddCommand(
calculate.NewCommand(),
calculatefromfile.NewCommand(),
add.NewCommand(),
validate.NewCommand(),
)

return imagedigestCmd
}
89 changes: 89 additions & 0 deletions internal/tools/imagedigest/cmd/validate/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
Copyright 2025 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package validate

import (
"fmt"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"

"github.com/deckhouse/deckhouse-cli/internal/tools/imagedigest"
)

func NewCommand() *cobra.Command {
validateCmd := &cobra.Command{
Use: "validate <image>",
Short: "Validating the image digest in the image metadata calculated according to the GOST standard Streebog (GOST R 34.11-2012)",
Long: `Validating the image digest in the image metadata calculated according to the GOST standard Streebog (GOST R 34.11-2012)`,
Args: func(cmd *cobra.Command, args []string) error {
if err := cobra.MinimumNArgs(1)(cmd, args); err != nil {
return err
}
return nil
},
RunE: runValidate,
}

validateCmd.Flags().Bool("fix", false, "Fix GOST Image Digest if it is incorrect")

return validateCmd
}

func runValidate(cmd *cobra.Command, args []string) error {
imageName := args[0]

insecure, err := cmd.Flags().GetBool("insecure")
if err != nil {
return fmt.Errorf("failed to get insecure flag: %w", err)
}

fix, err := cmd.Flags().GetBool("fix")
if err != nil {
return fmt.Errorf("failed to get fix flag: %w", err)
}

var opts []crane.Option
if insecure {
opts = append(opts, crane.Insecure)
}

result, err := imagedigest.PullAndValidate(imageName, opts...)
if err != nil {
log.Error().Err(err).Msg("ValidateGostImageDigest")

if fix {
log.Info().Msg("Fix GOST Image Digest")
newDigest, fixErr := imagedigest.PullAnnotatePush(imageName, opts...)
if fixErr != nil {
log.Fatal().Err(fixErr).Msg("AddGostImageDigest")
}
log.Info().Msgf("GOST Image Digest: %s", newDigest)
log.Info().Msg("Added successfully")
return nil
}

return nil
}

log.Info().Msgf("GOST Image Digest from image: %s", result.StoredDigest)
log.Info().Msgf("Calculated GOST Image Digest: %s", result.CalculatedDigest)
log.Info().Msg("Validate successfully")

return nil
}
Loading