Подтвердить что ты не робот

Почему это плохая идея написать данные конфигурации в коде?

Реальный случай (от caff), чтобы проиллюстрировать короткий вопрос:

$CONFIG{'owner'} = q{Peter Palfrader};
$CONFIG{'email'} = q{[email protected]};
$CONFIG{'keyid'} = [ qw{DE7AAF6E94C09C7F 62AF4031C82E0039} ];
$CONFIG{'keyserver'} = 'wwwkeys.de.pgp.net';
$CONFIG{'mailer-send'} = [ 'testfile' ];

Затем в коде: eval `cat $config`, доступ %CONFIG


Предоставьте ответы, в которых изложены общие проблемы, а не только конкретные примеры.

4b9b3361

Ответ 1

Есть много причин, чтобы избежать конфигурации в коде, и я прохожу через некоторые из них в главе конфигурации в мастеринге Perl.

  • Никакое изменение конфигурации не должно нести риск взлома программы. Это, безусловно, не должно нести риск взлома стадии компиляции.
  • Людям не нужно редактировать источник, чтобы получить другую конфигурацию.
  • Люди должны иметь возможность использовать одно и то же приложение, не используя общую группу параметров, вместо этого повторно устанавливая приложение только для изменения конфигурации.
  • Людям разрешается создавать несколько различных конфигураций и запускать их в пакетах без необходимости редактировать источник.
  • Вы должны иметь возможность тестировать свое приложение под разными настройками без изменения кода.
  • Люди не должны научиться программировать, чтобы иметь возможность использовать ваш инструмент.
  • Вы должны только привязывать свои структуры данных конфигурации к источнику информации, чтобы упростить последующие архитектурные изменения.
  • Вам действительно нужен интерфейс вместо прямого доступа на уровне приложения.

Я суммирую это в своем классе Mastering Perl, рассказывая людям, что первым правилом программирования является создание ситуации, когда вы делаете меньше работы, и люди оставляют вас в покое. Когда вы добавляете конфигурацию в код, вы тратите больше времени на решение проблем установки и реагирования на поломки. Если вам не нравятся подобные вещи, дайте людям возможность изменить настройки, не заставляя вас больше работать.

Ответ 2

Одна из основных проблем с этим подходом заключается в том, что ваша конфигурация не очень переносима. Если функционально идентичный инструмент был построен на Java, загрузка конфигурации должна быть переделана. Если оба варианта Perl и Java использовали простой макет key=value, например:

owner = "Peter Palfrader"
email = "[email protected]@palfrader.org"
...

они могли бы поделиться конфигурацией.

Кроме того, вызов eval в файле конфигурации, похоже, открывает эту систему для атаки. Что может добавить злоумышленник в этот файл конфигурации, если они хотят нанести какой-то хаос? Вы понимаете, что любой произвольный код в вашем файле конфигурации будет выполнен?

Другая проблема заключается в том, что она очень противоречит интуиции (по крайней мере для меня). Я ожидал бы, что файл конфигурации будет прочитан некоторым загрузчиком конфигурации, а не выполнен как исполняемый фрагмент кода. Это не так серьезно, но может запутать новых разработчиков, которые не привыкли к этому.

Наконец, хотя маловероятно, что реализация конструкций вроде p{...} будет когда-либо изменяться, если они действительно изменились, это может не работать.

Ответ 3

$CONFIG{'unhappy_employee'} = `rm -rf /`

Ответ 4

Плохая идея поместить данные конфигурации в скомпилированный код, потому что пользователь не может легко изменить его. Для скриптов просто убедитесь, что он полностью отделен от остальных и хорошо документирует его.

Ответ 5

Причина, по которой я удивлен, никто не упоминал, что это тестирование. Когда в коде содержится код, вам приходится писать сумасшедшие, искаженные тесты, чтобы они могли безопасно протестировать. Вы можете закончить писать тесты, которые дублируют код, который они тестируют, что делает тесты почти бесполезными; в основном, просто испытывают себя, вероятно, дрейфуют и трудно поддерживать.

Рука об руку с тестированием - это развертывание, о котором упоминалось. Когда что-то легко тестировать, будет легко (ну, проще) развернуть.

Ответ 6

Основная проблема здесь - повторное использование в среде, где возможны несколько языков. Если ваш файл конфигурации находится на языке A, то вы хотите поделиться этой конфигурацией с языком B, вам придется сделать некоторые перезаписи.

Это еще сложнее, если у вас более сложные конфигурации (например, файлы конфигурации apache) и пытаются выяснить, как обрабатывать потенциальные различия в структурах данных. Если вы используете что-то вроде JSON, YAML и т.д., Синтаксические анализаторы на языке будут знать, как сопоставлять вещи в отношении структур данных языка.

Одним из основных недостатков отсутствия их на языке является то, что вы теряете возможность использования значений параметров настройки для динамических данных.

Ответ 7

