Compare commits

..

2 Commits

Author SHA1 Message Date
6a3a22dd6f chore(deps): update davidanson/markdownlint-cli2-action action to v20
All checks were successful
Lint Golang files / Run golang CI linter (stable, ubuntu-latest-amd64) (push) Successful in 13s
Lint Golang files / Run golang CI linter (stable, ubuntu-latest-amd64) (pull_request) Successful in 13s
Lint Markdown files / Run markdown linter (pull_request) Successful in 4s
Lint Golang files / Run golang CI linter (stable, ubuntu-latest-arm64) (push) Successful in 45s
Lint Golang files / Run golang CI linter (stable, ubuntu-latest-arm64) (pull_request) Successful in 45s
2025-08-12 16:29:30 +00:00
2f17abedb9 fix: be compliant with golanci linter
All checks were successful
Lint Markdown files / Run markdown linter (push) Successful in 13s
Lint Golang files / Run golang CI linter (stable, ubuntu-latest-amd64) (push) Successful in 30s
Lint Golang files / Run golang CI linter (stable, ubuntu-latest-arm64) (push) Successful in 1m38s
2025-08-12 16:28:40 +02:00
5 changed files with 50 additions and 21 deletions

View File

@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"log"
"os" "os"
"path/filepath" "path/filepath"
"time" "time"
@ -26,7 +27,9 @@ autharr /etc/lidarr/config.xml`,
} }
rootCmd.Flags().Bool("watch", false, "Listens for changes to the configuration and writes the token continuously to the output") rootCmd.Flags().Bool("watch", false, "Listens for changes to the configuration and writes the token continuously to the output")
rootCmd.Execute() if err := rootCmd.Execute(); err != nil {
log.Fatalln(err)
}
} }
func runE(cmd *cobra.Command, args []string) error { func runE(cmd *cobra.Command, args []string) error {
@ -84,10 +87,16 @@ func runWatch(cmd *cobra.Command, args []string) error {
select { select {
case <-cmd.Context().Done(): case <-cmd.Context().Done():
return nil return nil
case err := <-errorChannel: case err, open := <-errorChannel:
logrus.WithError(err).Errorln("Received from config watcher") if !open {
return fmt.Errorf("error channel has been closed")
}
logrus.WithError(err).Errorln("received from config watcher")
case <-timer.C: case <-timer.C:
writeConfig(cachedConfig, dest) err = writeConfig(cachedConfig, dest)
if err != nil {
logrus.WithError(err).Errorln("failed to write config")
}
case config := <-configChannel: case config := <-configChannel:
cachedConfig = config cachedConfig = config
timer.Reset(waitFor) timer.Reset(waitFor)
@ -105,18 +114,23 @@ func writeConfig(config *domain.Config, dest string) error {
case len(dest) > 0: case len(dest) > 0:
dirname := filepath.Dir(dest) dirname := filepath.Dir(dest)
// #nosec G301
err := os.MkdirAll(dirname, 0755) err := os.MkdirAll(dirname, 0755)
if err != nil { if err != nil {
return err return err
} }
// #nosec G304
f, err := os.Create(dest) f, err := os.Create(dest)
if err != nil { if err != nil {
return err return err
} }
defer func() { _ = f.Close() }() defer func() { _ = f.Close() }()
f.WriteString(config.API.Token) _, err = f.WriteString(config.API.Token)
if err != nil {
return err
}
} }
return nil return nil

View File

@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"time" "time"
"git.cryptic.systems/volker.raschek/tarr/pkg/config" "git.cryptic.systems/volker.raschek/tarr/pkg/config"
@ -95,7 +96,10 @@ healarr sonarr https://sonarr.example.com:8443 --api-token my-token`,
rootCmd.PersistentFlags().Bool("insecure", false, "Trust insecure TLS certificates") rootCmd.PersistentFlags().Bool("insecure", false, "Trust insecure TLS certificates")
rootCmd.PersistentFlags().Duration("timeout", time.Minute, "Timeout") rootCmd.PersistentFlags().Duration("timeout", time.Minute, "Timeout")
rootCmd.Execute() err := rootCmd.Execute()
if err != nil {
log.Fatalln(err)
}
} }
func runBazarrE(cmd *cobra.Command, args []string) error { func runBazarrE(cmd *cobra.Command, args []string) error {
@ -152,7 +156,7 @@ func runE(cmd *cobra.Command, args []string, queryKey string) error {
switch { switch {
case len(apiToken) <= 0 && len(configPath) <= 0: case len(apiToken) <= 0 && len(configPath) <= 0:
return fmt.Errorf("At least --api-token oder --config must be defined") return fmt.Errorf("at least --api-token oder --config must be defined")
case len(apiToken) > 0 && len(configPath) <= 0: case len(apiToken) > 0 && len(configPath) <= 0:
err = health.NewReadinessProbe(args[0]). err = health.NewReadinessProbe(args[0]).
QueryAdd(queryKey, apiToken). QueryAdd(queryKey, apiToken).
@ -175,7 +179,7 @@ func runE(cmd *cobra.Command, args []string, queryKey string) error {
return err return err
} }
default: default:
return fmt.Errorf("Neither --api-token nor --config can be used at the same time.") return fmt.Errorf("neither --api-token nor --config can be used at the same time")
} }
return nil return nil

View File

@ -46,11 +46,12 @@ type YAMLConfig struct {
// Read reads the config struct from a file. The decoding format will be determined by the file extension like // Read reads the config struct from a file. The decoding format will be determined by the file extension like
// `xml` or `yaml`. // `xml` or `yaml`.
func ReadConfig(name string) (*domain.Config, error) { func ReadConfig(name string) (*domain.Config, error) {
// #nosec G304
f, err := os.Open(name) f, err := os.Open(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer f.Close() defer func() { _ = f.Close() }()
switch { switch {
case strings.HasSuffix(name, "xml"): case strings.HasSuffix(name, "xml"):
@ -58,7 +59,7 @@ func ReadConfig(name string) (*domain.Config, error) {
case strings.HasSuffix(name, "yml") || strings.HasSuffix(name, "yaml"): case strings.HasSuffix(name, "yml") || strings.HasSuffix(name, "yaml"):
return readYAMLConfig(f) return readYAMLConfig(f)
default: default:
return nil, fmt.Errorf("Unsupported file extension") return nil, fmt.Errorf("unsupported file extension")
} }
} }
@ -97,8 +98,8 @@ func readYAMLConfig(r io.Reader) (*domain.Config, error) {
} }
func WatchConfig(ctx context.Context, name string) (<-chan *domain.Config, <-chan error) { func WatchConfig(ctx context.Context, name string) (<-chan *domain.Config, <-chan error) {
configChannel := make(chan *domain.Config, 0) configChannel := make(chan *domain.Config)
errorChannel := make(chan error, 0) errorChannel := make(chan error)
go func() { go func() {
wait := time.Second * 3 wait := time.Second * 3
@ -110,14 +111,18 @@ func WatchConfig(ctx context.Context, name string) (<-chan *domain.Config, <-cha
errorChannel <- err errorChannel <- err
return return
} }
watcher.Add(name) err = watcher.Add(name)
if err != nil {
errorChannel <- err
return
}
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
close(configChannel) close(configChannel)
close(errorChannel) close(errorChannel)
break return
case event, open := <-watcher.Events: case event, open := <-watcher.Events:
if !open { if !open {
errorChannel <- fmt.Errorf("FSWatcher closed channel: %w", err) errorChannel <- fmt.Errorf("FSWatcher closed channel: %w", err)
@ -145,11 +150,12 @@ func WatchConfig(ctx context.Context, name string) (<-chan *domain.Config, <-cha
// WriteConfig writes the config struct into the file. The encoding format will be determined by the file extension like // WriteConfig writes the config struct into the file. The encoding format will be determined by the file extension like
// `xml` or `yaml`. // `xml` or `yaml`.
func WriteConfig(name string, config *domain.Config) error { func WriteConfig(name string, config *domain.Config) error {
// #nosec G304
f, err := os.Create(name) f, err := os.Create(name)
if err != nil { if err != nil {
return err return err
} }
defer f.Close() defer func() { _ = f.Close() }()
switch { switch {
case strings.HasSuffix(name, "xml"): case strings.HasSuffix(name, "xml"):
@ -157,13 +163,13 @@ func WriteConfig(name string, config *domain.Config) error {
case strings.HasSuffix(name, "yml") || strings.HasSuffix(name, "yaml"): case strings.HasSuffix(name, "yml") || strings.HasSuffix(name, "yaml"):
return writeYAMLConfig(f, config) return writeYAMLConfig(f, config)
default: default:
return fmt.Errorf("Unsupported file extension") return fmt.Errorf("unsupported file extension")
} }
} }
func writeXMLConfig(w io.Writer, config *domain.Config) error { func writeXMLConfig(w io.Writer, config *domain.Config) error {
xmlEncoder := xml.NewEncoder(w) xmlEncoder := xml.NewEncoder(w)
defer xmlEncoder.Close() defer func() { _ = xmlEncoder.Close() }()
xmlConfig := &XMLConfig{ xmlConfig := &XMLConfig{
APIToken: config.API.Token, APIToken: config.API.Token,
@ -180,7 +186,7 @@ func writeXMLConfig(w io.Writer, config *domain.Config) error {
func writeYAMLConfig(w io.Writer, config *domain.Config) error { func writeYAMLConfig(w io.Writer, config *domain.Config) error {
yamlEncoder := yaml.NewEncoder(w) yamlEncoder := yaml.NewEncoder(w)
defer yamlEncoder.Close() defer func() { _ = yamlEncoder.Close() }()
yamlConfig := &YAMLConfig{ yamlConfig := &YAMLConfig{
Auth: YAMLConfigAuth{ Auth: YAMLConfigAuth{

View File

@ -24,6 +24,7 @@ func TestReadWriteConfig_XML(t *testing.T) {
t.Cleanup(func() { _ = os.RemoveAll(tmpDir) }) t.Cleanup(func() { _ = os.RemoveAll(tmpDir) })
expectedXMLConfigName := filepath.Join(tmpDir, "expected_config.xml") expectedXMLConfigName := filepath.Join(tmpDir, "expected_config.xml")
// #nosec G304
f, err := os.Create(expectedXMLConfigName) f, err := os.Create(expectedXMLConfigName)
require.NoError(err) require.NoError(err)
@ -38,6 +39,7 @@ func TestReadWriteConfig_XML(t *testing.T) {
err = config.WriteConfig(actualXMLConfigName, actualConfig) err = config.WriteConfig(actualXMLConfigName, actualConfig)
require.NoError(err) require.NoError(err)
// #nosec G304
b, err := os.ReadFile(actualXMLConfigName) b, err := os.ReadFile(actualXMLConfigName)
require.NoError(err) require.NoError(err)
require.Equal(expectedXMLConfig, string(b)) require.Equal(expectedXMLConfig, string(b))
@ -51,6 +53,7 @@ func TestReadWriteConfig_YAML(t *testing.T) {
t.Cleanup(func() { _ = os.RemoveAll(tmpDir) }) t.Cleanup(func() { _ = os.RemoveAll(tmpDir) })
expectedYAMLConfigName := filepath.Join(tmpDir, "expected_config.yaml") expectedYAMLConfigName := filepath.Join(tmpDir, "expected_config.yaml")
// #nosec G304
f, err := os.Create(expectedYAMLConfigName) f, err := os.Create(expectedYAMLConfigName)
require.NoError(err) require.NoError(err)
@ -65,6 +68,7 @@ func TestReadWriteConfig_YAML(t *testing.T) {
err = config.WriteConfig(actualYAMLConfigName, actualConfig) err = config.WriteConfig(actualYAMLConfigName, actualConfig)
require.NoError(err) require.NoError(err)
// #nosec G304
b, err := os.ReadFile(actualYAMLConfigName) b, err := os.ReadFile(actualYAMLConfigName)
require.NoError(err) require.NoError(err)
require.Equal(expectedYAMLConfig, string(b)) require.Equal(expectedYAMLConfig, string(b))

View File

@ -9,8 +9,8 @@ import (
"net/url" "net/url"
) )
var ErrNoAPIToken error = errors.New("No API token defined") var ErrNoAPIToken error = errors.New("no API token defined")
var ErrNoURL error = errors.New("No API token defined") var ErrNoURL error = errors.New("no API token defined")
type ReadinessProbe struct { type ReadinessProbe struct {
additionalQueryValues url.Values additionalQueryValues url.Values
@ -40,6 +40,7 @@ func (rp *ReadinessProbe) Run(ctx context.Context) error {
httpClient := &http.Client{ httpClient := &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
TLSClientConfig: &tls.Config{ TLSClientConfig: &tls.Config{
// #nosec G402
InsecureSkipVerify: rp.insecure, InsecureSkipVerify: rp.insecure,
}, },
}, },
@ -64,7 +65,7 @@ func (rp *ReadinessProbe) Run(ctx context.Context) error {
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return fmt.Errorf("Received unexpected HTTP status code %v", resp.StatusCode) return fmt.Errorf("received unexpected HTTP status code %v", resp.StatusCode)
} }
return nil return nil