Причина 1. Эстетика. Хотя никто не пострадал от плохого запаха, люди склонны прилагать усилия, чтобы избавиться от него.

Причина 2. Операционные затраты. Для команды из 5 это, вероятно, хорошо, но как только у вас есть разделение разработчиков /sysadmin, вы должны нанять системных администраторов, которые понимают Perl (что составляет $$$) или дают разработчикам доступ к производственной системе (большой $$$).

И чтобы усугубить ситуацию, у вас не будет времени (также $$$), чтобы ввести механизм конфигурации, когда вам это вдруг понадобится.

Ответ 8

Я согласен с Тимом Андерсоном. Кто-то здесь путает конфигурацию в коде, поскольку конфигурация не настраивается. Это исправлено для скомпилированного кода.

Оба файла perl или ruby ​​считываются и интерпретируются, как файл yml или xml файл с данными конфигурации. Я выбираю yml, потому что это проще на глазу, чем в коде, так как группировка тестовой средой, разработкой, постановкой и производством, которая в коде будет включать больше.. кода.

Как примечание, XML полностью противоречит "легкому глазу". Мне интересно, что XML-config широко используется с скомпилированными языками.

Ответ 9

Моя основная проблема с настройкой во многих небольших сценариях, которые я пишу, заключается в том, что они часто содержат данные для входа (имя пользователя и пароль или токен аутентификации) для службы, которую я использую. Затем, когда скрипты становятся больше, я запускаю его версию и хочу загрузить его на github.

Итак, перед каждой фиксацией мне нужно заменить мою конфигурацию на некоторые фиктивные значения.

$CONFIG{'user'} = 'username';
$CONFIG{'password'} = '123456';

Также вы должны быть осторожны, чтобы эти ценности в какой-то момент не скользнули в вашу историю фиксации. Это может стать очень раздражающим. Когда вы пройдете через это один или два раза, вы больше никогда не будете пытаться включить конфигурацию в код.

Ответ 10

Извините за длинный код. Ниже приведен удобный модуль Conf.pm, который я использовал во многих системах, что позволяет вам указывать разные переменные для разных производств, промежуточных и срединных сред. Затем я создаю свои программы, чтобы либо принимать параметры среды в командной строке, либо хранить этот файл вне дерева управления версиями, чтобы никогда не переписывался.

AUTOLOAD предоставляет автоматические методы для поиска переменных.

# Instructions:
# use Conf;
# my $c = Conf->new("production");
# print $c->root_dir;
# print $c->log_dir;

package Conf;
use strict;
our $AUTOLOAD;

my $default_environment = "production";

my @valid_environments  = qw(
    development
    production
);

#######################################################################################
# You might need to change this.
sub set_vars {
    my ($self) = @_;

    $self->{"access_token"} = 'asdafsifhefh';

    if ( $self->env eq "development" ) {
       $self->{"root_dir"}       = "/Users/patrickcollins/Documents/workspace/SysG_perl";
       $self->{"server_base"}    = "http://localhost:3000";
    }

    elsif ($self->env eq "production" ) {
       $self->{"root_dir"}       = "/mnt/SysG-production/current/lib";
       $self->{"server_base"}    = "http://api.SysG.com";
       $self->{"log_dir"}        = "/mnt/SysG-production/current/log"
    }  else {
            die "No environment defined\n";
    }

    #######################################################################################
    # You shouldn't need to configure this.

    # More dirs. Move these into the dev/prod sections if they're different per env.
    my $r = $self->{'root_dir'};
    my $b = $self->{'server_base'};

    $self->{"working_dir"} ||= "$r/working";
    $self->{"bin_dir"}     ||= "$r/bin";
    $self->{"log_dir"}     ||= "$r/log";

    # Other URLs. Move these into the dev/prod sections if they're different per env.

    $self->{"new_contract_url"}   = "$b/SysG-training-center/v1/contract/new";
    $self->{"new_documents_url"}  = "$b/SysG-training-center/v1/documents/new";

}

#######################################################################################
# Code, don't change below here.

sub new {
    my ($class,$env) = @_;
    my $self = {};
    bless ($self,$class);

    if ($env) {
            $self->env($env);
    } else {
            $self->env($default_environment);
    }

    $self->set_vars;
    return $self;
}

sub AUTOLOAD {
    my ($self,$val) = @_;
    my $type = ref ($self) || die "$self is not an object";
    my $field = $AUTOLOAD;

    $field =~ s/.*://;

    #print "field: $field\n";

    unless (exists $self->{$field} || $field =~ /DESTROY/ )
    {
       die "ERROR: {$field} does not exist in object/class $type\n";
    }

    $self->{$field} = $val if ($val);
    return $self->{$field};

}

sub env {
    my ($self,$in) = @_;
    if ($in) {
            die ("Invalid environment $in") unless (grep($in,@valid_environments));
            $self->{"_env"} = $in;
    }
    return $self->{"_env"};
}

1;